diff --git a/packages/serve/package.json b/packages/serve/package.json index ab3821768a5..ab73f8f5126 100644 --- a/packages/serve/package.json +++ b/packages/serve/package.json @@ -13,8 +13,12 @@ "webpack-dev-server": "3.10.3" }, "peerDependencies": { - "webpack-cli": "4.x.x", - "webpack-dev-server": "3.x.x || 4.x.x" + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } }, "gitHead": "fb50f766851f500ca12867a2aa9de81fa6e368f9" } diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 4fbb646fcf1..852c4fb2b1e 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -1,4 +1,3 @@ -import { devServer } from 'webpack-dev-server/bin/cli-flags'; import WebpackCLI from 'webpack-cli'; import logger from 'webpack-cli/lib/utils/logger'; import startDevServer from './startDevServer'; @@ -11,10 +10,16 @@ import startDevServer from './startDevServer'; * @returns {Function} invokes the devServer API */ export default function serve(...args: string[]): void { + let devServerFlags: object[]; + try { + devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer; + } catch (err) { + throw new Error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`); + } const cli = new WebpackCLI(); const core = cli.getCoreFlags(); - const parsedDevServerArgs = cli.argParser(devServer, args, true); + const parsedDevServerArgs = cli.argParser(devServerFlags, args, true); const devServerArgs = parsedDevServerArgs.opts; const parsedWebpackArgs = cli.argParser(core, parsedDevServerArgs.unknownArgs, true, process.title); const webpackArgs = parsedWebpackArgs.opts; diff --git a/packages/serve/src/startDevServer.ts b/packages/serve/src/startDevServer.ts index 8cbe238de76..00142402c5c 100644 --- a/packages/serve/src/startDevServer.ts +++ b/packages/serve/src/startDevServer.ts @@ -1,5 +1,3 @@ -import Server from 'webpack-dev-server/lib/Server'; - /** * * Starts the devServer @@ -10,6 +8,8 @@ import Server from 'webpack-dev-server/lib/Server'; * @returns {Void} */ export default function startDevServer(compiler, options): void { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Server = require('webpack-dev-server/lib/Server'); const firstWpOpt = compiler.compilers ? compiler.compilers[0].options : compiler.options; const devServerOptions = firstWpOpt.devServer || {}; diff --git a/packages/webpack-cli/__tests__/serve/index.js b/packages/webpack-cli/__tests__/serve/index.js new file mode 100644 index 00000000000..6be02374db1 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/index.js @@ -0,0 +1 @@ +console.log('hello world'); diff --git a/packages/webpack-cli/__tests__/serve/serve.test.js b/packages/webpack-cli/__tests__/serve/serve.test.js new file mode 100644 index 00000000000..007cff90e95 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/serve.test.js @@ -0,0 +1,27 @@ +const { runServe } = require('../../../../test/utils/test-utils'); + +describe('Serve', () => { + const isWindows = process.platform === 'win32'; + + // TODO fix me on windows + if (isWindows) { + it('TODO: Fix on windows', () => { + expect(true).toBe(true); + }); + return; + } + + it('should run with cli', async () => { + const { stdout, stderr } = await runServe([], __dirname); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); + + it('should work with flags', async () => { + const { stdout, stderr } = await runServe(['--hot'], __dirname); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); +}); diff --git a/packages/webpack-cli/__tests__/serve/webpack.config.js b/packages/webpack-cli/__tests__/serve/webpack.config.js new file mode 100644 index 00000000000..955bbdd3364 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/webpack.config.js @@ -0,0 +1,4 @@ +module.exports = { + mode: 'development', + devtool: false, +}; diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index 462b40a767b..dbe3657b7fb 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -26,6 +26,7 @@ "@webpack-cli/package-utils": "^1.0.1-alpha.4", "@webpack-cli/info": "^1.0.1-alpha.4", "@webpack-cli/init": "^1.0.1-alpha.5", + "@webpack-cli/serve": "^1.0.1-alpha.5", "ansi-escapes": "^4.3.1", "colorette": "^1.2.1", "command-line-usage": "^6.1.0", diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index 2c489313431..67f39c94d36 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -19,62 +19,61 @@ describe('basic serve usage', () => { const isWindows = process.platform === 'win32'; + // TODO fix me on windows if (isWindows) { - // TODO fix me on windows - it('compiles without flags', () => { + it('TODO: Fix on windows', () => { expect(true).toBe(true); - - console.warn('TODO: fix `serve` test on windows'); - }); - } else { - it('should respect the --no-color flag', async () => { - const { stdout, stderr } = await runServe(['--help', '--no-color'], __dirname); - options.enabled = true; - expect(stdout).not.toContain(yellow(usageText)); - expect(stdout).toContain(descriptionText); - expect(stderr).toHaveLength(0); }); + return; + } - it('should not invoke info subcommand', async () => { - const { stdout, stderr } = await runServe(['--client-log-level', 'info'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('should respect the --no-color flag', async () => { + const { stdout, stderr } = await runServe(['--help', '--no-color'], __dirname); + options.enabled = true; + expect(stdout).not.toContain(yellow(usageText)); + expect(stdout).toContain(descriptionText); + expect(stderr).toHaveLength(0); + }); - it('compiles without flags', async () => { - const { stdout, stderr } = await runServe(['--port', port], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('should not invoke info subcommand', async () => { + const { stdout, stderr } = await runServe(['--client-log-level', 'info'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses hot flag to alter bundle', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('compiles without flags', async () => { + const { stdout, stderr } = await runServe(['--port', port], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses no-hot flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('uses hot flag to alter bundle', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses hot flag and progress flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot', '--progress'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).toContain('hot/dev-server.js'); - // progress flag makes use of stderr - expect(stderr).not.toHaveLength(0); - }); + it('uses no-hot flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('throws error on unknown flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); - expect(stdout).toHaveLength(0); - expect(stderr).toContain('Unknown argument: --unknown-flag'); - }); - } + it('uses hot flag and progress flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot', '--progress'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + // progress flag makes use of stderr + expect(stderr).not.toHaveLength(0); + }); + + it('throws error on unknown flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); + expect(stdout).toHaveLength(0); + expect(stderr).toContain('Unknown argument: --unknown-flag'); + }); }); diff --git a/test/serve/with-custom-port/serve-custom-config.test.js b/test/serve/with-custom-port/serve-custom-config.test.js index 7025374304c..15088616b6a 100644 --- a/test/serve/with-custom-port/serve-custom-config.test.js +++ b/test/serve/with-custom-port/serve-custom-config.test.js @@ -15,54 +15,53 @@ describe('serve with devServer in config', () => { const isWindows = process.platform === 'win32'; + // TODO fix me on windows if (isWindows) { - // TODO fix me on windows - it('compiles without flags', () => { + it('TODO: Fix on windows', () => { expect(true).toBe(true); - - console.warn('TODO: fix `serve` test on windows'); - }); - } else { - it('Should pick up the host and port from config', async () => { - const { stdout, stderr } = await runServe([], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain('http://0.0.0.0:1234'); - expect(stderr).toBeFalsy(); }); + return; + } - it('Port flag should override the config port', async () => { - const { stdout, stderr } = await runServe(['--port', port], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); + it('Should pick up the host and port from config', async () => { + const { stdout, stderr } = await runServe([], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain('http://0.0.0.0:1234'); + expect(stderr).toBeFalsy(); + }); - it('Passing hot flag works alongside other server config', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - // HMR is being used - expect(stdout).toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); + it('Port flag should override the config port', async () => { + const { stdout, stderr } = await runServe(['--port', port], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); - it('works fine when no-hot flag is passed alongside other server config', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - // HMR is not being used - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); - } + it('Passing hot flag works alongside other server config', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + // HMR is being used + expect(stdout).toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); + + it('works fine when no-hot flag is passed alongside other server config', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + // HMR is not being used + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); });