diff --git a/OPTIONS.md b/OPTIONS.md index 374b7cc3141..cfdd161bd68 100644 --- a/OPTIONS.md +++ b/OPTIONS.md @@ -1,8 +1,10 @@ ``` Usage: webpack [options] -Alternative usage: webpack bundle [options] Alternative usage: webpack --config [options] -Alternative usage: webpack bundle --config [options] +Alternative usage: webpack build [options] +Alternative usage: webpack bundle [options] +Alternative usage: webpack b [options] +Alternative usage: webpack build --config [options] The build tool for modern web applications. @@ -697,7 +699,7 @@ Global options: -h, --help [verbose] Display help for commands and options. Commands: - bundle|b [options] Run webpack (default command, can be omitted). + build|bundle|b [options] Run webpack (default command, can be omitted). version|v Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands. help|h Display help for commands and options. serve|s [options] Run the webpack dev server. diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index 1aeaf5504dd..605311f7c7d 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -31,7 +31,7 @@ class WebpackCLI { async makeCommand(commandOptions, options, action) { const alreadyLoaded = this.program.commands.find( - (command) => command.name() === commandOptions.name || command.alias() === commandOptions.alias, + (command) => command.name() === commandOptions.name || command.aliases().includes(commandOptions.alias), ); if (alreadyLoaded) { @@ -229,9 +229,9 @@ class WebpackCLI { async run(args, parseOptions) { // Built-in internal commands - const bundleCommandOptions = { - name: 'bundle', - alias: 'b', + const buildCommandOptions = { + name: 'build', + alias: ['bundle', 'b'], description: 'Run webpack (default command, can be omitted).', usage: '[options]', }; @@ -286,8 +286,25 @@ class WebpackCLI { }, ]; - const knownCommands = [bundleCommandOptions, versionCommandOptions, helpCommandOptions, ...externalBuiltInCommandsInfo]; - const isKnownCommand = (name) => knownCommands.find((command) => command.name === name || command.alias === name); + const knownCommands = [buildCommandOptions, versionCommandOptions, helpCommandOptions, ...externalBuiltInCommandsInfo]; + const isKnownCommand = (name) => + knownCommands.find( + (command) => + command.name === name || (Array.isArray(command.alias) ? command.alias.includes(name) : command.alias === name), + ); + const isBuildCommand = (name) => + buildCommandOptions.name === name || + (Array.isArray(buildCommandOptions.alias) ? buildCommandOptions.alias.includes(name) : buildCommandOptions.alias === name); + const isHelpCommand = (name) => + helpCommandOptions.name === name || + (Array.isArray(helpCommandOptions.alias) ? helpCommandOptions.alias.includes(name) : helpCommandOptions.alias === name); + const isVersionCommand = (name) => + versionCommandOptions.name === name || + (Array.isArray(versionCommandOptions.alias) + ? versionCommandOptions.alias.includes(name) + : versionCommandOptions.alias === name); + const findCommandByName = (name) => + this.program.commands.find((command) => name === command.name() || command.alias().includes(name)); const getCommandNameAndOptions = (args) => { let commandName; @@ -311,16 +328,15 @@ class WebpackCLI { const isDefault = typeof commandName === 'undefined'; - return { commandName: isDefault ? bundleCommandOptions.name : commandName, options, isDefault }; + return { commandName: isDefault ? buildCommandOptions.name : commandName, options, isDefault }; }; const loadCommandByName = async (commandName, allowToInstall = false) => { - if (commandName === bundleCommandOptions.name || commandName === bundleCommandOptions.alias) { - // Make `bundle|b [options]` command - await this.makeCommand(bundleCommandOptions, this.getBuiltInOptions(), async (program) => { + if (isBuildCommand(commandName)) { + await this.makeCommand(buildCommandOptions, this.getBuiltInOptions(), async (program) => { const options = program.opts(); if (program.args.length > 0) { - const possibleCommands = [].concat([bundleCommandOptions.name]).concat(program.args); + const possibleCommands = [].concat([buildCommandOptions.name]).concat(program.args); logger.error('Running multiple commands at the same time is not possible'); logger.error(`Found commands: ${possibleCommands.map((item) => `'${item}'`).join(', ')}`); @@ -330,16 +346,19 @@ class WebpackCLI { await this.bundleCommand(options); }); - } else if (commandName === helpCommandOptions.name || commandName === helpCommandOptions.alias) { + } else if (isHelpCommand(commandName)) { // Stub for the `help` command this.makeCommand(helpCommandOptions, [], () => {}); - } else if (commandName === versionCommandOptions.name || commandName === helpCommandOptions.alias) { + } else if (isVersionCommand(commandName)) { // Stub for the `help` command this.makeCommand(versionCommandOptions, [], () => {}); } else { const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find( (externalBuiltInCommandInfo) => - externalBuiltInCommandInfo.name === commandName || externalBuiltInCommandInfo.alias === commandName, + externalBuiltInCommandInfo.name === commandName || + (typeof Array.isArray(externalBuiltInCommandInfo.alias) + ? externalBuiltInCommandInfo.alias.includes(commandName) + : externalBuiltInCommandInfo.alias === commandName), ); let pkg; @@ -420,9 +439,7 @@ class WebpackCLI { const { commandName } = getCommandNameAndOptions(this.program.args); if (commandName) { - const command = this.program.commands.find( - (command) => command.name() === commandName || command.alias() === commandName, - ); + const command = findCommandByName(commandName); if (!command) { logger.error(`Can't find and load command '${commandName}'`); @@ -470,13 +487,7 @@ class WebpackCLI { const outputVersion = async (options) => { // Filter `bundle`, `version` and `help` commands const possibleCommandNames = options.filter( - (options) => - options !== bundleCommandOptions.name && - options !== bundleCommandOptions.alias && - options !== versionCommandOptions.name && - options !== versionCommandOptions.alias && - options !== helpCommandOptions.name && - options !== helpCommandOptions.alias, + (option) => !isBuildCommand(option) && !isVersionCommand(option) && !isHelpCommand(option), ); possibleCommandNames.forEach((possibleCommandName) => { @@ -495,9 +506,7 @@ class WebpackCLI { await Promise.all(possibleCommandNames.map((possibleCommand) => loadCommandByName(possibleCommand))); for (const possibleCommandName of possibleCommandNames) { - const foundCommand = this.program.commands.find( - (command) => command.name() === possibleCommandName || command.alias() === possibleCommandName, - ); + const foundCommand = findCommandByName(possibleCommandName); if (!foundCommand) { logger.error(`Unknown command '${possibleCommandName}'`); @@ -563,9 +572,7 @@ class WebpackCLI { }), ); - const bundleCommand = this.program.commands.find( - (command) => command.name() === bundleCommandOptions.name || command.alias() === bundleCommandOptions.alias, - ); + const bundleCommand = findCommandByName(buildCommandOptions.name); if (!isVerbose) { hideVerboseOptions(bundleCommand); @@ -574,10 +581,10 @@ class WebpackCLI { let helpInformation = bundleCommand .helpInformation() .trimRight() - .replace(bundleCommandOptions.description, 'The build tool for modern web applications.') + .replace(buildCommandOptions.description, 'The build tool for modern web applications.') .replace( /Usage:.+/, - 'Usage: webpack [options]\nAlternative usage: webpack bundle [options]\nAlternative usage: webpack --config [options]\nAlternative usage: webpack bundle --config [options]', + 'Usage: webpack [options]\nAlternative usage: webpack --config [options]\nAlternative usage: webpack build [options]\nAlternative usage: webpack bundle [options]\nAlternative usage: webpack b [options]\nAlternative usage: webpack build --config [options]', ); logger.raw(helpInformation); @@ -598,7 +605,7 @@ class WebpackCLI { await loadCommandByName(name); - const command = this.program.commands.find((command) => command.name() === name || command.alias() === name); + const command = findCommandByName(name); if (!command) { logger.error(`Can't find and load command '${name}'`); @@ -612,30 +619,36 @@ class WebpackCLI { let helpInformation = command.helpInformation().trimRight(); - if (name === bundleCommandOptions.name || name === bundleCommandOptions.alias) { + if (isBuildCommand(name)) { helpInformation = helpInformation - .replace(bundleCommandOptions.description, 'The build tool for modern web applications.') + .replace(buildCommandOptions.description, 'The build tool for modern web applications.') .replace( /Usage:.+/, - 'Usage: webpack [options]\nAlternative usage: webpack bundle [options]\nAlternative usage: webpack --config [options]\nAlternative usage: webpack bundle --config [options]', + 'Usage: webpack [options]\nAlternative usage: webpack --config [options]\nAlternative usage: webpack build [options]\nAlternative usage: webpack bundle [options]\nAlternative usage: webpack b [options]\nAlternative usage: webpack build --config [options]', ); } logger.raw(helpInformation); } - const globalOptions = program.helpInformation().match(/Options:\n(?.+)\nCommands:\n/s); + const programHelpInformation = program.helpInformation(); + const globalOptions = programHelpInformation.match(/Options:\n(?.+)\nCommands:\n/s); if (globalOptions && globalOptions.groups.globalOptions) { logger.raw('\nGlobal options:'); logger.raw(globalOptions.groups.globalOptions.trimRight()); } - if (isGlobal) { - const globalCommands = program.helpInformation().match(/Commands:\n(?.+)/s); + const globalCommands = programHelpInformation.match(/Commands:\n(?.+)/s); + if (isGlobal && globalCommands.groups.globalCommands) { logger.raw('\nCommands:'); - logger.raw(globalCommands.groups.globalCommands.trimRight()); + logger.raw( + globalCommands.groups.globalCommands + .trimRight() + // `commander` doesn't support multiple alias in help + .replace('build|bundle [options] ', 'build|bundle|b [options]'), + ); } logger.raw("\nTo see list of all supported commands and options run 'webpack --help=verbose'.\n"); @@ -665,7 +678,7 @@ class WebpackCLI { const opts = program.opts(); - if (opts.help || commandName === helpCommandOptions.name || commandName === helpCommandOptions.alias) { + if (opts.help || isHelpCommand(commandName)) { let isVerbose = false; if (opts.help) { @@ -686,7 +699,7 @@ class WebpackCLI { await outputHelp(optionsForHelp, isVerbose, program); } - if (opts.version || commandName === versionCommandOptions.name || commandName === versionCommandOptions.alias) { + if (opts.version || isVersionCommand(commandName)) { const optionsForVersion = [].concat(opts.version ? [commandName] : []).concat(options); await outputVersion(optionsForVersion, program); @@ -700,7 +713,9 @@ class WebpackCLI { const found = knownCommands.find((commandOptions) => distance(commandName, commandOptions.name) < 3); if (found) { - logger.error(`Did you mean '${found.name}' (alias '${found.alias}')?`); + logger.error( + `Did you mean '${found.name}' (alias '${Array.isArray(found.alias) ? found.alias.join(', ') : found.alias}')?`, + ); } logger.error("Run 'webpack --help' to see available commands and options"); @@ -1313,7 +1328,7 @@ class WebpackCLI { } }; - options.argv = { ...options, env: { WEBPACK_BUNDLE: true, ...options.env } }; + options.argv = { ...options, env: { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true, ...options.env } }; compiler = await this.createCompiler(options, callback); diff --git a/test/bundle/basic/basic.test.js b/test/build/basic/basic.test.js similarity index 75% rename from test/bundle/basic/basic.test.js rename to test/build/basic/basic.test.js index 595d09863f8..cd5a3137ed1 100644 --- a/test/bundle/basic/basic.test.js +++ b/test/build/basic/basic.test.js @@ -4,14 +4,14 @@ const { run } = require('../../utils/test-utils'); describe('bundle command', () => { it('should work', async () => { - const { exitCode, stderr, stdout } = run(__dirname, ['bundle'], false); + const { exitCode, stderr, stdout } = run(__dirname, ['build'], false); expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); expect(stdout).toBeTruthy(); }); - it('should work with alias', async () => { + it('should work with the "b" alias', async () => { const { exitCode, stderr, stdout } = run(__dirname, ['b'], false); expect(exitCode).toBe(0); @@ -19,12 +19,20 @@ describe('bundle command', () => { expect(stdout).toBeTruthy(); }); + it('should work with "bundle" alias', async () => { + const { exitCode, stderr, stdout } = run(__dirname, ['bundle'], false); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + }); + it('should log error with multi commands', async () => { const { exitCode, stderr, stdout } = run(__dirname, ['bundle', 'info'], false); expect(exitCode).toBe(2); expect(stderr).toContain('Running multiple commands at the same time is not possible'); - expect(stderr).toContain("Found commands: 'bundle', 'info'"); + expect(stderr).toContain("Found commands: 'build', 'info'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); expect(stdout).toBeFalsy(); }); @@ -34,7 +42,7 @@ describe('bundle command', () => { expect(exitCode).toBe(2); expect(stderr).toContain('Running multiple commands at the same time is not possible'); - expect(stderr).toContain("Found commands: 'bundle', 'i'"); + expect(stderr).toContain("Found commands: 'build', 'i'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); expect(stdout).toBeFalsy(); }); diff --git a/test/bundle/basic/src/index.js b/test/build/basic/src/index.js similarity index 100% rename from test/bundle/basic/src/index.js rename to test/build/basic/src/index.js diff --git a/test/bundle/bundle-variable/bundle-variable.test.js b/test/build/build-variable/build-variable.test.js similarity index 100% rename from test/bundle/bundle-variable/bundle-variable.test.js rename to test/build/build-variable/build-variable.test.js diff --git a/test/bundle/bundle-variable/src/index.js b/test/build/build-variable/src/index.js similarity index 100% rename from test/bundle/bundle-variable/src/index.js rename to test/build/build-variable/src/index.js diff --git a/test/build/build-variable/webpack.config.js b/test/build/build-variable/webpack.config.js new file mode 100644 index 00000000000..a0ed78dbfb5 --- /dev/null +++ b/test/build/build-variable/webpack.config.js @@ -0,0 +1,24 @@ +const isInProcess = process.env.WEBPACK_BUNDLE; + +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_BUILD)], + }; +}; diff --git a/test/build/bundle-variable/bundle-variable.test.js b/test/build/bundle-variable/bundle-variable.test.js new file mode 100644 index 00000000000..8458e95e483 --- /dev/null +++ b/test/build/bundle-variable/bundle-variable.test.js @@ -0,0 +1,13 @@ +'use strict'; + +const { run } = require('../../utils/test-utils'); + +describe('bundle variable', () => { + it('compiles without flags and export variable', async () => { + const { exitCode, stderr, stdout } = run(__dirname, [], false); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain('PASS'); + }); +}); diff --git a/test/build/bundle-variable/src/index.js b/test/build/bundle-variable/src/index.js new file mode 100644 index 00000000000..6be02374db1 --- /dev/null +++ b/test/build/bundle-variable/src/index.js @@ -0,0 +1 @@ +console.log('hello world'); diff --git a/test/bundle/bundle-variable/webpack.config.js b/test/build/bundle-variable/webpack.config.js similarity index 100% rename from test/bundle/bundle-variable/webpack.config.js rename to test/build/bundle-variable/webpack.config.js diff --git a/test/config/type/function-with-argv/function-with-argv.test.js b/test/config/type/function-with-argv/function-with-argv.test.js index c1d212164d5..5df5a5abcb8 100644 --- a/test/config/type/function-with-argv/function-with-argv.test.js +++ b/test/config/type/function-with-argv/function-with-argv.test.js @@ -10,7 +10,8 @@ describe('function configuration', () => { expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); expect(stdout).toBeTruthy(); - expect(stdout).toContain("{ argv: { mode: 'development', env: { WEBPACK_BUNDLE: true } } }"); + expect(stdout).toContain('WEBPACK_BUNDLE: true'); + expect(stdout).toContain('WEBPACK_BUILD: true'); expect(stdout).toContain("mode: 'development'"); expect(existsSync(resolve(__dirname, './dist/dev.js'))); }); diff --git a/test/help/help.test.js b/test/help/help.test.js index 56e863512f1..2e14e2bab9c 100644 --- a/test/help/help.test.js +++ b/test/help/help.test.js @@ -155,7 +155,7 @@ describe('help', () => { expect(stdout).toContain('Made with ♥ by the webpack team'); }); - const commands = ['bundle', 'loader', 'plugin', 'info', 'init', 'serve', 'migrate']; + const commands = ['build', 'bundle', 'loader', 'plugin', 'info', 'init', 'serve', 'migrate']; commands.forEach((command) => { it(`should show help information for '${command}' command using the "--help" option`, () => { @@ -163,7 +163,7 @@ describe('help', () => { expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain(`webpack ${command === 'bundle' ? '' : command}`); + expect(stdout).toContain(`webpack ${command === 'build' || command === 'bundle' ? '' : command}`); }); it(`should show help information for '${command}' command using command syntax`, () => { @@ -171,7 +171,7 @@ describe('help', () => { expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain(`webpack ${command === 'bundle' ? '' : command}`); + expect(stdout).toContain(`webpack ${command === 'build' || command === 'bundle' ? '' : command}`); }); it('should show help information and respect the "--color" flag using the "--help" option', () => { @@ -179,7 +179,7 @@ describe('help', () => { expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain(`webpack ${command === 'bundle' ? '' : command}`); + expect(stdout).toContain(`webpack ${command === 'build' || command === 'bundle' ? '' : command}`); expect(stdout).toContain(coloretteEnabled ? bold('Made with ♥ by the webpack team') : 'Made with ♥ by the webpack team'); }); @@ -188,7 +188,7 @@ describe('help', () => { expect(exitCode).toBe(0); expect(stderr).toBeFalsy(); - expect(stdout).toContain(`webpack ${command === 'bundle' ? '' : command}`); + expect(stdout).toContain(`webpack ${command === 'build' || command === 'bundle' ? '' : command}`); // TODO bug in tests // expect(stdout).not.toContain(bold('Made with ♥ by the webpack team')); expect(stdout).toContain('Made with ♥ by the webpack team'); diff --git a/test/version/version.test.js b/test/version/version.test.js index 42749637dbc..5a3cc96d2d2 100644 --- a/test/version/version.test.js +++ b/test/version/version.test.js @@ -85,6 +85,16 @@ describe('single version flag', () => { expect(stdout).toContain(`webpack-dev-server ${webpackDevServerPkgJSON.version}`); }); + it('outputs version with build', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['build', '--version'], false); + + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(stdout).toContain(`webpack-cli ${pkgJSON.version}`); + expect(stdout).toContain(`webpack ${webpack.version}`); + expect(stdout).toContain(`webpack-dev-server ${webpackDevServerPkgJSON.version}`); + }); + it('outputs version with bundle', () => { const { exitCode, stderr, stdout } = run(__dirname, ['bundle', '--version'], false);