diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 508b5cee234..8b52e1568ec 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -24,7 +24,7 @@ class ServeCommand { process.exit(2); } - const builtInOptions = cli.getBuiltInOptions(); + const builtInOptions = cli.getBuiltInOptions().filter((option) => option.name !== 'watch'); return [...builtInOptions, ...devServerFlags]; }, diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index 8fb1ffdebbd..04d28a9135e 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -537,27 +537,17 @@ class WebpackCLI { const isWatchCommandUsed = isCommand(commandName, watchCommandOptions); if (isBuildCommandUsed || isWatchCommandUsed) { + const options = this.getBuiltInOptions(); + await this.makeCommand( isBuildCommandUsed ? buildCommandOptions : watchCommandOptions, - this.getBuiltInOptions(), + isWatchCommandUsed ? options.filter((option) => option.name !== 'watch') : options, async (entries, options) => { if (entries.length > 0) { options.entry = [...entries, ...(options.entry || [])]; } - if (isWatchCommandUsed) { - if (typeof options.watch !== 'undefined') { - this.logger.warn( - `No need to use the ${ - options.watch ? "'--watch, -w'" : "'--no-watch'" - } option together with the 'watch' command, it does not make sense`, - ); - } - - options.watch = true; - } - - await this.buildCommand(options); + await this.buildCommand(options, isWatchCommandUsed); }, ); } else if (isCommand(commandName, helpCommandOptions)) { @@ -1369,6 +1359,31 @@ class WebpackCLI { process.exit(2); } + const outputHints = (configOptions) => { + if ( + configOptions.watch && + options.argv && + options.argv.env && + (options.argv.env['WEBPACK_WATCH'] || options.argv.env['WEBPACK_SERVE']) + ) { + this.logger.warn( + `No need to use the '${ + options.argv.env['WEBPACK_WATCH'] ? 'watch' : 'serve' + }' command together with '{ watch: true }' configuration, it does not make sense.`, + ); + + if (options.argv.env['WEBPACK_SERVE']) { + configOptions.watch = false; + } + } + + return configOptions; + }; + + config.options = Array.isArray(config.options) + ? config.options.map((options) => outputHints(options)) + : outputHints(config.options); + if (this.webpack.cli) { const processArguments = (configOptions) => { const args = this.getBuiltInOptions() @@ -1666,7 +1681,7 @@ class WebpackCLI { return compiler; } - async buildCommand(options) { + async buildCommand(options, isWatchCommand) { let compiler; const callback = (error, stats) => { @@ -1731,7 +1746,16 @@ class WebpackCLI { } }; - options.argv = { ...options, env: { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, ...options.env } }; + const env = + isWatchCommand || options.watch + ? { WEBPACK_WATCH: true, ...options.env } + : { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, ...options.env }; + + options.argv = { ...options, env }; + + if (isWatchCommand) { + options.watch = true; + } compiler = await this.createCompiler(options, callback); diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index a57308f0a00..d215cea596e 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -296,7 +296,33 @@ describe('basic serve usage', () => { expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); - it('should log and error on unknown flag', async () => { + it('should work and log warning on the `watch option in a configuration', async () => { + const { stderr, stdout } = await runServe(__dirname, ['--config', './watch.config.js', '--port', port]); + + expect(stderr).toContain( + "No need to use the 'serve' command together with '{ watch: true }' configuration, it does not make sense.", + ); + expect(stdout).toContain('development'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it("should log error on using '--watch' flag with serve", async () => { + const { stdout, stderr } = await runServe(testPath, ['--watch']); + + expect(stderr).toContain("Error: Unknown option '--watch'"); + expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); + expect(stdout).toBeFalsy(); + }); + + it("should log error on using '-w' alias with serve", async () => { + const { stdout, stderr } = await runServe(testPath, ['-w']); + + expect(stderr).toContain("Error: Unknown option '-w'"); + expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); + expect(stdout).toBeFalsy(); + }); + + it('should log an error on unknown flag', async () => { const { exitCode, stdout, stderr } = await runServe(testPath, ['--port', port, '--unknown-flag']); expect(exitCode).toBe(2); diff --git a/test/serve/basic/watch.config.js b/test/serve/basic/watch.config.js new file mode 100644 index 00000000000..7f7eb3153b8 --- /dev/null +++ b/test/serve/basic/watch.config.js @@ -0,0 +1,8 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = { + mode: 'development', + devtool: false, + plugins: [new WebpackCLITestPlugin(['mode'], false, 'hooks.compilation.taps')], + watch: true, +}; diff --git a/test/watch/basic/basic.test.js b/test/watch/basic/basic.test.js index 0d24c6dda3a..771e4f81344 100644 --- a/test/watch/basic/basic.test.js +++ b/test/watch/basic/basic.test.js @@ -5,7 +5,7 @@ const { run, runAndGetWatchProc, isWebpack5 } = require('../../utils/test-utils' const { writeFileSync } = require('fs'); const { resolve } = require('path'); -const wordsInStatsv4 = ['Hash', 'Version', 'Time', 'Built at:', 'main.js']; +const wordsInStatsv4 = ['Hash', 'Built at:', 'main.js']; const wordsInStatsv5 = ['asset', 'index.js', 'compiled successfully']; describe('basic', () => { @@ -118,11 +118,10 @@ describe('basic', () => { }); }); - it('should recompile upon file change using the `command` option and the `--watch` option and log warning', (done) => { - const proc = runAndGetWatchProc(__dirname, ['watch', '--watch', '--mode', 'development'], false, '', true); + it('should log warning about the `watch` option in the configuration and recompile upon file change using the `watch` command', (done) => { + const proc = runAndGetWatchProc(__dirname, ['--watch', '--mode', 'development', '--config', './watch.config.js'], false, '', true); let modified = false; - let hasWarning = false; proc.stdout.on('data', (chunk) => { const data = stripAnsi(chunk.toString()); @@ -138,7 +137,7 @@ describe('basic', () => { } } - if (!modified && !hasWarning) { + if (!modified) { process.nextTick(() => { writeFileSync(resolve(__dirname, './src/index.js'), `console.log('watch flag test');`); }); @@ -154,51 +153,27 @@ describe('basic', () => { proc.stderr.on('data', (chunk) => { const data = stripAnsi(chunk.toString()); - hasWarning = true; - - expect(data).toContain("No need to use the '--watch, -w' option together with the 'watch' command, it does not make sense"); + expect(data).toContain( + "No need to use the 'watch' command together with '{ watch: true }' configuration, it does not make sense.", + ); }); }); - it('should recompile upon file change using the `command` option and the `--no-watch` option and log warning', (done) => { - const proc = runAndGetWatchProc(__dirname, ['watch', '--no-watch', '--mode', 'development'], false, '', true); - - let modified = false; - let hasWarning = false; - - proc.stdout.on('data', (chunk) => { - const data = stripAnsi(chunk.toString()); - - if (data.includes('index.js')) { - if (isWebpack5) { - for (const word of wordsInStatsv5) { - expect(data).toContain(word); - } - } else { - for (const word of wordsInStatsv4) { - expect(data).toContain(word); - } - } - - if (!modified && !hasWarning) { - process.nextTick(() => { - writeFileSync(resolve(__dirname, './src/index.js'), `console.log('watch flag test');`); - }); + it('should recompile upon file change using the `command` option and the `--watch` option and log warning', async () => { + const { exitCode, stderr, stdout } = await run(__dirname, ['watch', '--watch', '--mode', 'development']); - modified = true; - } else { - proc.kill(); - done(); - } - } - }); - - proc.stderr.on('data', (chunk) => { - const data = stripAnsi(chunk.toString()); + expect(exitCode).toBe(2); + expect(stderr).toContain("Error: Unknown option '--watch'"); + expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); + expect(stdout).toBeFalsy(); + }); - hasWarning = true; + it('should recompile upon file change using the `command` option and the `--no-watch` option and log warning', async () => { + const { exitCode, stderr, stdout } = await run(__dirname, ['watch', '--no-watch', '--mode', 'development']); - expect(data).toContain("No need to use the '--no-watch' option together with the 'watch' command, it does not make sense"); - }); + expect(exitCode).toBe(2); + expect(stderr).toContain("Error: Unknown option '--no-watch'"); + expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); + expect(stdout).toBeFalsy(); }); }); diff --git a/test/watch/watch-variable/src/index.js b/test/watch/watch-variable/src/index.js new file mode 100644 index 00000000000..efc51ca0a97 --- /dev/null +++ b/test/watch/watch-variable/src/index.js @@ -0,0 +1 @@ +console.log('watch flag test'); \ No newline at end of file diff --git a/test/watch/watch-variable/watch-variable.test.js b/test/watch/watch-variable/watch-variable.test.js new file mode 100644 index 00000000000..0f5cc994ed6 --- /dev/null +++ b/test/watch/watch-variable/watch-variable.test.js @@ -0,0 +1,81 @@ +'use strict'; + +const stripAnsi = require('strip-ansi'); +const { runAndGetWatchProc, isWebpack5 } = require('../../utils/test-utils'); +const { writeFileSync } = require('fs'); +const { resolve } = require('path'); + +const wordsInStatsv4 = ['Hash', 'Built at:', 'main.js']; +const wordsInStatsv5 = ['asset', 'index.js', 'compiled successfully']; + +describe('watch variable', () => { + it('should pass `WEBPACK_WATCH` env variable and recompile upon file change using the `watch` command', (done) => { + const proc = runAndGetWatchProc(__dirname, ['watch', '--mode', 'development'], false, '', true); + + let modified = false; + + proc.stdout.on('data', (chunk) => { + const data = stripAnsi(chunk.toString()); + + expect(data).not.toContain('FAIL'); + + if (data.includes('index.js')) { + if (isWebpack5) { + for (const word of wordsInStatsv5) { + expect(data).toContain(word); + } + } else { + for (const word of wordsInStatsv4) { + expect(data).toContain(word); + } + } + + if (!modified) { + process.nextTick(() => { + writeFileSync(resolve(__dirname, './src/index.js'), `console.log('watch flag test');`); + }); + + modified = true; + } else { + proc.kill(); + done(); + } + } + }); + }); + + it('should pass `WEBPACK_WATCH` env variable and recompile upon file change using the `--watch` option', (done) => { + const proc = runAndGetWatchProc(__dirname, ['--watch', '--mode', 'development'], false, '', true); + + let modified = false; + + proc.stdout.on('data', (chunk) => { + const data = stripAnsi(chunk.toString()); + + expect(data).not.toContain('FAIL'); + + if (data.includes('index.js')) { + if (isWebpack5) { + for (const word of wordsInStatsv5) { + expect(data).toContain(word); + } + } else { + for (const word of wordsInStatsv4) { + expect(data).toContain(word); + } + } + + if (!modified) { + process.nextTick(() => { + writeFileSync(resolve(__dirname, './src/index.js'), `console.log('watch flag test');`); + }); + + modified = true; + } else { + proc.kill(); + done(); + } + } + }); + }); +}); diff --git a/test/watch/watch-variable/webpack.config.js b/test/watch/watch-variable/webpack.config.js new file mode 100644 index 00000000000..a177e2cf731 --- /dev/null +++ b/test/watch/watch-variable/webpack.config.js @@ -0,0 +1,24 @@ +const isInProcess = process.env.WEBPACK_WATCH; + +class CustomTestPlugin { + constructor(isInEnvironment) { + this.isInEnvironment = isInEnvironment; + } + apply(compiler) { + compiler.hooks.done.tap('testPlugin', () => { + if (!isInProcess && this.isInEnvironment) { + console.log('PASS'); + } else { + console.log('FAIL'); + } + }); + } +} + +module.exports = (env) => { + return { + mode: 'development', + devtool: false, + plugins: [new CustomTestPlugin(env.WEBPACK_WATCH)], + }; +};