From b5f494d6255a7e209ade52a1b66fde3814199e84 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 26 Dec 2020 18:51:05 +0300 Subject: [PATCH 01/15] chore(deps-dev): bump @types/node from 14.14.14 to 14.14.16 (#2255) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.14 to 14.14.16. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 3a780f28088..b07154704e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2452,9 +2452,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*", "@types/node@>= 8", "@types/node@^14.14.6": - version "14.14.14" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae" - integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ== + version "14.14.16" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b" + integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw== "@types/normalize-package-data@^2.4.0": version "2.4.0" From e6f99439fe0668a18488b74f10fa5175089d0a14 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Sat, 26 Dec 2020 18:51:21 +0300 Subject: [PATCH 02/15] chore(deps): bump @discoveryjs/json-ext from 0.5.1 to 0.5.2 (#2264) Bumps [@discoveryjs/json-ext](https://github.com/discoveryjs/json-ext) from 0.5.1 to 0.5.2. - [Release notes](https://github.com/discoveryjs/json-ext/releases) - [Changelog](https://github.com/discoveryjs/json-ext/blob/master/CHANGELOG.md) - [Commits](https://github.com/discoveryjs/json-ext/compare/v0.5.1...v0.5.2) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b07154704e6..5eb31c139ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1051,9 +1051,9 @@ integrity sha512-VoNqai1vR5anRF5Tuh/+SWDFk7xi7oMwHrHrbm1BprYXjB2RJsWLhUrStMssDxEl5lW/z3EUdg8RvH/IUBccSQ== "@discoveryjs/json-ext@^0.5.0": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.1.tgz#cf87081ac9b0f3eb3b5740415b50b7966bac8fc5" - integrity sha512-Oee4NT60Lxe90m7VTYBU4UbABNaz0N4Q3G62CPB+6mGE4KuLMsTACmH8q3PH5u9pSZCuOdE9JClJ9vBqsp6DQg== + version "0.5.2" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.2.tgz#8f03a22a04de437254e8ce8cc84ba39689288752" + integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== "@eslint/eslintrc@^0.2.2": version "0.2.2" From 952a1883b1a18c4fb38e8eb7bbbdb2aefc7942f4 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Sat, 26 Dec 2020 19:27:04 +0300 Subject: [PATCH 03/15] fix: the `--progress` option with the `serve` command (#2265) --- packages/serve/__tests__/mergeOptions.test.ts | 34 ------------------- packages/serve/src/index.ts | 2 +- packages/serve/src/mergeOptions.ts | 24 ------------- packages/serve/src/startDevServer.ts | 17 ++++++++-- test/serve/basic/serve-basic.test.js | 16 +++++++++ 5 files changed, 31 insertions(+), 62 deletions(-) delete mode 100644 packages/serve/__tests__/mergeOptions.test.ts delete mode 100644 packages/serve/src/mergeOptions.ts diff --git a/packages/serve/__tests__/mergeOptions.test.ts b/packages/serve/__tests__/mergeOptions.test.ts deleted file mode 100644 index 5b97dacfb87..00000000000 --- a/packages/serve/__tests__/mergeOptions.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -'use strict'; - -import mergeOptions from '../src/mergeOptions'; -import { devServerClientLogging } from '../src/types'; - -describe('mergeOptions', () => { - it('merges CLI and devServer options correctly', () => { - const cliOptions = { - client: { - logging: devServerClientLogging.verbose, - }, - hot: true, - bonjour: true, - }; - const devServerOptions = { - client: { - host: 'localhost', - logging: devServerClientLogging.none, - }, - hot: false, - liveReload: false, - }; - // CLI should take priority - expect(mergeOptions(cliOptions, devServerOptions)).toEqual({ - client: { - host: 'localhost', - logging: 'verbose', - }, - hot: true, - bonjour: true, - liveReload: false, - }); - }); -}); diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index c8fe5dfc1dd..0987e357b90 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -51,7 +51,7 @@ class ServeCommand { const processors: Array<(opts: Record) => void> = []; for (const optionName in options) { - if (optionName === 'hot' || optionName === 'progress') { + if (optionName === 'hot') { devServerOptions[optionName] = options[optionName]; webpackOptions[optionName] = options[optionName]; } else { diff --git a/packages/serve/src/mergeOptions.ts b/packages/serve/src/mergeOptions.ts deleted file mode 100644 index 6022dc4ffb8..00000000000 --- a/packages/serve/src/mergeOptions.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { devServerOptionsType } from './types'; - -/** - * - * Merges CLI options and devServer options from config file - * - * @param {Object} cliOptions - devServer CLI args - * @param {Object} devServerOptions - devServer config options - * - * @returns {Object} merged options object - */ -export default function mergeOptions(cliOptions: devServerOptionsType, devServerOptions: devServerOptionsType): devServerOptionsType { - // CLI options should take precedence over devServer options, - // and CLI options should have no default values included - const options = { ...devServerOptions, ...cliOptions }; - - if (devServerOptions.client && cliOptions.client) { - // the user could set some client options in their devServer config, - // then also specify client options on the CLI - options.client = { ...devServerOptions.client, ...cliOptions.client }; - } - - return options; -} diff --git a/packages/serve/src/startDevServer.ts b/packages/serve/src/startDevServer.ts index 51ce17b9e45..08a9478cc76 100644 --- a/packages/serve/src/startDevServer.ts +++ b/packages/serve/src/startDevServer.ts @@ -1,6 +1,4 @@ -import { utils } from 'webpack-cli'; - -import mergeOptions from './mergeOptions'; +import { devServerOptionsType } from './types'; /** * @@ -48,6 +46,19 @@ export default async function startDevServer(compiler, cliOptions, logger): Prom const servers = []; const usedPorts: number[] = []; + const mergeOptions = (cliOptions: devServerOptionsType, devServerOptions: devServerOptionsType): devServerOptionsType => { + // CLI options should take precedence over devServer options, + // and CLI options should have no default values included + const options = { ...devServerOptions, ...cliOptions }; + + if (devServerOptions.client && cliOptions.client) { + // the user could set some client options in their devServer config, + // then also specify client options on the CLI + options.client = { ...devServerOptions.client, ...cliOptions.client }; + } + + return options; + }; for (const devServerOpts of devServerOptions) { const options = mergeOptions(cliOptions, devServerOpts); diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index 16549148b67..923dea3fff2 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -61,6 +61,22 @@ describe('basic serve usage', () => { expect(stdout).not.toContain('HotModuleReplacementPlugin'); }); + it('should work with the "--progress" option', async () => { + const { stderr, stdout } = await runServe(['--progress'], __dirname); + + expect(stderr).toContain('webpack.Progress'); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('HotModuleReplacementPlugin'); + }); + + it('should work with the "--progress" option using the "profile" value', async () => { + const { stderr, stdout } = await runServe(['--progress', 'profile'], __dirname); + + expect(stderr).toContain('webpack.Progress'); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('HotModuleReplacementPlugin'); + }); + it('should work with flags', async () => { const { stderr, stdout } = await runServe(['--hot'], __dirname); From 1dae54da94d3220437b9257efe512447023de1d3 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 28 Dec 2020 20:00:50 +0300 Subject: [PATCH 04/15] fix: the `--help` option is working without `webpack-dev-server` (#2267) --- packages/generators/src/index.ts | 4 +- packages/info/src/index.ts | 4 +- packages/init/src/index.ts | 4 +- packages/migrate/src/index.ts | 4 +- packages/serve/src/index.ts | 62 ++++++------ .../lib/utils/prompt-installation.js | 10 +- packages/webpack-cli/lib/webpack-cli.js | 98 +++++++++++++------ test/help/help.test.js | 4 +- test/optimization/optimization.test.js | 18 ---- test/optimization/src/index.js | 1 - test/optimization/webpack.config.js | 8 -- test/utils/test-utils.js | 16 ++- test/version/version.test.js | 12 +-- 13 files changed, 129 insertions(+), 116 deletions(-) delete mode 100644 test/optimization/optimization.test.js delete mode 100644 test/optimization/src/index.js delete mode 100644 test/optimization/webpack.config.js diff --git a/packages/generators/src/index.ts b/packages/generators/src/index.ts index d423d9caee6..d4f0a93d18a 100644 --- a/packages/generators/src/index.ts +++ b/packages/generators/src/index.ts @@ -5,10 +5,10 @@ import addonGenerator from './addon-generator'; import initGenerator from './init-generator'; class GeneratorsCommand { - apply(cli): void { + async apply(cli): Promise { const { logger } = cli; - cli.makeCommand( + await cli.makeCommand( { name: 'loader [output-path]', alias: 'l', diff --git a/packages/info/src/index.ts b/packages/info/src/index.ts index d61d2b67303..83b3884d7c1 100644 --- a/packages/info/src/index.ts +++ b/packages/info/src/index.ts @@ -30,8 +30,8 @@ const DEFAULT_DETAILS: Information = { }; class InfoCommand { - apply(cli): void { - cli.makeCommand( + async apply(cli): Promise { + await cli.makeCommand( { name: 'info', alias: 'i', diff --git a/packages/init/src/index.ts b/packages/init/src/index.ts index 1de1b7e0331..e0ddd3b209b 100644 --- a/packages/init/src/index.ts +++ b/packages/init/src/index.ts @@ -2,8 +2,8 @@ import { initGenerator } from '@webpack-cli/generators'; import { modifyHelperUtil, npmPackagesExists } from '@webpack-cli/utils'; class InitCommand { - apply(cli): void { - cli.makeCommand( + async apply(cli): Promise { + await cli.makeCommand( { name: 'init [scaffold...]', alias: 'c', diff --git a/packages/migrate/src/index.ts b/packages/migrate/src/index.ts index c9f458a0877..3e7789a5e6c 100644 --- a/packages/migrate/src/index.ts +++ b/packages/migrate/src/index.ts @@ -149,10 +149,10 @@ function runMigration(currentConfigPath: string, outputConfigPath: string, logge } class MigrationCommand { - apply(cli): void { + async apply(cli): Promise { const { logger } = cli; - cli.makeCommand( + await cli.makeCommand( { name: 'migrate [new-config-path]', alias: 'm', diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 0987e357b90..240031b69dc 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -2,45 +2,43 @@ import startDevServer from './startDevServer'; class ServeCommand { async apply(cli): Promise { - const { logger, utils } = cli; - const isPackageExist = utils.getPkg('webpack-dev-server'); - - if (!isPackageExist) { - try { - await utils.promptInstallation('webpack-dev-server', () => { - // TODO colors - logger.error("For using this command you need to install: 'webpack-dev-server' package"); - }); - } catch (error) { - logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands."); - process.exit(2); - } - } - - let devServerFlags = []; - - try { - // eslint-disable-next-line node/no-extraneous-require - require('webpack-dev-server'); - // eslint-disable-next-line node/no-extraneous-require - devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer; - } catch (err) { - logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`); - process.exit(2); - } - - const builtInOptions = cli.getBuiltInOptions(); - - cli.makeCommand( + const { logger } = cli; + + await cli.makeCommand( { name: 'serve', alias: 's', description: 'Run the webpack dev server.', usage: '[options]', pkg: '@webpack-cli/serve', + dependencies: ['webpack-dev-server'], + }, + () => { + let devServerFlags = []; + + try { + // eslint-disable-next-line + devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer; + } catch (error) { + logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${error}`); + process.exit(2); + } + + const builtInOptions = cli.getBuiltInOptions(); + + return [...builtInOptions, ...devServerFlags]; }, - [...builtInOptions, ...devServerFlags], async (program) => { + const builtInOptions = cli.getBuiltInOptions(); + let devServerFlags = []; + + try { + // eslint-disable-next-line + devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer; + } catch (error) { + // Nothing, to prevent future updates + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any const webpackOptions: Record = {}; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -89,7 +87,7 @@ class ServeCommand { let servers; if (cli.needWatchStdin(compiler) || devServerOptions.stdin) { - // TODO + // TODO remove in the next major release // Compatibility with old `stdin` option for `webpack-dev-server` // Should be removed for the next major release on both sides if (devServerOptions.stdin) { diff --git a/packages/webpack-cli/lib/utils/prompt-installation.js b/packages/webpack-cli/lib/utils/prompt-installation.js index 5f77893cb95..d025d80e6ff 100644 --- a/packages/webpack-cli/lib/utils/prompt-installation.js +++ b/packages/webpack-cli/lib/utils/prompt-installation.js @@ -18,15 +18,13 @@ async function promptInstallation(packageName, preMessage) { process.exit(2); } - // yarn uses 'add' command, rest npm and pnpm both use 'install' - const options = [packageManager === 'yarn' ? 'add' : 'install', '-D', packageName]; - - const commandToBeRun = `${packageManager} ${options.join(' ')}`; - if (preMessage) { preMessage(); } + // yarn uses 'add' command, rest npm and pnpm both use 'install' + const commandToBeRun = `${packageManager} ${[packageManager === 'yarn' ? 'add' : 'install', '-D', packageName].join(' ')}`; + let installConfirm; try { @@ -34,7 +32,7 @@ async function promptInstallation(packageName, preMessage) { { type: 'confirm', name: 'installConfirm', - message: `Would you like to install '${packageName}' package? (That will run '${green(commandToBeRun)}')`, + message: `Would you like to install '${green(packageName)}' package? (That will run '${green(commandToBeRun)}')`, initial: 'Y', stdout: process.stderr, }, diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index b4ede2b8d62..3a7f744ee81 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -29,8 +29,8 @@ class WebpackCLI { this.utils = { toKebabCase, getPkg, promptInstallation }; } - makeCommand(commandOptions, optionsForCommand = [], action) { - const command = program.command(commandOptions.name, { + async makeCommand(commandOptions, options, action) { + const command = this.program.command(commandOptions.name, { noHelp: commandOptions.noHelp, hidden: commandOptions.hidden, isDefault: commandOptions.isDefault, @@ -56,8 +56,50 @@ class WebpackCLI { command.pkg = 'webpack-cli'; } - if (optionsForCommand.length > 0) { - optionsForCommand.forEach((optionForCommand) => { + const { forHelp } = this.program; + + let allDependenciesInstalled = true; + + if (commandOptions.dependencies && commandOptions.dependencies.length > 0) { + for (const dependency of commandOptions.dependencies) { + const isPkgExist = getPkg(dependency); + + if (isPkgExist) { + continue; + } else if (!isPkgExist && forHelp) { + allDependenciesInstalled = false; + continue; + } + + try { + await promptInstallation(dependency, () => { + logger.error( + `For using '${green(commandOptions.name)}' command you need to install: '${green(dependency)}' package`, + ); + }); + } catch (error) { + logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands."); + logger.error(error); + process.exit(2); + } + } + } + + if (options) { + if (typeof options === 'function') { + if (forHelp && !allDependenciesInstalled) { + command.description( + `${commandOptions.description} To see all available options you need to install ${commandOptions.dependencies + .map((dependency) => `'${dependency}'`) + .join(',')}.`, + ); + options = []; + } else { + options = options(); + } + } + + options.forEach((optionForCommand) => { this.makeOption(command, optionForCommand); }); } @@ -271,29 +313,11 @@ class WebpackCLI { await this.bundleCommand(options); }); } else if (commandName === helpCommandOptions.name || commandName === helpCommandOptions.alias) { - this.makeCommand( - { - name: 'help [command]', - alias: 'h', - description: 'Display help for commands and options', - usage: '[command]', - }, - [], - // Stub for the `help` command - () => {}, - ); + // Stub for the `help` command + this.makeCommand(helpCommandOptions, [], () => {}); } else if (commandName === versionCommandOptions.name || commandName === helpCommandOptions.alias) { - this.makeCommand( - { - name: 'version [commands...]', - alias: 'v', - description: "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands", - usage: '[commands...]', - }, - [], - // Stub for the `help` command - () => {}, - ); + // Stub for the `help` command + this.makeCommand(versionCommandOptions, [], () => {}); } else { const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find( (externalBuiltInCommandInfo) => @@ -310,11 +334,7 @@ class WebpackCLI { if (pkg !== 'webpack-cli' && !getPkg(pkg)) { if (!allowToInstall) { - const isOptions = commandName.startsWith('-'); - - logger.error(`Unknown ${isOptions ? 'option' : 'command'} '${commandName}'`); - logger.error("Run 'webpack --help' to see available commands and options"); - process.exit(2); + return; } try { @@ -464,6 +484,12 @@ class WebpackCLI { (command) => command.name() === possibleCommandName || command.alias() === possibleCommandName, ); + if (!foundCommand) { + logger.error(`Unknown command '${possibleCommandName}'`); + logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + try { const { name, version } = require(`${foundCommand.pkg}/package.json`); @@ -481,7 +507,7 @@ class WebpackCLI { logger.raw(`webpack-cli ${pkgJSON.version}`); if (getPkg('webpack-dev-server')) { - // eslint-disable-next-line node/no-extraneous-require + // eslint-disable-next-line const { version } = require('webpack-dev-server/package.json'); logger.raw(`webpack-dev-server ${version}`); @@ -547,6 +573,12 @@ class WebpackCLI { } else { const [name, ...optionsWithoutCommandName] = options; + if (name.startsWith('-')) { + logger.error(`Unknown option '${name}'`); + logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } + optionsWithoutCommandName.forEach((option) => { logger.error(`Unknown option '${option}'`); logger.error("Run 'webpack --help' to see available commands and options"); @@ -636,6 +668,8 @@ class WebpackCLI { } } + this.program.forHelp = true; + const optionsForHelp = [].concat(opts.help && !isDefault ? [commandName] : []).concat(options); await outputHelp(optionsForHelp, isVerbose, program); diff --git a/test/help/help.test.js b/test/help/help.test.js index 35c4c568acb..d446a20947c 100644 --- a/test/help/help.test.js +++ b/test/help/help.test.js @@ -219,7 +219,7 @@ describe('help', () => { const { exitCode, stderr, stdout } = run(__dirname, ['help', 'myCommand'], false); expect(exitCode).toBe(2); - expect(stderr).toContain("Unknown command 'myCommand'"); + expect(stderr).toContain("Can't find and load command 'myCommand'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); expect(stdout).toBeFalsy(); }); @@ -228,7 +228,7 @@ describe('help', () => { const { exitCode, stderr, stdout } = run(__dirname, ['help', 'verbose'], false); expect(exitCode).toBe(2); - expect(stderr).toContain("Unknown command 'verbose'"); + expect(stderr).toContain("Can't find and load command 'verbose'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); expect(stdout).toBeFalsy(); }); diff --git a/test/optimization/optimization.test.js b/test/optimization/optimization.test.js deleted file mode 100644 index ea28769da3a..00000000000 --- a/test/optimization/optimization.test.js +++ /dev/null @@ -1,18 +0,0 @@ -const { run, isWebpack5 } = require('../utils/test-utils'); - -describe('optimization option in config', () => { - it('should work with mangleExports disabled', () => { - const { exitCode, stderr, stdout } = run(__dirname, [], false); - - // Should throw when webpack is less than 5 - if (isWebpack5) { - expect(exitCode).toBe(0); - expect(stderr).toBeFalsy(); - expect(stdout).toContain('mangleExports: false'); - } else { - expect(exitCode).toBe(2); - expect(stderr).toContain("configuration.optimization has an unknown property 'mangleExports'"); - expect(stdout).toBeFalsy(); - } - }); -}); diff --git a/test/optimization/src/index.js b/test/optimization/src/index.js deleted file mode 100644 index c56f17c89e9..00000000000 --- a/test/optimization/src/index.js +++ /dev/null @@ -1 +0,0 @@ -console.log("Bokuto") diff --git a/test/optimization/webpack.config.js b/test/optimization/webpack.config.js deleted file mode 100644 index c5ddd675368..00000000000 --- a/test/optimization/webpack.config.js +++ /dev/null @@ -1,8 +0,0 @@ -const WebpackCLITestPlugin = require('../utils/webpack-cli-test-plugin'); - -module.exports = { - plugins: [new WebpackCLITestPlugin()], - optimization: { - mangleExports: false, - }, -}; diff --git a/test/utils/test-utils.js b/test/utils/test-utils.js index 52d4af7bf56..8515dbced2b 100644 --- a/test/utils/test-utils.js +++ b/test/utils/test-utils.js @@ -8,12 +8,22 @@ const { Writable } = require('readable-stream'); const concat = require('concat-stream'); const { version } = require('webpack'); const stripAnsi = require('strip-ansi'); -const { version: devServerVersion } = require('webpack-dev-server/package.json'); + +const isWebpack5 = version.startsWith('5'); + +let devServerVersion; + +try { + // eslint-disable-next-line + devServerVersion = require('webpack-dev-server/package.json').version; +} catch (error) { + // Nothing +} + +const isDevServer4 = devServerVersion && devServerVersion.startsWith('4'); const WEBPACK_PATH = path.resolve(__dirname, '../../packages/webpack-cli/bin/cli.js'); const ENABLE_LOG_COMPILATION = process.env.ENABLE_PIPE || false; -const isWebpack5 = version.startsWith('5'); -const isDevServer4 = devServerVersion.startsWith('4'); const isWindows = process.platform === 'win32'; const hyphenToUpperCase = (name) => { diff --git a/test/version/version.test.js b/test/version/version.test.js index b9a953703d4..ac2b3deb1ca 100644 --- a/test/version/version.test.js +++ b/test/version/version.test.js @@ -171,13 +171,13 @@ describe('single version flag', () => { expect(stdout).toBeFalsy(); }); - it('should log error when command using command syntax with multi commands', () => { + it('should log version for known command and log error for unknown command using command syntax with multi commands', () => { const { exitCode, stderr, stdout } = run(__dirname, ['version', 'info', 'unknown'], false); expect(exitCode).toBe(2); expect(stderr).toContain("Unknown command 'unknown'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); - expect(stdout).toBeFalsy(); + expect(stdout).toContain(`@webpack-cli/info ${infoPkgJSON.version}`); }); it('should work for multiple commands', () => { @@ -214,22 +214,22 @@ describe('single version flag', () => { expect(stdout).toContain(`webpack-dev-server ${webpackDevServerPkgJSON.version}`); }); - it('should log error when unknown command used with --version flag', () => { + it('should log version for known command and log error for unknown command using the "--version" option', () => { const { exitCode, stderr, stdout } = run(__dirname, ['init', 'abc', '--version'], false); expect(exitCode).toBe(2); expect(stderr).toContain("Unknown command 'abc'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); - expect(stdout).toBeFalsy(); + expect(stdout).toContain(`@webpack-cli/init ${initPkgJSON.version}`); }); - it('should log error when unknown command used with -v alias', () => { + it('should log version for known command and log error for unknown command using the "-v" option', () => { const { exitCode, stderr, stdout } = run(__dirname, ['init', 'abc', '-v'], false); expect(exitCode).toBe(2); expect(stderr).toContain("Unknown command 'abc'"); expect(stderr).toContain("Run 'webpack --help' to see available commands and options"); - expect(stdout).toBeFalsy(); + expect(stdout).toContain(`@webpack-cli/init ${initPkgJSON.version}`); }); it('should not output version with help dashed', () => { From bb16d4481414a5f3c0cbeb18af690084b2ae4215 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Mon, 28 Dec 2020 21:45:20 +0300 Subject: [PATCH 05/15] fix: do not apply HotModuleReplacement plugin twice (#2269) --- packages/serve/src/index.ts | 29 +++++----- test/serve/basic/serve-basic.test.js | 64 +++++++++++------------ test/serve/basic/webpack.config.js | 2 +- test/utils/cli-plugin-test/plugin.test.js | 2 +- test/utils/webpack-cli-test-plugin.js | 17 +++++- 5 files changed, 63 insertions(+), 51 deletions(-) diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 240031b69dc..5108eb93924 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -49,26 +49,23 @@ class ServeCommand { const processors: Array<(opts: Record) => void> = []; for (const optionName in options) { - if (optionName === 'hot') { - devServerOptions[optionName] = options[optionName]; + const kebabedOption = cli.utils.toKebabCase(optionName); + // `webpack-dev-server` has own logic for the `--hot` option + const isBuiltInOption = + kebabedOption !== 'hot' && builtInOptions.find((builtInOption) => builtInOption.name === kebabedOption); + + if (isBuiltInOption) { webpackOptions[optionName] = options[optionName]; } else { - const kebabedOption = cli.utils.toKebabCase(optionName); - const isBuiltInOption = builtInOptions.find((builtInOption) => builtInOption.name === kebabedOption); - - if (isBuiltInOption) { - webpackOptions[optionName] = options[optionName]; - } else { - const needToProcess = devServerFlags.find( - (devServerOption) => devServerOption.name === kebabedOption && devServerOption.processor, - ); + const needToProcess = devServerFlags.find( + (devServerOption) => devServerOption.name === kebabedOption && devServerOption.processor, + ); - if (needToProcess) { - processors.push(needToProcess.processor); - } - - devServerOptions[optionName] = options[optionName]; + if (needToProcess) { + processors.push(needToProcess.processor); } + + devServerOptions[optionName] = options[optionName]; } } diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index 923dea3fff2..7efedca684f 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -27,11 +27,11 @@ describe('basic serve usage', () => { } it('should work', async () => { - const { stderr, stdout } = await runServe(['--no-hot'], __dirname); + const { stderr, stdout } = await runServe([''], __dirname); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); it('should work with the "--mode" option', async () => { @@ -40,7 +40,7 @@ describe('basic serve usage', () => { expect(stderr).toBeFalsy(); expect(stdout).toContain('development'); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); it('should work with the "--mode" option #2', async () => { @@ -49,16 +49,16 @@ describe('basic serve usage', () => { expect(stderr).toBeFalsy(); expect(stdout).toContain('production'); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); - it('should work with the "--mode" option #2', async () => { + it('should work with the "--mode" option #3', async () => { const { stderr, stdout } = await runServe(['--mode', 'development'], __dirname); expect(stderr).toBeFalsy(); expect(stdout).toContain('development'); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); it('should work with the "--progress" option', async () => { @@ -66,7 +66,7 @@ describe('basic serve usage', () => { expect(stderr).toContain('webpack.Progress'); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); it('should work with the "--progress" option using the "profile" value', async () => { @@ -74,18 +74,10 @@ describe('basic serve usage', () => { expect(stderr).toContain('webpack.Progress'); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); - it('should work with flags', async () => { - const { stderr, stdout } = await runServe(['--hot'], __dirname); - - expect(stderr).toBeFalsy(); - expect(stdout).toContain('main.js'); - expect(stdout).toContain('HotModuleReplacementPlugin'); - }); - - it('should respect the --no-color flag', async () => { + it('should log help information and respect the "--no-color" option', async () => { const { stdout, stderr } = await runServe(['--help', '--no-color'], __dirname); expect(stderr).toBeFalsy(); @@ -93,55 +85,63 @@ describe('basic serve usage', () => { expect(stdout).toContain(descriptionText); }); - it('should not invoke info subcommand', async () => { + it('should work with the "--client-log-level" option', async () => { const { stdout, stderr } = await runServe(['--client-log-level', 'info'], testPath); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); - it('compiles without flags', async () => { + it('should work with the "--port" option', async () => { const { stdout, stderr } = await runServe(['--port', port], testPath); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); - it('uses hot flag to alter bundle', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); + it('should work with the "--hot" option', async () => { + const { stderr, stdout } = await runServe(['--hot'], __dirname); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); - it('uses hot-only flag to alter bundle', async () => { + it('should work with the "--no-hot" option', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it('should work with the "--hot" option using the "only" value', async () => { const { stdout, stderr } = await runServe(['--port', port, isDevServer4 ? '--hot only' : '--hot-only'], testPath); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); - it('uses no-hot flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + it('should work with "--hot" and "--port" options', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); expect(stderr).toBeFalsy(); expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); - it('uses hot flag and progress flag', async () => { + it('should work with the "--hot" and "--progress" options', async () => { const { stdout, stderr } = await runServe(['--port', port, '--hot', '--progress'], testPath); expect(stderr).toContain('webpack.Progress'); expect(stdout).toContain('main.js'); - expect(stdout).toContain('HotModuleReplacementPlugin'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); - it('throws error on unknown flag', async () => { + it('should log and error on unknown flag', async () => { const { exitCode, stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); expect(exitCode).toBe(2); diff --git a/test/serve/basic/webpack.config.js b/test/serve/basic/webpack.config.js index 98b22edfdc9..cef4d803dc3 100644 --- a/test/serve/basic/webpack.config.js +++ b/test/serve/basic/webpack.config.js @@ -3,5 +3,5 @@ const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); module.exports = { mode: 'development', devtool: false, - plugins: [new WebpackCLITestPlugin(['mode', 'plugins'], false)], + plugins: [new WebpackCLITestPlugin(['mode'], false, 'hooks.compilation.taps')], }; diff --git a/test/utils/cli-plugin-test/plugin.test.js b/test/utils/cli-plugin-test/plugin.test.js index 77b4a6e8d09..f4add985907 100644 --- a/test/utils/cli-plugin-test/plugin.test.js +++ b/test/utils/cli-plugin-test/plugin.test.js @@ -14,6 +14,6 @@ describe('webpack-cli-test-plugin Test', () => { expect(stdout).toContain(`alias: { alias: [ 'alias1', 'alias2' ] }`); } - expect(stdout).toContain(` WebpackCLITestPlugin { opts: [Array], showAll: true }`); + expect(stdout).toContain('WebpackCLITestPlugin'); }); }); diff --git a/test/utils/webpack-cli-test-plugin.js b/test/utils/webpack-cli-test-plugin.js index b04869a16e4..510b5889bea 100644 --- a/test/utils/webpack-cli-test-plugin.js +++ b/test/utils/webpack-cli-test-plugin.js @@ -1,17 +1,32 @@ class WebpackCLITestPlugin { - constructor(options, showAll = true) { + constructor(options, showAll = true, showHooks) { this.opts = options; this.showAll = showAll; + this.showHooks = showHooks; } apply(compiler) { compiler.hooks.done.tap('webpack-cli Test Plugin', () => { + if (this.showHooks) { + const identifiers = this.showHooks.split('.'); + + let shown = compiler; + + identifiers.forEach((identifier) => { + shown = shown[identifier]; + }); + + console.log(shown); + } + if (this.showAll) { console.log(compiler.options); } + if (this.opts) { this.opts.map((e) => { const config = Object.getOwnPropertyDescriptor(compiler.options, e); + console.log(config.value); }); } From 92826d621df7454fd12b949ed926c236004439a0 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Tue, 29 Dec 2020 20:41:59 +0300 Subject: [PATCH 06/15] tests: open and serve (#2275) --- package.json | 6 +- packages/utils/package.json | 2 +- packages/webpack-cli/package.json | 2 +- test/serve/basic/serve-basic.test.js | 8 + yarn.lock | 215 ++++++++++++++------------- 5 files changed, 123 insertions(+), 110 deletions(-) diff --git a/package.json b/package.json index 7ddcd2bc6fb..08bbfd4a276 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "eslint-config-prettier": "^6.15.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^3.1.4", - "execa": "^4.1.0", + "execa": "^5.0.0", "get-port": "^5.1.1", "git-cz": "^4.7.1", "husky": "^4.3.0", @@ -91,8 +91,8 @@ "ts-jest": "^26.4.3", "typescript": "^3.9.7", "webpack": "^5.11.0", - "webpack-bundle-analyzer": "^3.9.0", - "webpack-dev-server": "^3.11.0", + "webpack-bundle-analyzer": "^4.3.0", + "webpack-dev-server": "^3.11.1", "yeoman-test": "^2.7.0" } } diff --git a/packages/utils/package.json b/packages/utils/package.json index 7ef4459979d..2f763950450 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -13,7 +13,7 @@ ], "dependencies": { "colorette": "^1.2.1", - "execa": "^4.1.0", + "execa": "^5.0.0", "findup-sync": "^4.0.0", "global-modules": "^2.0.0", "got": "^11.8.0", diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index 84e827d69f3..b0efd6392e1 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -33,7 +33,7 @@ "colorette": "^1.2.1", "commander": "^6.2.0", "enquirer": "^2.3.6", - "execa": "^4.1.0", + "execa": "^5.0.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index 7efedca684f..e6754e52658 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -141,6 +141,14 @@ describe('basic serve usage', () => { expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); + it.only('should work with the "--open" option', async () => { + const { stdout, stderr } = await runServe(['--open', '--port', port], testPath); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + it('should log and error on unknown flag', async () => { const { exitCode, stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); diff --git a/yarn.lock b/yarn.lock index 5eb31c139ca..b7143c47de9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2193,6 +2193,11 @@ dependencies: "@types/node" ">= 8" +"@polka/url@^1.0.0-next.9": + version "1.0.0-next.11" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.11.tgz#aeb16f50649a91af79dbe36574b66d0f9e4d9f71" + integrity sha512-3NsZsJIA/22P3QUyrEDNA2D133H4j224twJrdipXN38dpnIOzAbUDtOwkcJ5pXmn75w7LSQDjA4tO9dm1XlqlA== + "@samverschueren/stream-to-observable@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.1.tgz#a21117b19ee9be70c379ec1877537ef2e1c63301" @@ -2808,6 +2813,11 @@ acorn-walk@^7.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +acorn-walk@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.0.0.tgz#56ae4c0f434a45fff4a125e7ea95fa9c98f67a16" + integrity sha512-oZRad/3SMOI/pxbbmqyurIx7jHw1wZDcR9G44L8pUVFEomX/0dH89SrM1KaDXuv1NpzAXz6Op/Xu/Qd5XXzdEA== + acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" @@ -3280,16 +3290,6 @@ before-after-hook@^2.0.0: resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== -bfj@^6.1.1: - version "6.1.2" - resolved "https://registry.yarnpkg.com/bfj/-/bfj-6.1.2.tgz#325c861a822bcb358a41c78a33b8e6e2086dde7f" - integrity sha512-BmBJa4Lip6BPRINSZ0BPEIfB1wUY/9rwbwvIHQA1KjX9om29B6id0wnWXq7m3bn5JrUVjeOTnVuhPT1FiHwPGw== - dependencies: - bluebird "^3.5.5" - check-types "^8.0.3" - hoopy "^0.1.4" - tryer "^1.0.1" - binary-extensions@^1.0.0: version "1.13.1" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" @@ -3640,11 +3640,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -check-types@^8.0.3: - version "8.0.3" - resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" - integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== - chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -3888,7 +3883,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.18.0, commander@^2.20.0: +commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -4185,7 +4180,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.2: +cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4299,7 +4294,7 @@ debug@^3.1.0, debug@^3.1.1: dependencies: ms "^2.1.1" -debug@^3.2.5: +debug@^3.2.6: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -4623,7 +4618,7 @@ duplexer3@^0.1.4: resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= -duplexer@^0.1.1: +duplexer@^0.1.1, duplexer@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== @@ -5074,6 +5069,21 @@ execa@^4.0.0, execa@^4.1.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" +execa@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376" + integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -5111,7 +5121,7 @@ expect@^26.6.2: jest-message-util "^26.6.2" jest-regex-util "^26.0.0" -express@^4.16.3, express@^4.17.1: +express@^4.17.1: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== @@ -5256,14 +5266,7 @@ fastq@^1.6.0: dependencies: reusify "^1.0.4" -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: +faye-websocket@^0.11.3: version "0.11.3" resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== @@ -5323,11 +5326,6 @@ filelist@^1.0.1: dependencies: minimatch "^3.0.4" -filesize@^3.6.1: - version "3.6.1" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" - integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== - fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -5707,6 +5705,11 @@ get-stream@^5.0.0, get-stream@^5.1.0: dependencies: pump "^3.0.0" +get-stream@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718" + integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -5994,13 +5997,12 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -gzip-size@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" - integrity sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA== +gzip-size@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" + integrity sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q== dependencies: - duplexer "^0.1.1" - pify "^4.0.1" + duplexer "^0.1.2" handle-thing@^2.0.0: version "2.0.1" @@ -6124,11 +6126,6 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hoopy@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d" - integrity sha512-HRcs+2mr52W0K+x8RzcLzuPPmVIKMSv97RGHy0Ea9y/mpcaK+xTrjICA04KAHi4GRzxliNqNJEFYWHghy3rSfQ== - hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -6270,6 +6267,11 @@ human-signals@^1.1.1: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" integrity sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw== +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + humanize-ms@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" @@ -7509,7 +7511,7 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json3@^3.3.2: +json3@^3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== @@ -8216,7 +8218,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.4.4: +mime@^2.3.1, mime@^2.4.4: version "2.4.7" resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.7.tgz#962aed9be0ed19c91fd7dc2ece5d7f4e89a90d74" integrity sha512-dhNd1uA2u397uQk3Nv5LM4lm93WYDUXFn3Fu291FJerns4jyTudqhIWe4W04YLy7Uk1tm1Ore04NpjRvQp/NPA== @@ -8671,7 +8673,7 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -npm-run-path@^4.0.0: +npm-run-path@^4.0.0, npm-run-path@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== @@ -8836,7 +8838,7 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" -onetime@^5.1.0: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -8848,7 +8850,7 @@ opencollective-postinstall@^2.0.2: resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== -opener@^1.5.1: +opener@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== @@ -10129,7 +10131,7 @@ select-hose@^2.0.0: resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= -selfsigned@^1.10.7: +selfsigned@^1.10.8: version "1.10.8" resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== @@ -10299,7 +10301,7 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== @@ -10317,6 +10319,15 @@ sinon@^9.0.1: nise "^4.0.4" supports-color "^7.1.0" +sirv@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.10.tgz#3e591f5a9ae2520f50d5830f5fae38d97e7be194" + integrity sha512-H5EZCoZaggEUQy8ocKsF7WAToGuZhjJlLvM3XOef46CbdIgbNeQ1p32N1PCuCjkVYwrAVOSMacN6CXXgIzuspg== + dependencies: + "@polka/url" "^1.0.0-next.9" + mime "^2.3.1" + totalist "^1.0.0" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" @@ -10400,26 +10411,26 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sockjs-client@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" - integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== +sockjs-client@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.5.0.tgz#2f8ff5d4b659e0d092f7aba0b7c386bd2aa20add" + integrity sha512-8Dt3BDi4FYNrCFGTL/HtwVzkARrENdwOUf1ZoW/9p3M8lZdFT35jVdrHza+qgxuG9H3/shR4cuX/X9umUrjP8Q== dependencies: - debug "^3.2.5" + debug "^3.2.6" eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" + faye-websocket "^0.11.3" + inherits "^2.0.4" + json3 "^3.3.3" + url-parse "^1.4.7" -sockjs@0.3.20: - version "0.3.20" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" - integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== +sockjs@^0.3.21: + version "0.3.21" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" + integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== dependencies: - faye-websocket "^0.10.0" + faye-websocket "^0.11.3" uuid "^3.4.0" - websocket-driver "0.6.5" + websocket-driver "^0.7.4" socks-proxy-agent@^4.0.0: version "4.0.2" @@ -11091,6 +11102,11 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== +totalist@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" + integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== + tough-cookie@^2.3.3, tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -11142,11 +11158,6 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -tryer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" - integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== - ts-jest@^26.4.3: version "26.4.4" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.4.4.tgz#61f13fb21ab400853c532270e52cc0ed7e502c49" @@ -11393,7 +11404,7 @@ url-parse-lax@^1.0.0: dependencies: prepend-http "^1.0.1" -url-parse@^1.4.3: +url-parse@^1.4.3, url-parse@^1.4.7: version "1.4.7" resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== @@ -11572,24 +11583,20 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== -webpack-bundle-analyzer@^3.9.0: - version "3.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.9.0.tgz#f6f94db108fb574e415ad313de41a2707d33ef3c" - integrity sha512-Ob8amZfCm3rMB1ScjQVlbYYUEJyEjdEtQ92jqiFUYt5VkEeO2v5UMbv49P/gnmCZm3A6yaFQzCBvpZqN4MUsdA== +webpack-bundle-analyzer@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.3.0.tgz#2f3c0ca9041d5ee47fa418693cf56b4a518b578b" + integrity sha512-J3TPm54bPARx6QG8z4cKBszahnUglcv70+N+8gUqv2I5KOFHJbzBiLx+pAp606so0X004fxM7hqRu10MLjJifA== dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - bfj "^6.1.1" - chalk "^2.4.1" - commander "^2.18.0" - ejs "^2.6.1" - express "^4.16.3" - filesize "^3.6.1" - gzip-size "^5.0.0" - lodash "^4.17.19" - mkdirp "^0.5.1" - opener "^1.5.1" - ws "^6.0.0" + acorn "^8.0.4" + acorn-walk "^8.0.0" + chalk "^4.1.0" + commander "^6.2.0" + gzip-size "^6.0.0" + lodash "^4.17.20" + opener "^1.5.2" + sirv "^1.0.7" + ws "^7.3.1" webpack-dev-middleware@^3.7.2: version "3.7.3" @@ -11602,10 +11609,10 @@ webpack-dev-middleware@^3.7.2: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@^3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" - integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== +webpack-dev-server@^3.11.1: + version "3.11.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.1.tgz#c74028bf5ba8885aaf230e48a20e8936ab8511f0" + integrity sha512-u4R3mRzZkbxQVa+MBWi2uVpB5W59H3ekZAJsQlKUTdl7Elcah2EhygTPLmeFXybQkf9i2+L0kn7ik9SnXa6ihQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -11627,11 +11634,11 @@ webpack-dev-server@^3.11.0: p-retry "^3.0.1" portfinder "^1.0.26" schema-utils "^1.0.0" - selfsigned "^1.10.7" + selfsigned "^1.10.8" semver "^6.3.0" serve-index "^1.9.1" - sockjs "0.3.20" - sockjs-client "1.4.0" + sockjs "^0.3.21" + sockjs-client "^1.5.0" spdy "^4.0.2" strip-ansi "^3.0.1" supports-color "^6.1.0" @@ -11694,14 +11701,7 @@ webpack@^5.11.0: watchpack "^2.0.0" webpack-sources "^2.1.1" -websocket-driver@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" - integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= - dependencies: - websocket-extensions ">=0.1.1" - -websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -11884,7 +11884,7 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -ws@^6.0.0, ws@^6.2.1: +ws@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== @@ -11896,6 +11896,11 @@ ws@^7.2.3: resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== +ws@^7.3.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb" + integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" From 818fc1cbdf3c5b756b8ade6a2e7e032616aa520b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 30 Dec 2020 15:14:29 +0300 Subject: [PATCH 07/15] chore(deps-dev): bump webpack from 5.11.0 to 5.11.1 (#2270) Bumps [webpack](https://github.com/webpack/webpack) from 5.11.0 to 5.11.1. - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](https://github.com/webpack/webpack/compare/v5.11.0...v5.11.1) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b7143c47de9..c442a38f70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11672,9 +11672,9 @@ webpack-sources@^2.1.1: source-map "^0.6.1" webpack@^5.11.0: - version "5.11.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.0.tgz#1647abc060441d86d01d8835b8f0fc1dae2fe76f" - integrity sha512-ubWv7iP54RqAC/VjixgpnLLogCFbAfSOREcSWnnOlZEU8GICC5eKmJSu6YEnph2N2amKqY9rvxSwgyHxVqpaRw== + version "5.11.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.11.1.tgz#39b2b9daeb5c6c620e03b7556ec674eaed4016b4" + integrity sha512-tNUIdAmYJv+nupRs/U/gqmADm6fgrf5xE+rSlSsf2PgsGO7j2WG7ccU6AWNlOJlHFl+HnmXlBmHIkiLf+XA9mQ== dependencies: "@types/eslint-scope" "^3.7.0" "@types/estree" "^0.0.45" From 05956030cbb1491a2e9313732470bcd4ebe5a36d Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Wed, 30 Dec 2020 16:38:27 +0300 Subject: [PATCH 08/15] fix: the `--progress` option is working with `--json` (#2276) --- packages/webpack-cli/lib/plugins/CLIPlugin.js | 6 +- packages/webpack-cli/lib/webpack-cli.js | 5 +- test/build-errors/errors.test.js | 4 +- test/build-warnings/warnings.test.js | 5 +- test/json/json.test.js | 98 +++++++++++++++---- test/json/logging.config.js | 5 + 6 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 test/json/logging.config.js diff --git a/packages/webpack-cli/lib/plugins/CLIPlugin.js b/packages/webpack-cli/lib/plugins/CLIPlugin.js index 643088643e5..584d6e21a07 100644 --- a/packages/webpack-cli/lib/plugins/CLIPlugin.js +++ b/packages/webpack-cli/lib/plugins/CLIPlugin.js @@ -79,7 +79,7 @@ class CLIPlugin { apply(compiler) { this.logger = compiler.getInfrastructureLogger('webpack-cli'); - if (this.options.progress && this.options.helpfulOutput) { + if (this.options.progress) { this.setupProgressPlugin(compiler); } @@ -95,9 +95,7 @@ class CLIPlugin { this.setupBundleAnalyzerPlugin(compiler); } - if (this.options.helpfulOutput) { - this.setupHelpfulOutput(compiler); - } + this.setupHelpfulOutput(compiler); } } diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index 3a7f744ee81..67201852276 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -1234,7 +1234,10 @@ class WebpackCLI { .on('error', handleWriteError) .pipe(createWriteStream(options.json)) .on('error', handleWriteError) - .on('close', () => logger.success(`stats are successfully stored as json to ${options.json}`)); + // Use stderr to logging + .on('close', () => + process.stderr.write(`[webpack-cli] ${green(`stats are successfully stored as json to ${options.json}`)}\n`), + ); } } else { const printedStats = stats.toString(foundStats); diff --git a/test/build-errors/errors.test.js b/test/build-errors/errors.test.js index 50a397ebffa..30fac63060d 100644 --- a/test/build-errors/errors.test.js +++ b/test/build-errors/errors.test.js @@ -32,8 +32,8 @@ describe('errors', () => { const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json']); expect(exitCode).toBe(1); - expect(stderr).toBeFalsy(); - expect(stdout).toContain('stats are successfully stored as json to stats.json'); + expect(stderr).toContain('stats are successfully stored as json to stats.json'); + expect(stdout).toBeFalsy(); readFile(resolve(__dirname, 'stats.json'), 'utf-8', (error, data) => { expect(error).toBe(null); diff --git a/test/build-warnings/warnings.test.js b/test/build-warnings/warnings.test.js index 27422faf42e..8bf676da732 100644 --- a/test/build-warnings/warnings.test.js +++ b/test/build-warnings/warnings.test.js @@ -33,9 +33,8 @@ describe('warnings', () => { const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json']); expect(exitCode).toBe(0); - expect(stderr).toBeFalsy(); - expect(stdout).toContain('stats are successfully stored as json to stats.json'); - + expect(stderr).toContain('stats are successfully stored as json to stats.json'); + expect(stdout).toBeFalsy(); expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); readFile(resolve(__dirname, 'stats.json'), 'utf-8', (error, data) => { diff --git a/test/json/json.test.js b/test/json/json.test.js index 9fd1ef88964..64441e4504a 100644 --- a/test/json/json.test.js +++ b/test/json/json.test.js @@ -5,20 +5,24 @@ const { resolve } = require('path'); const successMessage = 'stats are successfully stored as json to stats.json'; -describe('json flag', () => { - it('should return valid json', () => { - const { stdout, exitCode } = run(__dirname, ['--json']); - expect(() => JSON.parse(stdout)).not.toThrow(); +describe('json', () => { + it('should work and output json stats', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json']); + expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(() => JSON.parse(stdout)).not.toThrow(); expect(JSON.parse(stdout)['hash']).toBeDefined(); }); - it('should store json to a file', (done) => { - const { stdout, exitCode } = run(__dirname, ['--json', 'stats.json']); + it('should work and store json to a file', (done) => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json']); - expect(stdout).toContain(successMessage); expect(exitCode).toBe(0); + expect(stderr).toContain(successMessage); + expect(stdout).toBeFalsy(); expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); + readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { expect(err).toBe(null); expect(JSON.parse(data)['hash']).toBeTruthy(); @@ -29,12 +33,31 @@ describe('json flag', () => { }); }); - it('should store json to a file and respect --color flag', (done) => { - const { stdout, exitCode } = run(__dirname, ['--json', 'stats.json', '--color']); + it('should work and store json to a file and respect --color flag', (done) => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json', '--color']); - expect(stdout).toContain(`\u001b[32m${successMessage}`); expect(exitCode).toBe(0); + expect(stderr).toContain(`\u001b[32m${successMessage}`); + expect(stdout).toBeFalsy(); + expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); + readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { + expect(err).toBe(null); + expect(JSON.parse(data)['hash']).toBeTruthy(); + expect(JSON.parse(data)['version']).toBeTruthy(); + expect(JSON.parse(data)['time']).toBeTruthy(); + expect(() => JSON.parse(data)).not.toThrow(); + done(); + }); + }); + + it('should work and store json to a file and respect --no-color', (done) => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json', '--no-color']); + + expect(exitCode).toBe(0); + expect(stderr).not.toContain(`\u001b[32m${successMessage}`); + expect(stderr).toContain(`${successMessage}`); + expect(stdout).toBeFalsy(); expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { @@ -47,13 +70,31 @@ describe('json flag', () => { }); }); - it('should store json to a file and respect --no-color', (done) => { - const { stdout, exitCode } = run(__dirname, ['--json', 'stats.json', '--no-color']); + it('should work using the "-j" option (alias)', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['-j']); - expect(stdout).not.toContain(`\u001b[32m${successMessage}`); - expect(stdout).toContain(`${successMessage}`); expect(exitCode).toBe(0); + expect(stderr).toBeFalsy(); + expect(() => JSON.parse(stdout)).not.toThrow(); + expect(JSON.parse(stdout)['hash']).toBeDefined(); + }); + + it('should work and output json stats with the "--progress" option', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', '--progress']); + expect(exitCode).toBe(0); + expect(stderr).toContain('webpack.Progress'); + expect(() => JSON.parse(stdout)).not.toThrow(); + expect(JSON.parse(stdout)['hash']).toBeDefined(); + }); + + it('should work and store json to a file with the "--progress" option', (done) => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json', '--progress']); + + expect(exitCode).toBe(0); + expect(stderr).toContain('webpack.Progress'); + expect(stderr).toContain(successMessage); + expect(stdout).toBeFalsy(); expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { @@ -66,10 +107,33 @@ describe('json flag', () => { }); }); - it('should return valid json with -j alias', () => { - const { stdout, exitCode } = run(__dirname, ['-j']); - expect(() => JSON.parse(stdout)).not.toThrow(); + it('should work and output json stats with cli logs', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', '--config', 'logging.config.js']); + expect(exitCode).toBe(0); + expect(stderr).toContain('Compilation starting'); + expect(stderr).toContain('Compilation finished'); + expect(() => JSON.parse(stdout)).not.toThrow(); expect(JSON.parse(stdout)['hash']).toBeDefined(); }); + + it('should work and store json to a file with cli logs', (done) => { + const { exitCode, stderr, stdout } = run(__dirname, ['--json', 'stats.json', '--config', 'logging.config.js']); + + expect(exitCode).toBe(0); + expect(stderr).toContain('Compilation starting'); + expect(stderr).toContain('Compilation finished'); + expect(stderr).toContain(successMessage); + expect(stdout).toBeFalsy(); + expect(existsSync(resolve(__dirname, './stats.json'))).toBeTruthy(); + + readFile(resolve(__dirname, 'stats.json'), 'utf-8', (err, data) => { + expect(err).toBe(null); + expect(JSON.parse(data)['hash']).toBeTruthy(); + expect(JSON.parse(data)['version']).toBeTruthy(); + expect(JSON.parse(data)['time']).toBeTruthy(); + expect(() => JSON.parse(data)).not.toThrow(); + done(); + }); + }); }); diff --git a/test/json/logging.config.js b/test/json/logging.config.js new file mode 100644 index 00000000000..481fe38bff4 --- /dev/null +++ b/test/json/logging.config.js @@ -0,0 +1,5 @@ +module.exports = { + infrastructureLogging: { + level: 'log', + }, +}; From a3092ef2b51ece30221f7dd7b30a686626c1fd7a Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Wed, 30 Dec 2020 17:28:03 +0300 Subject: [PATCH 09/15] fix: respect the `output.publicPath` option for the `serve`command (#2271) --- .../__snapshots__/startDevServer.test.ts.snap | 5 + packages/serve/src/startDevServer.ts | 73 ++++++++------ packages/serve/src/types.ts | 1 + .../dev-server-output-public-path.config.js | 13 +++ ...ti-dev-server-output-public-path.config.js | 26 +++++ test/serve/basic/multi-dev-server.config.js | 32 +++++++ .../basic/multi-output-public-path.config.js | 23 +++++ test/serve/basic/multi.config.js | 25 +++++ .../serve/basic/multiple-dev-server.config.js | 29 ++++++ test/serve/basic/output-public-path.config.js | 10 ++ test/serve/basic/serve-basic.test.js | 96 ++++++++++++++++++- test/serve/basic/src/other.js | 1 + 12 files changed, 302 insertions(+), 32 deletions(-) create mode 100644 test/serve/basic/dev-server-output-public-path.config.js create mode 100644 test/serve/basic/multi-dev-server-output-public-path.config.js create mode 100644 test/serve/basic/multi-dev-server.config.js create mode 100644 test/serve/basic/multi-output-public-path.config.js create mode 100644 test/serve/basic/multi.config.js create mode 100644 test/serve/basic/multiple-dev-server.config.js create mode 100644 test/serve/basic/output-public-path.config.js create mode 100644 test/serve/basic/src/other.js diff --git a/packages/serve/__tests__/__snapshots__/startDevServer.test.ts.snap b/packages/serve/__tests__/__snapshots__/startDevServer.test.ts.snap index e6fdcbe3b3d..ad76801f1da 100644 --- a/packages/serve/__tests__/__snapshots__/startDevServer.test.ts.snap +++ b/packages/serve/__tests__/__snapshots__/startDevServer.test.ts.snap @@ -4,6 +4,7 @@ exports[`startDevServer should set default port and host if not provided 1`] = ` Object { "host": "localhost", "port": 8080, + "publicPath": "/", } `; @@ -22,6 +23,7 @@ Object { "hot": true, "port": 9000, "progress": true, + "publicPath": "/", } `; @@ -40,6 +42,7 @@ Object { "hot": true, "port": 9000, "progress": true, + "publicPath": "/", } `; @@ -56,6 +59,7 @@ Object { "host": "localhost", "port": 9000, "progress": true, + "publicPath": "/", } `; @@ -64,6 +68,7 @@ Object { "host": "localhost", "port": 9001, "progress": true, + "publicPath": "/", } `; diff --git a/packages/serve/src/startDevServer.ts b/packages/serve/src/startDevServer.ts index 08a9478cc76..dd5065248ca 100644 --- a/packages/serve/src/startDevServer.ts +++ b/packages/serve/src/startDevServer.ts @@ -11,10 +11,7 @@ import { devServerOptionsType } from './types'; * @returns {Object[]} array of resulting servers */ export default async function startDevServer(compiler, cliOptions, logger): Promise { - let isDevServer4 = false, - devServerVersion, - Server, - findPort; + let devServerVersion, Server, findPort; try { // eslint-disable-next-line node/no-extraneous-require @@ -28,24 +25,6 @@ export default async function startDevServer(compiler, cliOptions, logger): Prom process.exit(2); } - isDevServer4 = devServerVersion.startsWith('4'); - - const defaultOpts = {}; - const devServerOptions = []; - const compilers = compiler.compilers || [compiler]; - - compilers.forEach((compiler) => { - if (compiler.options.devServer) { - devServerOptions.push(compiler.options.devServer); - } - }); - - if (devServerOptions.length === 0) { - devServerOptions.push(defaultOpts); - } - - const servers = []; - const usedPorts: number[] = []; const mergeOptions = (cliOptions: devServerOptionsType, devServerOptions: devServerOptionsType): devServerOptionsType => { // CLI options should take precedence over devServer options, // and CLI options should have no default values included @@ -60,35 +39,69 @@ export default async function startDevServer(compiler, cliOptions, logger): Prom return options; }; - for (const devServerOpts of devServerOptions) { - const options = mergeOptions(cliOptions, devServerOpts); + const isMultiCompiler = Boolean(compiler.compilers); + + let compilersWithDevServerOption; + + if (isMultiCompiler) { + compilersWithDevServerOption = compiler.compilers.filter((compiler) => compiler.options.devServer); + + // No compilers found with the `devServer` option, let's use first compiler + if (compilersWithDevServerOption.length === 0) { + compilersWithDevServerOption = [compiler.compilers[0]]; + } + } else { + compilersWithDevServerOption = [compiler]; + } + + const isDevServer4 = devServerVersion.startsWith('4'); + const usedPorts = []; + const devServersOptions = []; + + for (const compilerWithDevServerOption of compilersWithDevServerOption) { + const options = mergeOptions(cliOptions, compilerWithDevServerOption.options.devServer || {}); if (isDevServer4) { options.port = await findPort(options.port); options.client = options.client || {}; options.client.port = options.client.port || options.port; } else { + if (!options.publicPath) { + options.publicPath = + typeof compilerWithDevServerOption.options.output.publicPath === 'undefined' || + compilerWithDevServerOption.options.output.publicPath === 'auto' + ? '/' + : compilerWithDevServerOption.options.output.publicPath; + } + options.host = options.host || 'localhost'; options.port = options.port || 8080; } if (options.port) { - const portNum = +options.port; + const portNumber = Number(options.port); - if (usedPorts.find((port) => portNum === port)) { + if (usedPorts.find((port) => portNumber === port)) { throw new Error( 'Unique ports must be specified for each devServer option in your webpack configuration. Alternatively, run only 1 devServer config using the --config-name flag to specify your desired config.', ); } - usedPorts.push(portNum); + usedPorts.push(portNumber); } + devServersOptions.push({ compiler, options }); + } + + const servers = []; + + for (const devServerOptions of devServersOptions) { + const { compiler, options } = devServerOptions; const server = new Server(compiler, options); - server.listen(options.port, options.host, (err): void => { - if (err) { - throw err; + server.listen(options.port, options.host, (error): void => { + if (error) { + throw error; } }); diff --git a/packages/serve/src/types.ts b/packages/serve/src/types.ts index 61c59c5543a..f4a1b276fd0 100644 --- a/packages/serve/src/types.ts +++ b/packages/serve/src/types.ts @@ -27,6 +27,7 @@ export type devServerOptionsType = { static?: boolean | string | object | (string | object)[]; transportMode?: object | string; useLocalIp?: boolean; + publicPath?: undefined; }; type devServerClientOptions = { diff --git a/test/serve/basic/dev-server-output-public-path.config.js b/test/serve/basic/dev-server-output-public-path.config.js new file mode 100644 index 00000000000..e84e9137dd6 --- /dev/null +++ b/test/serve/basic/dev-server-output-public-path.config.js @@ -0,0 +1,13 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = { + mode: 'development', + devtool: false, + output: { + publicPath: '/my-public-path/', + }, + devServer: { + publicPath: '/dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], +}; diff --git a/test/serve/basic/multi-dev-server-output-public-path.config.js b/test/serve/basic/multi-dev-server-output-public-path.config.js new file mode 100644 index 00000000000..56409276d4e --- /dev/null +++ b/test/serve/basic/multi-dev-server-output-public-path.config.js @@ -0,0 +1,26 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = [ + { + name: 'one', + mode: 'development', + devtool: false, + entry: './src/other.js', + output: { + filename: 'first-output/[name].js', + }, + }, + { + name: 'two', + mode: 'development', + devtool: false, + output: { + publicPath: '/my-public-path/', + filename: 'second-output/[name].js', + }, + devServer: { + publicPath: '/dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], + }, +]; diff --git a/test/serve/basic/multi-dev-server.config.js b/test/serve/basic/multi-dev-server.config.js new file mode 100644 index 00000000000..59bb8f16133 --- /dev/null +++ b/test/serve/basic/multi-dev-server.config.js @@ -0,0 +1,32 @@ +const getPort = require('get-port'); + +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = async () => [ + { + name: 'one', + mode: 'development', + devtool: false, + output: { + filename: 'first-output/[name].js', + }, + devServer: { + port: await getPort(), + publicPath: '/one-dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], + }, + { + name: 'two', + mode: 'development', + devtool: false, + entry: './src/other.js', + output: { + filename: 'second-output/[name].js', + }, + devServer: { + port: await getPort(), + publicPath: '/two-dev-server-my-public-path/', + }, + }, +]; diff --git a/test/serve/basic/multi-output-public-path.config.js b/test/serve/basic/multi-output-public-path.config.js new file mode 100644 index 00000000000..64286a1c2ef --- /dev/null +++ b/test/serve/basic/multi-output-public-path.config.js @@ -0,0 +1,23 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = [ + { + name: 'one', + mode: 'development', + devtool: false, + output: { + publicPath: '/my-public-path/', + filename: 'first-output/[name].js', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], + }, + { + name: 'two', + mode: 'development', + devtool: false, + entry: './src/other.js', + output: { + filename: 'second-output/[name].js', + }, + }, +]; diff --git a/test/serve/basic/multi.config.js b/test/serve/basic/multi.config.js new file mode 100644 index 00000000000..e344db6c72b --- /dev/null +++ b/test/serve/basic/multi.config.js @@ -0,0 +1,25 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = [ + { + name: 'one', + mode: 'development', + devtool: false, + output: { + filename: 'first-output/[name].js', + }, + devServer: { + publicPath: '/dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], + }, + { + name: 'two', + mode: 'development', + devtool: false, + entry: './src/other.js', + output: { + filename: 'second-output/[name].js', + }, + }, +]; diff --git a/test/serve/basic/multiple-dev-server.config.js b/test/serve/basic/multiple-dev-server.config.js new file mode 100644 index 00000000000..154a03b2012 --- /dev/null +++ b/test/serve/basic/multiple-dev-server.config.js @@ -0,0 +1,29 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = [ + { + name: 'one', + mode: 'development', + devtool: false, + output: { + filename: 'first-output/[name].js', + }, + devServer: { + publicPath: '/dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false)], + }, + { + name: 'two', + mode: 'development', + devtool: false, + entry: './src/other.js', + output: { + filename: 'first-output/[name].js', + }, + devServer: { + publicPath: '/dev-server-my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false)], + }, +]; diff --git a/test/serve/basic/output-public-path.config.js b/test/serve/basic/output-public-path.config.js new file mode 100644 index 00000000000..0b9f7f04547 --- /dev/null +++ b/test/serve/basic/output-public-path.config.js @@ -0,0 +1,10 @@ +const WebpackCLITestPlugin = require('../../utils/webpack-cli-test-plugin'); + +module.exports = { + mode: 'development', + devtool: false, + output: { + publicPath: '/my-public-path/', + }, + plugins: [new WebpackCLITestPlugin(['mode', 'output'], false, 'hooks.compilation.taps')], +}; diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index e6754e52658..f9f68736d73 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -2,7 +2,7 @@ const path = require('path'); const getPort = require('get-port'); -const { runServe, isDevServer4 } = require('../../utils/test-utils'); +const { runServe, isWebpack5, isDevServer4 } = require('../../utils/test-utils'); const testPath = path.resolve(__dirname); @@ -34,6 +34,29 @@ describe('basic serve usage', () => { expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); + it('should work in multi compiler mode', async () => { + const { stderr, stdout } = await runServe(['serve', '--config', 'multi.config.js', '--port', port], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('one'); + expect(stdout).toContain('first-output/main.js'); + expect(stdout).toContain('two'); + expect(stdout).toContain('second-output/main.js'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + // TODO need fix in future, edge case + it.skip('should work in multi compiler mode with multiple dev servers', async () => { + const { stderr, stdout } = await runServe(['serve', '--config', 'multi-dev-server.config.js'], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('one'); + expect(stdout).toContain('first-output/main.js'); + expect(stdout).toContain('two'); + expect(stdout).toContain('second-output/main.js'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + it('should work with the "--mode" option', async () => { const { stderr, stdout } = await runServe([], __dirname); @@ -141,7 +164,60 @@ describe('basic serve usage', () => { expect(stdout.match(/HotModuleReplacementPlugin/g)).toHaveLength(1); }); - it.only('should work with the "--open" option', async () => { + it('should work with the default "publicPath" option', async () => { + const { stderr, stdout } = await runServe(['serve'], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('from /'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it('should work with the "--output-public-path" option', async () => { + const { stderr, stdout } = await runServe(['serve', '--output-public-path', '/my-public-path/'], __dirname); + + if (isWebpack5) { + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('/my-public-path/'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + } else { + expect(stderr).toContain("unknown option '--output-public-path'"); + expect(stdout).toBeFalsy(); + } + }); + + it('should respect the "publicPath" option from configuration', async () => { + const { stderr, stdout } = await runServe(['serve', '--config', 'output-public-path.config.js'], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('/my-public-path/'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it('should respect the "publicPath" option from configuration using multi compiler mode', async () => { + const { stderr, stdout } = await runServe(['serve', '--config', 'multi-output-public-path.config.js', '--port', port], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('one'); + expect(stdout).toContain('first-output/main.js'); + expect(stdout).toContain('two'); + expect(stdout).toContain('second-output/main.js'); + expect(stdout).toContain('/my-public-path/'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it('should respect the "publicPath" option from configuration (from the "devServer" options)', async () => { + const { stderr, stdout } = await runServe(['serve', '--config', 'dev-server-output-public-path.config.js'], __dirname); + + expect(stderr).toBeFalsy(); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('/dev-server-my-public-path/'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + + it('should work with the "--open" option', async () => { const { stdout, stderr } = await runServe(['--open', '--port', port], testPath); expect(stderr).toBeFalsy(); @@ -149,6 +225,22 @@ describe('basic serve usage', () => { expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); }); + it('should respect the "publicPath" option from configuration using multi compiler mode (from the "devServer" options)', async () => { + const { stderr, stdout } = await runServe( + ['serve', '--config', 'multi-dev-server-output-public-path.config.js', '--port', port], + __dirname, + ); + + expect(stderr).toBeFalsy(); + expect(stderr).toBeFalsy(); + expect(stdout).toContain('one'); + expect(stdout).toContain('first-output/main.js'); + expect(stdout).toContain('two'); + expect(stdout).toContain('second-output/main.js'); + expect(stdout).toContain('/dev-server-my-public-path/'); + expect(stdout.match(/HotModuleReplacementPlugin/g)).toBeNull(); + }); + it('should log and error on unknown flag', async () => { const { exitCode, stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); diff --git a/test/serve/basic/src/other.js b/test/serve/basic/src/other.js new file mode 100644 index 00000000000..2457f618e17 --- /dev/null +++ b/test/serve/basic/src/other.js @@ -0,0 +1 @@ +console.log('HERE'); From 2b5874e7da98d5439be6e30b6b9ffb0aa19f0b03 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 31 Dec 2020 18:43:01 +0300 Subject: [PATCH 10/15] refactor: code (#2280) --- packages/webpack-cli/__tests__/CLI.test.js | 1228 +++++++++++++++++ .../webpack-cli/__tests__/arg-parser.test.js | 472 ------- packages/webpack-cli/lib/utils/cli-flags.js | 28 +- packages/webpack-cli/lib/webpack-cli.js | 154 ++- .../function-with-env.test.js | 2 +- 5 files changed, 1335 insertions(+), 549 deletions(-) create mode 100644 packages/webpack-cli/__tests__/CLI.test.js delete mode 100644 packages/webpack-cli/__tests__/arg-parser.test.js diff --git a/packages/webpack-cli/__tests__/CLI.test.js b/packages/webpack-cli/__tests__/CLI.test.js new file mode 100644 index 00000000000..cf64bbf55b0 --- /dev/null +++ b/packages/webpack-cli/__tests__/CLI.test.js @@ -0,0 +1,1228 @@ +const CLI = require('../lib/webpack-cli'); + +describe('CLI API', () => { + let cli; + + beforeEach(() => { + cli = new CLI(); + }); + + describe('makeCommand', () => { + it('should make command', async (done) => { + const command = await cli.makeCommand({ name: 'command' }, [], (program) => { + expect(program.opts()).toEqual({}); + + done(); + }); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with Boolean option by default', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean'], { from: 'user' }); + }); + + it('should make command with Boolean option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: Boolean, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean'], { from: 'user' }); + }); + + it('should make command with Boolean option and negative value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: Boolean, + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: false }); + + done(); + }, + ); + + command.parseAsync(['--no-boolean'], { from: 'user' }); + }); + + it('should make command with Boolean option and negative value #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: Boolean, + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: false }); + + done(); + }, + ); + + command.parseAsync(['--boolean', '--no-boolean'], { from: 'user' }); + }); + + it('should make command with Boolean option and negative value #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: Boolean, + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: true }); + + done(); + }, + ); + + command.parseAsync(['--no-boolean', '--boolean'], { from: 'user' }); + }); + + it('should make command with Boolean option with default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: Boolean, + description: 'description', + defaultValue: false, + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: false }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with String option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + type: String, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'bar' }); + + done(); + }, + ); + + command.parseAsync(['--string', 'bar'], { from: 'user' }); + }); + + it('should make command with String option with alias', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + alias: 's', + type: String, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'foo' }); + + done(); + }, + ); + + command.parseAsync(['-s', 'foo'], { from: 'user' }); + }); + + it('should make command with String option with default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + type: String, + description: 'description', + defaultValue: 'default-value', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'default-value' }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with String option with default value #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + type: String, + description: 'description', + defaultValue: 'default-value', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'foo' }); + + done(); + }, + ); + + command.parseAsync(['--string', 'foo'], { from: 'user' }); + }); + + it('should make command with String option using "=" syntax', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + type: String, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'bar' }); + + done(); + }, + ); + + command.parseAsync(['--string=bar'], { from: 'user' }); + }); + + it('should make command with multiple String option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + multiple: true, + type: String, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: ['foo', 'bar'] }); + + done(); + }, + ); + + command.parseAsync(['--string', 'foo', 'bar'], { from: 'user' }); + }); + + it('should make command with multiple String option with default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + multiple: true, + type: String, + description: 'description', + defaultValue: 'string', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: 'string' }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with multiple String option with default value #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + multiple: true, + type: String, + description: 'description', + defaultValue: 'string', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: ['foo', 'bar'] }); + + done(); + }, + ); + + command.parseAsync(['--string', 'foo', '--string', 'bar'], { from: 'user' }); + }); + + it('should make command with multiple String option #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'string', + multiple: true, + type: String, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ string: ['foo', 'bar'] }); + + done(); + }, + ); + + command.parseAsync(['--string', 'foo', '--string', 'bar'], { from: 'user' }); + }); + + it('should make command with Number option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'number', + type: Number, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ number: 12 }); + + done(); + }, + ); + + command.parseAsync(['--number', '12'], { from: 'user' }); + }); + + it('should make command with Number option with default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'number', + type: Number, + description: 'description', + defaultValue: 20, + }, + ], + (program) => { + expect(program.opts()).toEqual({ number: 20 }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with multiple Number option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'number', + multiple: true, + type: Number, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ number: [1, 2] }); + + done(); + }, + ); + + command.parseAsync(['--number', '1', '--number', '2'], { from: 'user' }); + }); + + it('should make command with multiple Number option and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'number', + multiple: true, + type: Number, + description: 'description', + defaultValue: 50, + }, + ], + (program) => { + expect(program.opts()).toEqual({ number: [1, 2] }); + + done(); + }, + ); + + command.parseAsync(['--number', '1', '--number', '2'], { from: 'user' }); + }); + + it('should make command with multiple Number option and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'number', + multiple: true, + type: Number, + description: 'description', + defaultValue: 50, + }, + ], + (program) => { + expect(program.opts()).toEqual({ number: 50 }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with custom function type', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'custom', + type: () => { + return 'function'; + }, + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ custom: 'function' }); + + done(); + }, + ); + + command.parseAsync(['--custom', 'value'], { from: 'user' }); + }); + + it('should make command with custom function type and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'custom', + type: () => { + return 'function'; + }, + description: 'description', + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ custom: 'default' }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with multiple custom function type', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'custom', + type: (value, previous = []) => { + return previous.concat([value]); + }, + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ custom: ['value', 'other'] }); + + done(); + }, + ); + + command.parseAsync(['--custom', 'value', '--custom', 'other'], { from: 'user' }); + }); + + it('should make command with multiple custom function type and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'custom', + type: (value, previous = []) => { + return previous.concat([value]); + }, + description: 'description', + multiple: true, + defaultValue: 50, + }, + ], + (program) => { + expect(program.opts()).toEqual({ custom: 50 }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with multiple custom function type and default value #2', async (done) => { + let skipDefault = true; + + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'custom', + type: (value, previous = []) => { + if (skipDefault) { + previous = []; + skipDefault = false; + } + + return [].concat(previous).concat([value]); + }, + description: 'description', + multiple: true, + defaultValue: 50, + }, + ], + (program) => { + expect(program.opts()).toEqual({ custom: ['foo'] }); + + done(); + }, + ); + + command.parseAsync(['--custom', 'foo'], { from: 'user' }); + }); + + it('should make command with Boolean and String option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string'], { from: 'user' }); + }); + + it('should make command with Boolean and String option #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: 'value' }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string', 'value'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and String option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and String option #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: ['bar', 'baz'] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string', 'bar', '--boolean-and-string', 'baz'], { from: 'user' }); + }); + + it('should make command with Boolean and String option and negative', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string'], { from: 'user' }); + }); + + it('should make command with Boolean and String option and negative #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: 'foo' }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-string', 'foo'], { from: 'user' }); + }); + + it('should make command with Boolean and String option and negative #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-string', + type: [Boolean, String], + description: 'description', + negative: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndString: false }); + + done(); + }, + ); + + command.parseAsync(['--no-boolean-and-string'], { from: 'user' }); + }); + + it('should make command with Boolean and Number option', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number', + type: [Boolean, Number], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumber: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number'], { from: 'user' }); + }); + + it('should make command with Boolean and Number option #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number', + type: [Boolean, Number], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumber: 12 }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number', '12'], { from: 'user' }); + }); + + it('should make command with array Boolean type', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean', + type: [Boolean], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ boolean: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 12 }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 'bar' }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'bar'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 'default' }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type and default value #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 'foo' }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type and default value #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 12 }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with Boolean and Number and String type and default value #4', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 'default' }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String type', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: true }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String type #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: ['foo'] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String type #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: [12] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String type #4', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: ['foo', 'bar'] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo', '--boolean-and-number-and-string', 'bar'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String type #5', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: ['foo', 12] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo', '--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String and default value', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: 'default' }); + + done(); + }, + ); + + command.parseAsync([], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String and default value #2', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: ['foo'] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String and default value #3', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: [12] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with multiple Boolean and Number and String and default value #4', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'boolean-and-number-and-string', + type: [Boolean, Number, String], + description: 'description', + multiple: true, + defaultValue: 'default', + }, + ], + (program) => { + expect(program.opts()).toEqual({ booleanAndNumberAndString: ['foo', 12] }); + + done(); + }, + ); + + command.parseAsync(['--boolean-and-number-and-string', 'foo', '--boolean-and-number-and-string', '12'], { from: 'user' }); + }); + + it('should make command with array of unknown types', async (done) => { + const command = await cli.makeCommand( + { + name: 'command', + }, + [ + { + name: 'unknown', + type: [Boolean, Symbol], + description: 'description', + }, + ], + (program) => { + expect(program.opts()).toEqual({ unknown: 'foo' }); + + done(); + }, + ); + + command.parseAsync(['--unknown', 'foo'], { from: 'user' }); + }); + }); +}); diff --git a/packages/webpack-cli/__tests__/arg-parser.test.js b/packages/webpack-cli/__tests__/arg-parser.test.js deleted file mode 100644 index 55956e85d88..00000000000 --- a/packages/webpack-cli/__tests__/arg-parser.test.js +++ /dev/null @@ -1,472 +0,0 @@ -const warnMock = jest.fn(); -const rawMock = jest.fn(); -jest.mock('../lib/utils/logger', () => { - return { - warn: warnMock, - raw: rawMock, - }; -}); - -const processExitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {}); -const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); - -const argParser = () => {}; -const { flags } = require('../lib/utils/cli-flags'); - -const basicOptions = [ - { - name: 'bool-flag', - alias: 'b', - usage: '--bool-flag', - type: Boolean, - description: 'boolean flag', - }, - { - name: 'num-flag', - usage: '--num-flag ', - type: Number, - description: 'number flag', - }, - { - name: 'neg-flag', - alias: 'n', - usage: '--neg-flag', - type: Boolean, - negative: true, - description: 'boolean flag', - }, - { - name: 'string-flag', - alias: 's', - usage: '--string-flag ', - type: String, - description: 'string flag', - }, - { - name: 'string-flag-with-default', - usage: '--string-flag-with-default ', - type: String, - description: 'string flag', - defaultValue: 'default-value', - }, - { - name: 'multi-type', - alias: 'm', - usage: '--multi-type | --multi-type ', - type: [String, Boolean], - negative: true, - description: 'flag with multiple types', - }, - { - name: 'multi-type-different-order', - usage: '--multi-type-different-order | --multi-type-different-order ', - // duplicates and a different ordering should be handled correctly - type: [Boolean, String, Boolean], - description: 'flag with multiple types in different order', - }, - { - name: 'multi-type-empty', - usage: '--multi-type-empty', - // should default to Boolean type - type: [], - description: 'flag with empty multi type array', - }, - { - name: 'multi-type-number', - usage: '--multi-type-number', - // should use only Number type (the first in the array), - // because Number and Boolean together are not supported - type: [Number, Boolean], - description: 'flag with number multi type', - }, - { - name: 'custom-type-flag', - usage: '--custom-type-flag ', - type: (val) => { - return val.split(','); - }, - description: 'custom type flag', - }, - { - name: 'multi-flag', - usage: '--multi-flag ', - type: String, - multiple: true, - description: 'multi flag', - }, - { - name: 'processor-flag', - usage: '--processor-flag', - type: Boolean, - description: 'flag with processor', - processor(opts) { - opts.processed = opts.processorFlag; - delete opts.processorFlag; - }, - }, - { - name: 'env', - usage: '--env', - type: String, - multipleType: true, - description: 'Environment passed to the configuration when it is a function', - }, -]; - -const helpAndVersionOptions = basicOptions.slice(0); -helpAndVersionOptions.push( - { - name: 'help', - usage: '--help', - type: Boolean, - description: 'help', - }, - { - name: 'version', - alias: 'v', - usage: '--version', - type: Boolean, - description: 'version', - }, -); - -describe.skip('arg-parser', () => { - beforeEach(() => { - warnMock.mockClear(); - processExitSpy.mockClear(); - consoleErrorSpy.mockClear(); - }); - - it('parses no flags', () => { - const res = argParser(basicOptions, [], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses basic flags', () => { - const res = argParser(basicOptions, ['--bool-flag', '--string-flag', 'val', '--num-flag', '100'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - boolFlag: true, - numFlag: 100, - stringFlag: 'val', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses number flags', () => { - const res = argParser(basicOptions, ['--num-flag', '100'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - numFlag: 100, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses number flags with = sign', () => { - const res = argParser(basicOptions, ['--num-flag=10'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - numFlag: 10, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('should not parse negated boolean flags which are not specified', () => { - const res = argParser(basicOptions, ['--no-bool-flag'], true); - expect(res.unknownArgs.includes('--no-bool-flag')).toBeTruthy(); - }); - - it('parses boolean flag alias', () => { - const res = argParser(basicOptions, ['-b'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - boolFlag: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses string flag alias', () => { - const res = argParser(basicOptions, ['-s', 'string-value'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - stringFlag: 'string-value', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag as Boolean', () => { - const res = argParser(basicOptions, ['--multi-type'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiType: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag as String', () => { - const res = argParser(basicOptions, ['--multi-type', 'value'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiType: 'value', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag alias as Boolean', () => { - const res = argParser(basicOptions, ['-m'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiType: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag alias as String', () => { - const res = argParser(basicOptions, ['-m', 'value'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiType: 'value', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses negated multi type flag as Boolean', () => { - const res = argParser(basicOptions, ['--no-multi-type'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiType: false, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag with different ordering as Boolean', () => { - const res = argParser(basicOptions, ['--multi-type-different-order'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiTypeDifferentOrder: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag with different ordering as String', () => { - const res = argParser(basicOptions, ['--multi-type-different-order', 'value'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiTypeDifferentOrder: 'value', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses empty multi type flag array as Boolean', () => { - const res = argParser(basicOptions, ['--multi-type-empty'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiTypeEmpty: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multi type flag (Number, Boolean) as Number', () => { - const res = argParser(basicOptions, ['--multi-type-number', '1.1'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiTypeNumber: 1.1, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parsing multi type flag (Number, Boolean) as Boolean fails', () => { - // this should fail because a multi type of Number and Boolean is - // not supported - argParser(basicOptions, ['--multi-type-number'], true); - expect(warnMock.mock.calls.length).toEqual(0); - - // by default, commander handles the error with a process.exit(1) - // along with an error message - expect(processExitSpy.mock.calls.length).toEqual(1); - expect(processExitSpy.mock.calls[0]).toEqual([1]); - - expect(consoleErrorSpy.mock.calls.length).toEqual(1); - expect(consoleErrorSpy.mock.calls[0][0]).toContain("option '--multi-type-number ' argument missing"); - }); - - it('warns on usage of both flag alias and same negated flag, setting it to true', () => { - const res = argParser(basicOptions, ['--no-neg-flag', '-n'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - negFlag: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(1); - expect(warnMock.mock.calls[0][0]).toContain('You provided both -n and --no-neg-flag'); - }); - - it('parses string flag using equals sign', () => { - const res = argParser(basicOptions, ['--string-flag=val'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - stringFlag: 'val', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('handles multiple same args', () => { - const res = argParser(basicOptions, ['--multi-flag', 'a.js', '--multi-flag', 'b.js'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - multiFlag: ['a.js', 'b.js'], - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('handles additional node args from argv', () => { - const res = argParser(basicOptions, ['node', 'index.js', '--bool-flag', '--string-flag', 'val'], false); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - boolFlag: true, - stringFlag: 'val', - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('handles unknown args', () => { - const res = argParser(basicOptions, ['--unknown-arg', '-b', 'no-leading-dashes'], true); - expect(res.unknownArgs).toEqual(['--unknown-arg', 'no-leading-dashes']); - expect(res.opts).toEqual({ - boolFlag: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('handles custom type args', () => { - const res = argParser(basicOptions, ['--custom-type-flag', 'val1,val2,val3'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - customTypeFlag: ['val1', 'val2', 'val3'], - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses webpack args', () => { - const res = argParser(flags, ['--entry', 'test.js', '--hot', '-o', './dist/', '--stats'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts.entry).toEqual(['test.js']); - expect(res.opts.hot).toBeTruthy(); - expect(res.opts.outputPath).toEqual('./dist/'); - expect(res.opts.stats).toEqual(true); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses --neg-flag', () => { - const res = argParser(basicOptions, ['--neg-flag'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - negFlag: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses --no-neg-flag', () => { - const res = argParser(basicOptions, ['--no-neg-flag'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - negFlag: false, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('warns on usage of both --neg and --no-neg flag, setting it to false', () => { - const res = argParser(basicOptions, ['--neg-flag', '--no-neg-flag'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - negFlag: false, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(1); - expect(warnMock.mock.calls[0][0]).toContain('You provided both --neg-flag and --no-neg-flag'); - }); - - it('warns on usage of both flag and same negated flag, setting it to true', () => { - const res = argParser(basicOptions, ['--no-neg-flag', '--neg-flag'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts).toEqual({ - negFlag: true, - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(1); - expect(warnMock.mock.calls[0][0]).toContain('You provided both --neg-flag and --no-neg-flag'); - }); - - it('handles unknown flag', () => { - const res = argParser(basicOptions, ['--unknown-flag'], true); - expect(res.unknownArgs).toEqual(['--unknown-flag']); - expect(res.opts).toEqual({ - stringFlagWithDefault: 'default-value', - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses multiType flag', () => { - const res = argParser(basicOptions, ['--env', 'production', '--env', 'platform=staging'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts.env).toEqual({ - platform: 'staging', - production: true, - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses nested multiType flag', () => { - const res = argParser(basicOptions, ['--env', 'a.b=d', '--env', 'a.b.c=d'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts.env).toEqual({ - a: { - b: { - c: 'd', - }, - }, - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); - - it('parses nested multiType flag with diff keys', () => { - const res = argParser(basicOptions, ['--env', 'a.b=c', '--env', 'd.e.f=g'], true); - expect(res.unknownArgs.length).toEqual(0); - expect(res.opts.env).toEqual({ - a: { - b: 'c', - }, - d: { - e: { - f: 'g', - }, - }, - }); - expect(warnMock.mock.calls.length).toEqual(0); - }); -}); diff --git a/packages/webpack-cli/lib/utils/cli-flags.js b/packages/webpack-cli/lib/utils/cli-flags.js index 49272981835..b24410f2c84 100644 --- a/packages/webpack-cli/lib/utils/cli-flags.js +++ b/packages/webpack-cli/lib/utils/cli-flags.js @@ -47,8 +47,32 @@ const builtInFlags = [ { name: 'env', usage: '--env | --env --env ', - type: String, - multipleType: true, + type: (value, previous = {}) => { + // This ensures we're only splitting by the first `=` + const [allKeys, val] = value.split(/=(.+)/, 2); + const splitKeys = allKeys.split(/\.(?!$)/); + + let prevRef = previous; + + splitKeys.forEach((someKey, index) => { + if (!prevRef[someKey]) { + prevRef[someKey] = {}; + } + + if (typeof prevRef[someKey] === 'string') { + prevRef[someKey] = {}; + } + + if (index === splitKeys.length - 1) { + prevRef[someKey] = val || true; + } + + prevRef = prevRef[someKey]; + }); + + return previous; + }, + multiple: true, description: 'Environment passed to the configuration when it is a function.', }, diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index 67201852276..c3b91fc554e 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -109,96 +109,102 @@ class WebpackCLI { return command; } - // TODO refactor this terrible stuff makeOption(command, option) { - let optionType = option.type; - let isStringOrBool = false; - - if (Array.isArray(optionType)) { - // filter out duplicate types - optionType = optionType.filter((type, index) => { - return optionType.indexOf(type) === index; - }); - - // the only multi type currently supported is String and Boolean, - // if there is a case where a different multi type is needed it - // must be added here - if (optionType.length === 0) { - // if no type is provided in the array fall back to Boolean - optionType = Boolean; - } else if (optionType.length === 1 || optionType.length > 2) { - // treat arrays with 1 or > 2 args as a single type - optionType = optionType[0]; + let type = option.type; + let isMultipleTypes = Array.isArray(type); + let isOptional = false; + + if (isMultipleTypes) { + if (type.length === 1) { + type = type[0]; + isMultipleTypes = false; } else { - // only String and Boolean multi type is supported - if (optionType.includes(Boolean) && optionType.includes(String)) { - isStringOrBool = true; - } else { - optionType = optionType[0]; - } + isOptional = type.includes(Boolean); } } - const flags = option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`; + const isMultiple = option.multiple; + const isRequired = type !== Boolean && typeof type !== 'undefined'; - let flagsWithType = flags; + let flags = option.alias ? `-${option.alias}, --${option.name}` : `--${option.name}`; - if (isStringOrBool) { - // commander recognizes [value] as an optional placeholder, - // making this flag work either as a string or a boolean - flagsWithType = `${flags} [value]`; - } else if (optionType !== Boolean) { + if (isOptional) { + // `commander.js` recognizes [value] as an optional placeholder, making this flag work either as a string or a boolean + flags = `${flags} [value${isMultiple ? '...' : ''}]`; + } else if (isRequired) { // is a required placeholder for any non-Boolean types - flagsWithType = `${flags} `; + flags = `${flags} `; } - if (isStringOrBool || optionType === Boolean || optionType === String) { - if (option.multiple) { - // a multiple argument parsing function - const multiArg = (value, previous = []) => previous.concat([value]); - - command.option(flagsWithType, option.description, multiArg, option.defaultValue); - } else if (option.multipleType) { - // for options which accept multiple types like env - // so you can do `--env platform=staging --env production` - // { platform: "staging", production: true } - const multiArg = (value, previous = {}) => { - // this ensures we're only splitting by the first `=` - const [allKeys, val] = value.split(/=(.+)/, 2); - const splitKeys = allKeys.split(/\.(?!$)/); - - let prevRef = previous; - - splitKeys.forEach((someKey, index) => { - if (!prevRef[someKey]) { - prevRef[someKey] = {}; - } + // TODO need to fix on webpack-dev-server side + // `describe` used by `webpack-dev-server` + const description = option.description || option.describe || ''; + const defaultValue = option.defaultValue; + + if (type === Boolean) { + command.option(flags, description, defaultValue); + } else if (type === Number) { + let skipDefault = true; + + command.option( + flags, + description, + (value, prev = []) => { + if (defaultValue && isMultiple && skipDefault) { + prev = []; + skipDefault = false; + } - if (typeof prevRef[someKey] === 'string') { - prevRef[someKey] = {}; - } + return isMultiple ? [].concat(prev).concat(Number(value)) : Number(value); + }, + defaultValue, + ); + } else if (type === String) { + let skipDefault = true; + + command.option( + flags, + description, + (value, prev = []) => { + if (defaultValue && isMultiple && skipDefault) { + prev = []; + skipDefault = false; + } - if (index === splitKeys.length - 1) { - prevRef[someKey] = val || true; - } + return isMultiple ? [].concat(prev).concat(value) : value; + }, + defaultValue, + ); + } else if (isMultipleTypes) { + let skipDefault = true; + + command.option( + flags, + description, + (value, prev = []) => { + if (defaultValue && isMultiple && skipDefault) { + prev = []; + skipDefault = false; + } - prevRef = prevRef[someKey]; - }); + if (type.includes(Number)) { + const numberValue = Number(value); - return previous; - }; + if (!isNaN(numberValue)) { + return isMultiple ? [].concat(prev).concat(numberValue) : numberValue; + } + } - command.option(flagsWithType, option.description, multiArg, option.defaultValue); - } else { - // Prevent default behavior for standalone options - command.option(flagsWithType, option.description, option.defaultValue); - } - } else if (optionType === Number) { - // this will parse the flag as a number - command.option(flagsWithType, option.description, Number, option.defaultValue); + if (type.includes(String)) { + return isMultiple ? [].concat(prev).concat(value) : value; + } + + return value; + }, + defaultValue, + ); } else { - // in this case the type is a parsing function - command.option(flagsWithType, option.description, optionType, option.defaultValue); + command.option(flags, description, type, defaultValue); } if (option.negative) { diff --git a/test/config/type/function-with-env/function-with-env.test.js b/test/config/type/function-with-env/function-with-env.test.js index d498a5d582a..c3a0275e460 100644 --- a/test/config/type/function-with-env/function-with-env.test.js +++ b/test/config/type/function-with-env/function-with-env.test.js @@ -8,7 +8,7 @@ describe('function configuration', () => { const { exitCode, stderr, stdout } = run(__dirname, ['--env'], false); expect(exitCode).toBe(2); - expect(stderr).toContain(`option '--env ' argument missing`); + expect(stderr).toContain(`option '--env ' argument missing`); expect(stdout).toBeFalsy(); }); From d885426671ead56168e1f008dbfd6fe19fb57edd Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 31 Dec 2020 18:43:33 +0300 Subject: [PATCH 11/15] chore(deps-dev): bump @types/node from 14.14.16 to 14.14.17 (#2279) Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 14.14.16 to 14.14.17. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) Signed-off-by: dependabot-preview[bot] Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c442a38f70b..83f0a538aae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2457,9 +2457,9 @@ integrity sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= "@types/node@*", "@types/node@>= 8", "@types/node@^14.14.6": - version "14.14.16" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b" - integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw== + version "14.14.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.17.tgz#29fab92f3986c0e379968ad3c2043683d8020dbb" + integrity sha512-G0lD1/7qD60TJ/mZmhog76k7NcpLWkPVGgzkRy3CTlnFu4LUQh5v2Wa661z6vnXmD8EQrnALUyf0VRtrACYztw== "@types/normalize-package-data@^2.4.0": version "2.4.0" From d6380bb6c6756d2a00ac20f2ffc454481d97e4d3 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 31 Dec 2020 19:47:43 +0300 Subject: [PATCH 12/15] fix: provide useful error on unknown command --- packages/webpack-cli/lib/webpack-cli.js | 18 +++++++++++++++++- test/unknown/unknown.test.js | 18 ++++++++++++++---- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index c3b91fc554e..2f85a0c85ee 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -273,6 +273,9 @@ class WebpackCLI { }, ]; + const knownCommands = [bundleCommandOptions, versionCommandOptions, helpCommandOptions, ...externalBuiltInCommandsInfo]; + const isKnownCommand = (name) => knownCommands.find((command) => command.name === name || command.alias === name); + const getCommandNameAndOptions = (args) => { let commandName; const options = []; @@ -687,7 +690,20 @@ class WebpackCLI { await outputVersion(optionsForVersion, program); } - await loadCommandByName(commandName, true); + if (isKnownCommand(commandName)) { + await loadCommandByName(commandName, true); + } else { + logger.error(`Unknown command '${commandName}'`); + + const found = knownCommands.find((commandOptions) => distance(commandName, commandOptions.name) < 3); + + if (found) { + logger.error(`Did you mean '${found.name}' (alias '${found.alias}')?`); + } + + logger.error("Run 'webpack --help' to see available commands and options"); + process.exit(2); + } await this.program.parseAsync([commandName, ...options], { from: 'user' }); }); diff --git a/test/unknown/unknown.test.js b/test/unknown/unknown.test.js index bb8ba841b06..78258b22580 100644 --- a/test/unknown/unknown.test.js +++ b/test/unknown/unknown.test.js @@ -184,12 +184,22 @@ describe('unknown behaviour', () => { expect(stdout).toBeFalsy(); }); - it('should ask to install command if an unknown command passed', () => { + it('should log error if an unknown command passed', () => { const { exitCode, stderr, stdout } = run(__dirname, ['qqq'], true, [], { TERM_PROGRAM: false }); - expect(exitCode).toBe(0); - expect(stripAnsi(stderr)).toContain("For using this command you need to install: 'qqq' package"); - expect(stripAnsi(stderr)).toContain("Would you like to install 'qqq' package? (That will run 'npm install -D qqq')"); + expect(exitCode).toBe(2); + expect(stripAnsi(stderr)).toContain("Unknown command 'qqq'"); + expect(stripAnsi(stderr)).toContain("Run 'webpack --help' to see available commands and options"); + expect(stdout).toBeFalsy(); + }); + + it('should log error and provide suggestion if an unknown command passed', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['server'], true, [], { TERM_PROGRAM: false }); + + expect(exitCode).toBe(2); + expect(stripAnsi(stderr)).toContain("Unknown command 'server'"); + expect(stripAnsi(stderr)).toContain("Did you mean 'serve' (alias 's')?"); + expect(stripAnsi(stderr)).toContain("Run 'webpack --help' to see available commands and options"); expect(stdout).toBeFalsy(); }); }); From 29eaa8e843510e020ac4b57a13622df40713fe27 Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 31 Dec 2020 20:22:32 +0300 Subject: [PATCH 13/15] fix: error message on not installed module loaders for configuration (#2282) --- packages/webpack-cli/lib/webpack-cli.js | 18 +++++++++++++++++- test/config-format/failure/failure.test.js | 19 +++++++++++++++++++ .../failure/webpack.config.coffee | 10 ++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 test/config-format/failure/failure.test.js create mode 100644 test/config-format/failure/webpack.config.coffee diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index 2f85a0c85ee..df8649fba1a 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -717,7 +717,23 @@ class WebpackCLI { const interpreted = Object.keys(jsVariants).find((variant) => variant === ext); if (interpreted) { - rechoir.prepare(extensions, configPath); + try { + rechoir.prepare(extensions, configPath); + } catch (error) { + if (error.failures) { + logger.error(`Unable load '${configPath}'`); + logger.error(error.message); + + error.failures.forEach((failure) => { + logger.error(failure.error.message); + }); + logger.error('Please install one of them'); + process.exit(2); + } + + logger.error(error); + process.exit(2); + } } const { pathToFileURL } = require('url'); diff --git a/test/config-format/failure/failure.test.js b/test/config-format/failure/failure.test.js new file mode 100644 index 00000000000..125c94e31bf --- /dev/null +++ b/test/config-format/failure/failure.test.js @@ -0,0 +1,19 @@ +const path = require('path'); + +const { run } = require('../../utils/test-utils'); + +describe('webpack cli', () => { + it('should support mjs config format', () => { + const { exitCode, stderr, stdout } = run(__dirname, ['-c', 'webpack.config.coffee']); + + expect(exitCode).toBe(2); + expect(stderr).toContain(`Unable load '${path.resolve(__dirname, './webpack.config.coffee')}'`); + expect(stderr).toContain('Unable to use specified module loaders for ".coffee".'); + expect(stderr).toContain("Cannot find module 'coffeescript/register'"); + expect(stderr).toContain("Cannot find module 'coffee-script/register'"); + expect(stderr).toContain("Cannot find module 'coffeescript'"); + expect(stderr).toContain("Cannot find module 'coffee-script'"); + expect(stderr).toContain('Please install one of them'); + expect(stdout).toBeFalsy(); + }); +}); diff --git a/test/config-format/failure/webpack.config.coffee b/test/config-format/failure/webpack.config.coffee new file mode 100644 index 00000000000..15e1934891b --- /dev/null +++ b/test/config-format/failure/webpack.config.coffee @@ -0,0 +1,10 @@ +path = require 'path' + +config = + mode: 'production' + entry: './main.js' + output: + path: path.resolve(__dirname, 'dist') + filename: 'foo.bundle.js' + +module.exports = config; From 083f2a069d6dc0a3b9492eb3f205474ba843acfd Mon Sep 17 00:00:00 2001 From: Alexander Akait <4567934+alexander-akait@users.noreply.github.com> Date: Thu, 31 Dec 2020 20:27:03 +0300 Subject: [PATCH 14/15] fix: peer dependencies (#2284) --- packages/webpack-cli/package.json | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index b0efd6392e1..66515dec846 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -45,13 +45,10 @@ "webpack": "4.x.x || 5.x.x" }, "peerDependenciesMeta": { - "@webpack-cli/init": { - "optional": true - }, - "@webpack-cli/generate-loader": { + "@webpack-cli/generators": { "optional": true }, - "@webpack-cli/generate-plugin": { + "@webpack-cli/init": { "optional": true }, "@webpack-cli/migrate": { From a5171999177925529a1c54f242afc2bd9c9e4fe3 Mon Sep 17 00:00:00 2001 From: evilebottnawi Date: Thu, 31 Dec 2020 20:27:47 +0300 Subject: [PATCH 15/15] chore(release): publish new version - @webpack-cli/generators@1.2.1 - @webpack-cli/info@1.2.1 - @webpack-cli/init@1.1.1 - @webpack-cli/migrate@1.1.2 - @webpack-cli/serve@1.2.1 - @webpack-cli/utils@1.2.1 - webpack-cli@4.3.1 --- packages/generators/CHANGELOG.md | 6 ++++++ packages/generators/package.json | 4 ++-- packages/info/CHANGELOG.md | 6 ++++++ packages/info/package.json | 2 +- packages/init/CHANGELOG.md | 6 ++++++ packages/init/package.json | 6 +++--- packages/migrate/CHANGELOG.md | 6 ++++++ packages/migrate/package.json | 4 ++-- packages/serve/CHANGELOG.md | 9 +++++++++ packages/serve/package.json | 2 +- packages/utils/CHANGELOG.md | 4 ++++ packages/utils/package.json | 2 +- packages/webpack-cli/CHANGELOG.md | 10 ++++++++++ packages/webpack-cli/package.json | 6 +++--- 14 files changed, 60 insertions(+), 13 deletions(-) diff --git a/packages/generators/CHANGELOG.md b/packages/generators/CHANGELOG.md index 34b698e7486..d0ad83af03e 100644 --- a/packages/generators/CHANGELOG.md +++ b/packages/generators/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.2.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/generators@1.2.0...@webpack-cli/generators@1.2.1) (2020-12-31) + +### Bug Fixes + +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) + # [1.2.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/generators@1.1.0...@webpack-cli/generators@1.2.0) (2020-12-25) ### Bug Fixes diff --git a/packages/generators/package.json b/packages/generators/package.json index 95463bc867c..c8fd91dd020 100644 --- a/packages/generators/package.json +++ b/packages/generators/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/generators", - "version": "1.2.0", + "version": "1.2.1", "description": "Webpack-CLI generators", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -14,7 +14,7 @@ "templates" ], "dependencies": { - "@webpack-cli/utils": "^1.2.0", + "@webpack-cli/utils": "^1.2.1", "colorette": "^1.2.1", "log-symbols": "^4.0.0", "yeoman-environment": "^2.10.3", diff --git a/packages/info/CHANGELOG.md b/packages/info/CHANGELOG.md index ba92309543f..32c830bbc58 100644 --- a/packages/info/CHANGELOG.md +++ b/packages/info/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.2.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/info@1.2.0...@webpack-cli/info@1.2.1) (2020-12-31) + +### Bug Fixes + +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) + # [1.2.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/info@1.1.0...@webpack-cli/info@1.2.0) (2020-12-25) ### Features diff --git a/packages/info/package.json b/packages/info/package.json index 44a6eb34749..dbb837959a5 100644 --- a/packages/info/package.json +++ b/packages/info/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/info", - "version": "1.2.0", + "version": "1.2.1", "description": "Outputs info about system and webpack config", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/init/CHANGELOG.md b/packages/init/CHANGELOG.md index 97e89695a3f..93e6f1b35a2 100644 --- a/packages/init/CHANGELOG.md +++ b/packages/init/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/init@1.1.0...@webpack-cli/init@1.1.1) (2020-12-31) + +### Bug Fixes + +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) + # [1.1.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/init@1.0.3...@webpack-cli/init@1.1.0) (2020-12-25) ### Features diff --git a/packages/init/package.json b/packages/init/package.json index 8cd6773dd3d..0898105ef64 100644 --- a/packages/init/package.json +++ b/packages/init/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/init", - "version": "1.1.0", + "version": "1.1.1", "description": "init command for webpack-cli", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -12,8 +12,8 @@ "lib" ], "dependencies": { - "@webpack-cli/generators": "^1.2.0", - "@webpack-cli/utils": "^1.2.0" + "@webpack-cli/generators": "^1.2.1", + "@webpack-cli/utils": "^1.2.1" }, "peerDependencies": { "webpack": "4.x.x || 5.x.x", diff --git a/packages/migrate/CHANGELOG.md b/packages/migrate/CHANGELOG.md index cd4609fd11f..7415e2633a7 100644 --- a/packages/migrate/CHANGELOG.md +++ b/packages/migrate/CHANGELOG.md @@ -3,6 +3,12 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.1.2](https://github.com/webpack/webpack-cli/compare/@webpack-cli/migrate@1.1.1...@webpack-cli/migrate@1.1.2) (2020-12-31) + +### Bug Fixes + +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) + ## [1.1.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/migrate@1.1.0...@webpack-cli/migrate@1.1.1) (2020-12-25) **Note:** Version bump only for package @webpack-cli/migrate diff --git a/packages/migrate/package.json b/packages/migrate/package.json index b5d55622eda..e4e74bc21ac 100644 --- a/packages/migrate/package.json +++ b/packages/migrate/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/migrate", - "version": "1.1.1", + "version": "1.1.2", "description": "Migrate command for webpack-cli", "main": "lib/index.js", "types": "lib/index.d.ts", @@ -12,7 +12,7 @@ "lib" ], "dependencies": { - "@webpack-cli/utils": "^1.2.0", + "@webpack-cli/utils": "^1.2.1", "colorette": "^1.2.1", "diff": "^4.0.2", "inquirer": "^7.3.3", diff --git a/packages/serve/CHANGELOG.md b/packages/serve/CHANGELOG.md index 6d717309c1d..0e8cb3a23b2 100644 --- a/packages/serve/CHANGELOG.md +++ b/packages/serve/CHANGELOG.md @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.2.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/serve@1.2.0...@webpack-cli/serve@1.2.1) (2020-12-31) + +### Bug Fixes + +- do not apply HotModuleReplacement plugin twice ([#2269](https://github.com/webpack/webpack-cli/issues/2269)) ([bb16d44](https://github.com/webpack/webpack-cli/commit/bb16d4481414a5f3c0cbeb18af690084b2ae4215)) +- respect the `output.publicPath` option for the `serve`command ([#2271](https://github.com/webpack/webpack-cli/issues/2271)) ([a3092ef](https://github.com/webpack/webpack-cli/commit/a3092ef2b51ece30221f7dd7b30a686626c1fd7a)) +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) +- the `--progress` option with the `serve` command ([#2265](https://github.com/webpack/webpack-cli/issues/2265)) ([952a188](https://github.com/webpack/webpack-cli/commit/952a1883b1a18c4fb38e8eb7bbbdb2aefc7942f4)) + # [1.2.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/serve@1.1.0...@webpack-cli/serve@1.2.0) (2020-12-25) ### Bug Fixes diff --git a/packages/serve/package.json b/packages/serve/package.json index 8494acaeafc..4099c82ec35 100644 --- a/packages/serve/package.json +++ b/packages/serve/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/serve", - "version": "1.2.0", + "version": "1.2.1", "description": "", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/utils/CHANGELOG.md b/packages/utils/CHANGELOG.md index 8772374651c..caca2302e41 100644 --- a/packages/utils/CHANGELOG.md +++ b/packages/utils/CHANGELOG.md @@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [1.2.1](https://github.com/webpack/webpack-cli/compare/@webpack-cli/utils@1.2.0...@webpack-cli/utils@1.2.1) (2020-12-31) + +**Note:** Version bump only for package @webpack-cli/utils + # [1.2.0](https://github.com/webpack/webpack-cli/compare/@webpack-cli/utils@1.1.0...@webpack-cli/utils@1.2.0) (2020-12-25) ### Bug Fixes diff --git a/packages/utils/package.json b/packages/utils/package.json index 2f763950450..8f96970b4d4 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -1,6 +1,6 @@ { "name": "@webpack-cli/utils", - "version": "1.2.0", + "version": "1.2.1", "description": "webpack-cli utility files", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/webpack-cli/CHANGELOG.md b/packages/webpack-cli/CHANGELOG.md index aca420758ac..609b60d1d6e 100644 --- a/packages/webpack-cli/CHANGELOG.md +++ b/packages/webpack-cli/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## [4.3.1](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.3.0...webpack-cli@4.3.1) (2020-12-31) + +### Bug Fixes + +- error message on not installed module loaders for configuration ([#2282](https://github.com/webpack/webpack-cli/issues/2282)) ([29eaa8e](https://github.com/webpack/webpack-cli/commit/29eaa8e843510e020ac4b57a13622df40713fe27)) +- peer dependencies ([#2284](https://github.com/webpack/webpack-cli/issues/2284)) ([083f2a0](https://github.com/webpack/webpack-cli/commit/083f2a069d6dc0a3b9492eb3f205474ba843acfd)) +- provide useful error on unknown command ([d6380bb](https://github.com/webpack/webpack-cli/commit/d6380bb6c6756d2a00ac20f2ffc454481d97e4d3)) +- the `--help` option is working without `webpack-dev-server` ([#2267](https://github.com/webpack/webpack-cli/issues/2267)) ([1dae54d](https://github.com/webpack/webpack-cli/commit/1dae54da94d3220437b9257efe512447023de1d3)) +- the `--progress` option is working with `--json` ([#2276](https://github.com/webpack/webpack-cli/issues/2276)) ([0595603](https://github.com/webpack/webpack-cli/commit/05956030cbb1491a2e9313732470bcd4ebe5a36d)) + # [4.3.0](https://github.com/webpack/webpack-cli/compare/webpack-cli@4.2.0...webpack-cli@4.3.0) (2020-12-25) ### Bug Fixes diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index 66515dec846..d03d1ab2d84 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -1,6 +1,6 @@ { "name": "webpack-cli", - "version": "4.3.0", + "version": "4.3.1", "description": "CLI for webpack & friends", "license": "MIT", "repository": { @@ -28,8 +28,8 @@ ], "dependencies": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/info": "^1.2.0", - "@webpack-cli/serve": "^1.2.0", + "@webpack-cli/info": "^1.2.1", + "@webpack-cli/serve": "^1.2.1", "colorette": "^1.2.1", "commander": "^6.2.0", "enquirer": "^2.3.6",