From 51d27c6b3e62b788a5abffccfb86f860f22cb941 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Mon, 17 Aug 2020 17:02:59 +0530 Subject: [PATCH 1/8] refactor: remove --mode flag validation (#1744) --- .../__tests__/ZeroConfigGroup.test.js | 8 --- .../webpack-cli/lib/groups/ZeroConfigGroup.js | 7 -- .../mode-single-arg/mode-single-arg.test.js | 64 +++---------------- test/mode/mode-single-arg/src/index.js | 5 -- test/mode/mode-single-arg/src/main.js | 1 + 5 files changed, 10 insertions(+), 75 deletions(-) delete mode 100644 test/mode/mode-single-arg/src/index.js create mode 100644 test/mode/mode-single-arg/src/main.js diff --git a/packages/webpack-cli/__tests__/ZeroConfigGroup.test.js b/packages/webpack-cli/__tests__/ZeroConfigGroup.test.js index 3153f5c1caa..341ece87484 100644 --- a/packages/webpack-cli/__tests__/ZeroConfigGroup.test.js +++ b/packages/webpack-cli/__tests__/ZeroConfigGroup.test.js @@ -38,12 +38,4 @@ describe('ZeroConfigGroup', function () { expect(result.options).toMatchObject({ mode: 'none' }); expect(result.options.mode).toEqual('none'); }); - - it('should set mode=production by default', () => { - const group = new ZeroConfigGroup([{}]); - - const result = group.run(); - // ensure no other properties are added - expect(result.options).toMatchObject({ mode: 'production' }); - }); }); diff --git a/packages/webpack-cli/lib/groups/ZeroConfigGroup.js b/packages/webpack-cli/lib/groups/ZeroConfigGroup.js index 2c82a140949..1bdbdda74c1 100644 --- a/packages/webpack-cli/lib/groups/ZeroConfigGroup.js +++ b/packages/webpack-cli/lib/groups/ZeroConfigGroup.js @@ -1,9 +1,7 @@ const GroupHelper = require('../utils/GroupHelper'); -const logger = require('../utils/logger'); const PRODUCTION = 'production'; const DEVELOPMENT = 'development'; -const NONE = 'none'; /** * ZeroConfigGroup creates a zero configuration based on the environment */ @@ -21,13 +19,8 @@ class ZeroConfigGroup extends GroupHelper { return process.env.NODE_ENV; } else { if (this.args.mode) { - if (this.args.mode !== PRODUCTION && this.args.mode !== DEVELOPMENT && this.args.mode !== NONE) { - logger.warn('You provided an invalid value for "mode" option. Using "production" by default'); - return PRODUCTION; - } return this.args.mode; } - return PRODUCTION; } } diff --git a/test/mode/mode-single-arg/mode-single-arg.test.js b/test/mode/mode-single-arg/mode-single-arg.test.js index 1976447de82..d995308080a 100644 --- a/test/mode/mode-single-arg/mode-single-arg.test.js +++ b/test/mode/mode-single-arg/mode-single-arg.test.js @@ -1,80 +1,34 @@ 'use strict'; const { run } = require('../../utils/test-utils'); -const { stat, readFile } = require('fs'); -const { resolve } = require('path'); + describe('mode flags', () => { - it('should set mode=production by default', (done) => { + it('should set mode=production by default', () => { const { stderr, stdout } = run(__dirname); expect(stderr).toBeFalsy(); expect(stdout).toContain(`mode: 'production'`); - - stat(resolve(__dirname, './bin/main.js'), (err, stats) => { - expect(err).toBe(null); - expect(stats.isFile()).toBe(true); - done(); - }); - readFile(resolve(__dirname, './bin/main.js'), 'utf-8', (err, data) => { - expect(err).toBe(null); - expect(data).toContain('production test'); - done(); - }); }); - it('should load a development config when --mode=development is passed', (done) => { + it('should load a development config when --mode=development is passed', () => { const { stderr, stdout } = run(__dirname, ['--mode', 'development']); expect(stderr).toBeFalsy(); expect(stdout).toContain(`mode: 'development'`); - - stat(resolve(__dirname, './bin/main.js'), (err, stats) => { - expect(err).toBe(null); - expect(stats.isFile()).toBe(true); - done(); - }); }); - it('should load a production config when --mode=production is passed', (done) => { + it('should load a production config when --mode=production is passed', () => { const { stderr, stdout } = run(__dirname, ['--mode', 'production']); expect(stderr).toBeFalsy(); expect(stdout).toContain(`mode: 'production'`); - - stat(resolve(__dirname, './bin/main.js'), (err, stats) => { - expect(err).toBe(null); - expect(stats.isFile()).toBe(true); - done(); - }); - readFile(resolve(__dirname, './bin/main.js'), 'utf-8', (err, data) => { - expect(err).toBe(null); - expect(data).toContain('production test'); - done(); - }); }); - it('should load a none config when --mode=none is passed', (done) => { + it('should load a none config when --mode=none is passed', () => { const { stderr, stdout } = run(__dirname, ['--mode', 'none']); expect(stderr).toBeFalsy(); expect(stdout).toContain(`mode: 'none'`); - - stat(resolve(__dirname, './bin/main.js'), (err, stats) => { - expect(err).toBe(null); - expect(stats.isFile()).toBe(true); - done(); - }); }); - it('should show warning and load a production config when --mode=abcd is passed', (done) => { - const { stderr, stdout } = run(__dirname, ['--mode', 'abcd']); - expect(stderr).toContain('invalid value for "mode" option. Using "production" by default'); - expect(stdout).toContain(`mode: 'production'`); - - stat(resolve(__dirname, './bin/main.js'), (err, stats) => { - expect(err).toBe(null); - expect(stats.isFile()).toBe(true); - done(); - }); - readFile(resolve(__dirname, './bin/main.js'), 'utf-8', (err, data) => { - expect(err).toBe(null); - expect(data).toContain('production test'); - done(); - }); + it('should throw error when --mode=abcd is passed', () => { + const { stderr } = run(__dirname, ['--mode', 'abcd']); + expect(stderr).toContain('configuration.mode should be one of these'); + expect(stderr).toContain(`"development" | "production" | "none"`); }); }); diff --git a/test/mode/mode-single-arg/src/index.js b/test/mode/mode-single-arg/src/index.js deleted file mode 100644 index 45be1ee06f0..00000000000 --- a/test/mode/mode-single-arg/src/index.js +++ /dev/null @@ -1,5 +0,0 @@ -console.log('test'); - -if (process.env.NODE_ENV === "production") { - console.log('production test'); -} \ No newline at end of file diff --git a/test/mode/mode-single-arg/src/main.js b/test/mode/mode-single-arg/src/main.js new file mode 100644 index 00000000000..a2248efc6c9 --- /dev/null +++ b/test/mode/mode-single-arg/src/main.js @@ -0,0 +1 @@ +console.log('test'); From a7c931494b4cec22d27c6e5bbb47398f963b730f Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 17 Aug 2020 14:39:55 +0300 Subject: [PATCH 2/8] chore(deps-dev): bump @commitlint/config-lerna-scopes (#1750) Bumps [@commitlint/config-lerna-scopes](https://github.com/conventional-changelog/commitlint) from 9.1.1 to 9.1.2. - [Release notes](https://github.com/conventional-changelog/commitlint/releases) - [Changelog](https://github.com/conventional-changelog/commitlint/blob/master/CHANGELOG.md) - [Commits](https://github.com/conventional-changelog/commitlint/compare/v9.1.1...v9.1.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 e2f6b730188..9ed773bcd7d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1045,9 +1045,9 @@ yargs "^15.1.0" "@commitlint/config-lerna-scopes@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@commitlint/config-lerna-scopes/-/config-lerna-scopes-9.1.1.tgz#9598023dfff3bc268768476093b02e4293bcf1de" - integrity sha512-UZgbpRhnjE4W/RKkV7x822MvHY4iKH44M542GdgO5NJdojg95BpKV3AcGcV+gtxVeUpPW+pzrjMLh1eIfmUyhA== + version "9.1.2" + resolved "https://registry.yarnpkg.com/@commitlint/config-lerna-scopes/-/config-lerna-scopes-9.1.2.tgz#ff4d5248692573e6d1c8b8b499f0c660ab987235" + integrity sha512-pETpJUoXQZ65uZ9TLz1hOACoicV0dradr5vDgBAoZ48ZkYszQ38jSE4flTQeOFmYCqL91nUxFT0jKpxLMobpEQ== dependencies: import-from "3.0.0" resolve-pkg "2.0.0" From 1c52f79319ab2d30fb06371fe4dae3fac35be257 Mon Sep 17 00:00:00 2001 From: Anshuman Verma Date: Mon, 17 Aug 2020 17:10:38 +0530 Subject: [PATCH 3/8] tests: add tests for coffeescript config (#1749) --- test/config-format/coffee/coffee.test.js | 20 +++++++++++++++++++ test/config-format/coffee/main.js | 1 + test/config-format/coffee/package.json | 5 +++++ .../coffee/webpack.config.coffee | 10 ++++++++++ 4 files changed, 36 insertions(+) create mode 100644 test/config-format/coffee/coffee.test.js create mode 100644 test/config-format/coffee/main.js create mode 100644 test/config-format/coffee/package.json create mode 100644 test/config-format/coffee/webpack.config.coffee diff --git a/test/config-format/coffee/coffee.test.js b/test/config-format/coffee/coffee.test.js new file mode 100644 index 00000000000..0a42e309352 --- /dev/null +++ b/test/config-format/coffee/coffee.test.js @@ -0,0 +1,20 @@ +// eslint-disable-next-line node/no-unpublished-require +const { run } = require('../../utils/test-utils'); +const { existsSync } = require('fs'); +const { resolve } = require('path'); + +describe('webpack cli', () => { + it('should support coffeescript file as flag', () => { + const { stderr, stdout } = run(__dirname, ['-c', 'webpack.config.coffee'], false); + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + expect(existsSync(resolve(__dirname, 'dist/foo.bundle.js'))).toBeTruthy(); + }); + + it('should load coffeescript file by default', () => { + const { stderr, stdout } = run(__dirname, [], false); + expect(stderr).toBeFalsy(); + expect(stdout).toBeTruthy(); + expect(existsSync(resolve(__dirname, 'dist/foo.bundle.js'))).toBeTruthy(); + }); +}); diff --git a/test/config-format/coffee/main.js b/test/config-format/coffee/main.js new file mode 100644 index 00000000000..5fb227c6c10 --- /dev/null +++ b/test/config-format/coffee/main.js @@ -0,0 +1 @@ +console.log('Sakusa Kiyoomi'); diff --git a/test/config-format/coffee/package.json b/test/config-format/coffee/package.json new file mode 100644 index 00000000000..79463144cb1 --- /dev/null +++ b/test/config-format/coffee/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "coffeescript": "^2.5.1" + } +} diff --git a/test/config-format/coffee/webpack.config.coffee b/test/config-format/coffee/webpack.config.coffee new file mode 100644 index 00000000000..15e1934891b --- /dev/null +++ b/test/config-format/coffee/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 e06392ae531d111dab9603c785001338740f42ab Mon Sep 17 00:00:00 2001 From: Anshuman Verma Date: Mon, 17 Aug 2020 17:20:37 +0530 Subject: [PATCH 4/8] perf: do not spawn new process for running webpack (#1741) --- packages/webpack-cli/README.md | 1 - .../__tests__/cli-executer.test.js | 2 +- packages/webpack-cli/bin/cli.js | 6 +- packages/webpack-cli/lib/bootstrap.js | 31 ++++---- .../webpack-cli/lib/utils/cli-executer.js | 4 +- packages/webpack-cli/lib/utils/cli-flags.js | 8 --- .../webpack-cli/lib/utils/parse-node-args.js | 27 ------- packages/webpack-cli/lib/webpack-cli.js | 4 +- test/node/node.test.js | 70 +++++-------------- test/utils/test-utils.js | 10 +-- 10 files changed, 46 insertions(+), 117 deletions(-) delete mode 100644 packages/webpack-cli/lib/utils/parse-node-args.js diff --git a/packages/webpack-cli/README.md b/packages/webpack-cli/README.md index 673d751e578..4751e251fec 100644 --- a/packages/webpack-cli/README.md +++ b/packages/webpack-cli/README.md @@ -54,7 +54,6 @@ yarn add webpack-cli --dev -j, --json Prints result as JSON --mode string Defines the mode to pass to webpack -v, --version Get current version - --node-args string[] NodeJS flags --stats string It instructs webpack on how to treat the stats --no-stats Disables stats output ``` diff --git a/packages/webpack-cli/__tests__/cli-executer.test.js b/packages/webpack-cli/__tests__/cli-executer.test.js index fdd12f9de71..a67b63bc293 100644 --- a/packages/webpack-cli/__tests__/cli-executer.test.js +++ b/packages/webpack-cli/__tests__/cli-executer.test.js @@ -47,7 +47,7 @@ describe('CLI Executer', () => { it('runs enquirer options then runs webpack', async () => { await cliExecuter(); - // ensure that the webpack runner is called + // ensure that the webpack runCLI is called expect(runner.mock.calls.length).toEqual(1); expect(runner.mock.calls[0]).toEqual([[], ['--config', 'test1', '--entry', 'test2', '--progress']]); diff --git a/packages/webpack-cli/bin/cli.js b/packages/webpack-cli/bin/cli.js index 34faad08445..960fc1fad39 100755 --- a/packages/webpack-cli/bin/cli.js +++ b/packages/webpack-cli/bin/cli.js @@ -3,8 +3,7 @@ 'use strict'; require('v8-compile-cache'); const importLocal = require('import-local'); -const parseNodeArgs = require('../lib/utils/parse-node-args'); -const runner = require('../lib/runner'); +const runCLI = require('../lib/bootstrap'); // Prefer the local installation of webpack-cli if (importLocal(__filename)) { @@ -13,6 +12,5 @@ if (importLocal(__filename)) { process.title = 'webpack'; const [, , ...rawArgs] = process.argv; -const { cliArgs, nodeArgs } = parseNodeArgs(rawArgs); -runner(nodeArgs, cliArgs); +runCLI(rawArgs); diff --git a/packages/webpack-cli/lib/bootstrap.js b/packages/webpack-cli/lib/bootstrap.js index b7bbc6ac2d4..b95fb54c629 100644 --- a/packages/webpack-cli/lib/bootstrap.js +++ b/packages/webpack-cli/lib/bootstrap.js @@ -7,20 +7,25 @@ const argParser = require('./utils/arg-parser'); require('./utils/process-log'); process.title = 'webpack-cli'; -const isCommandUsed = (commands) => +// Create a new instance of the CLI object +const cli = new WebpackCLI(); + +const isCommandUsed = (args) => commands.find((cmd) => { - return process.argv.includes(cmd.name) || process.argv.includes(cmd.alias); + return args.includes(cmd.name) || args.includes(cmd.alias); }); -async function runCLI(cli, commandIsUsed) { +async function runCLI(cliArgs) { let args; + + const commandIsUsed = isCommandUsed(cliArgs); const runVersion = () => { - cli.runVersion(process.argv, commandIsUsed); + cli.runVersion(cliArgs, commandIsUsed); }; - const parsedArgs = argParser(core, process.argv, false, process.title, cli.runHelp, runVersion, commands); + const parsedArgs = argParser(core, cliArgs, true, process.title, cli.runHelp, runVersion, commands); if (parsedArgs.unknownArgs.includes('help')) { - cli.runHelp(process.argv); + cli.runHelp(cliArgs); process.exit(0); } @@ -55,7 +60,7 @@ async function runCLI(cli, commandIsUsed) { parsedArgs.unknownArgs.forEach((unknown) => { logger.warn(`Unknown argument: ${unknown}`); }); - cliExecuter(); + await cliExecuter(); return; } const parsedArgsOpts = parsedArgs.opts; @@ -77,10 +82,10 @@ async function runCLI(cli, commandIsUsed) { } else if (err.name === 'ALREADY_SET') { const argsMap = {}; const keysToDelete = []; - process.argv.forEach((arg, idx) => { + cliArgs.forEach((arg, idx) => { const oldMapValue = argsMap[arg]; argsMap[arg] = { - value: process.argv[idx], + value: cliArgs[idx], pos: idx, }; // Swap idx of overridden value @@ -92,8 +97,8 @@ async function runCLI(cli, commandIsUsed) { // Filter out the value for the overridden key const newArgKeys = Object.keys(argsMap).filter((arg) => !keysToDelete.includes(argsMap[arg].pos)); // eslint-disable-next-line require-atomic-updates - process.argv = newArgKeys; - args = argParser('', core, process.argv); + cliArgs = newArgKeys; + args = argParser('', core, cliArgs); await cli.run(args.opts, core); process.stdout.write('\n'); logger.warn('Duplicate flags found, defaulting to last set value'); @@ -104,6 +109,4 @@ async function runCLI(cli, commandIsUsed) { } } -const commandIsUsed = isCommandUsed(commands); -const cli = new WebpackCLI(); -runCLI(cli, commandIsUsed); +module.exports = runCLI; diff --git a/packages/webpack-cli/lib/utils/cli-executer.js b/packages/webpack-cli/lib/utils/cli-executer.js index bddb21d8cdb..fbb2ef6895c 100644 --- a/packages/webpack-cli/lib/utils/cli-executer.js +++ b/packages/webpack-cli/lib/utils/cli-executer.js @@ -1,8 +1,8 @@ const { MultiSelect, Input } = require('enquirer'); const { cyan } = require('colorette'); -const runner = require('../runner'); const logger = require('./logger'); const cliArgs = require('./cli-flags').core; +const runner = require('../runner'); async function prompter() { const args = []; @@ -56,7 +56,7 @@ async function run() { const args = await prompter(); process.stdout.write('\n'); logger.info('Executing CLI\n'); - runner([], args); + await runner([], args); } catch (err) { logger.error(`Action Interrupted, use ${cyan('webpack-cli help')} to see possible options.`); } diff --git a/packages/webpack-cli/lib/utils/cli-flags.js b/packages/webpack-cli/lib/utils/cli-flags.js index 7477a0b5da7..b11a1debb08 100644 --- a/packages/webpack-cli/lib/utils/cli-flags.js +++ b/packages/webpack-cli/lib/utils/cli-flags.js @@ -236,14 +236,6 @@ module.exports = { group: HELP_GROUP, description: 'Get current version', }, - { - name: 'node-args', - usage: '--node-args "--max-old-space-size=1024"', - type: String, - multiple: true, - group: BASIC_GROUP, - description: 'NodeJS flags', - }, { name: 'stats', usage: '--stats ', diff --git a/packages/webpack-cli/lib/utils/parse-node-args.js b/packages/webpack-cli/lib/utils/parse-node-args.js deleted file mode 100644 index 9ea49b8d6a9..00000000000 --- a/packages/webpack-cli/lib/utils/parse-node-args.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Parse cli args and split these to args for node js and the rest - * - * @param {string[]} rawArgs raw cli args - * @returns {{cliArgs: string[], nodeArgs: string[]}} cli and nodejs args - */ -module.exports = (rawArgs) => { - const cliArgs = []; - const nodeArgs = []; - let isNodeArg = false; - - for (const value of rawArgs) { - if (value === '--node-args') { - isNodeArg = true; - } else if (value.startsWith('--node-args=')) { - const [, argValue] = value.match(/^--node-args="?(.+?)"?$/); - nodeArgs.push(argValue); - } else if (isNodeArg) { - isNodeArg = false; - nodeArgs.push(...value.split(' ')); - } else { - cliArgs.push(value); - } - } - - return { cliArgs, nodeArgs }; -}; diff --git a/packages/webpack-cli/lib/webpack-cli.js b/packages/webpack-cli/lib/webpack-cli.js index aab0e10f6ab..a6f6909acf9 100644 --- a/packages/webpack-cli/lib/webpack-cli.js +++ b/packages/webpack-cli/lib/webpack-cli.js @@ -281,7 +281,7 @@ class WebpackCLI extends GroupHelper { const subject = allNames.filter((name) => { return args.includes(name); })[0]; - const invalidArgs = hasUnknownArgs(args.slice(2), ...allNames); + const invalidArgs = hasUnknownArgs(args, ...allNames); const isCommand = commands.includes(subject); options.enabled = !args.includes('--no-color'); return new HelpGroup().outputHelp(isCommand, subject, invalidArgs); @@ -291,7 +291,7 @@ class WebpackCLI extends GroupHelper { const HelpGroup = require('./groups/HelpGroup'); const { commands, allNames, hasUnknownArgs } = require('./utils/unknown-args'); const commandsUsed = args.filter((val) => commands.includes(val)); - const invalidArgs = hasUnknownArgs(args.slice(2), ...allNames); + const invalidArgs = hasUnknownArgs(args, ...allNames); options.enabled = !args.includes('--no-color'); return new HelpGroup().outputVersion(externalPkg, commandsUsed, invalidArgs); } diff --git a/test/node/node.test.js b/test/node/node.test.js index a449f4eee15..63114299a8e 100644 --- a/test/node/node.test.js +++ b/test/node/node.test.js @@ -2,57 +2,19 @@ const { stat } = require('fs'); const { resolve } = require('path'); const { run } = require('../utils/test-utils'); -const parseNodeArgs = require('../../packages/webpack-cli/lib/utils/parse-node-args'); +// TODO - We pass node args to `nodeOptions` in execa, +// passing via NODE_OPTIONS= in env in execa +// throws different error from what we manually see describe('node flags', () => { - it('parseNodeArgs helper must work correctly', () => { - [ - { - rawArgs: ['--foo', '--bar', '--baz=quux'], - expectedCliArgs: ['--foo', '--bar', '--baz=quux'], - expectedNodeArgs: [], - }, - { - rawArgs: ['--foo', '--bar', '--baz=quux', '--node-args', '--name1=value1', '--node-args', '--name2 value2'], - expectedCliArgs: ['--foo', '--bar', '--baz=quux'], - expectedNodeArgs: ['--name1=value1', '--name2', 'value2'], - }, - { - rawArgs: [ - '--node-args', - '--name1=value1', - '--node-args', - '--name2="value2"', - '--node-args', - '--name3 value3', - '--node-args', - '-k v', - ], - expectedCliArgs: [], - expectedNodeArgs: ['--name1=value1', '--name2="value2"', '--name3', 'value3', '-k', 'v'], - }, - ].map(({ rawArgs, expectedNodeArgs, expectedCliArgs }) => { - const { nodeArgs, cliArgs } = parseNodeArgs(rawArgs); - expect(nodeArgs).toEqual(expectedNodeArgs); - expect(cliArgs).toEqual(expectedCliArgs); - }); - }); - - it('is able to pass the options flags to node js', (done) => { - const { stdout } = run( - __dirname, - [ - '--node-args', - `--require=${resolve(__dirname, 'bootstrap.js')}`, - '--node-args', - `-r ${resolve(__dirname, 'bootstrap2.js')}`, - '--output', - './bin/[name].bundle.js', - ], - false, - ); + it('is able to pass the options flags to node js', async (done) => { + const { stdout, stderr } = await run(__dirname, ['--output', './bin/[name].bundle.js'], false, [ + `--require=${resolve(__dirname, 'bootstrap.js')}`, + `--require=${resolve(__dirname, 'bootstrap2.js')}`, + ]); expect(stdout).toContain('---from bootstrap.js---'); expect(stdout).toContain('---from bootstrap2.js---'); + expect(stderr).toBeFalsy(); stat(resolve(__dirname, './bin/main.bundle.js'), (err, stats) => { expect(err).toBe(null); expect(stats.isFile()).toBe(true); @@ -60,20 +22,20 @@ describe('node flags', () => { }); }); - it('throws an error on supplying unknown flags', () => { - const { stderr } = run(__dirname, ['--node-args', '--unknown']); + it('throws an error on supplying unknown flags', async () => { + const { stderr } = await run(__dirname, [], false, ['--unknown']); expect(stderr).toContain('bad option'); }); - it('throws an error if no values were supplied with --max-old-space-size', () => { - const { stderr, stdout } = run(__dirname, ['--node-args', '--max-old-space-size']); + it('throws an error if no values were supplied with --max-old-space-size', async () => { + const { stderr, stdout } = await run(__dirname, [], false, ['--max-old-space-size']); expect(stderr).toContain('missing value for flag --max-old-space-size'); expect(stdout).toBeFalsy(); }); - it('throws an error if an illegal value was supplied with --max-old-space-size', () => { - const { stderr, stdout } = run(__dirname, ['--node-args', '--max-old-space-size=1024a']); - expect(stderr).toContain('illegal value for flag --max-old-space-size'); + it('throws an error if an illegal value was supplied with --max-old-space-size', async () => { + const { stderr, stdout } = await run(__dirname, [], true, ['--max_old_space_size=1024a']); + expect(stderr).toContain('Error: illegal value for flag --max_old_space_size=1024a of type size_t'); expect(stdout).toBeFalsy(); }); }); diff --git a/test/utils/test-utils.js b/test/utils/test-utils.js index 80faa787a27..8d96d872333 100644 --- a/test/utils/test-utils.js +++ b/test/utils/test-utils.js @@ -2,7 +2,7 @@ const path = require('path'); const fs = require('fs'); const execa = require('execa'); -const { sync: spawnSync } = execa; +const { sync: spawnSync, node: execaNode } = execa; const { Writable } = require('readable-stream'); const concat = require('concat-stream'); @@ -15,16 +15,18 @@ const ENABLE_LOG_COMPILATION = process.env.ENABLE_PIPE || false; * @param {String} testCase The path to folder that contains the webpack.config.js * @param {Array} args Array of arguments to pass to webpack * @param {Boolean} setOutput Boolean that decides if a default output path will be set or not - * @returns {Object} The webpack output + * @returns {Object} The webpack output or Promise when nodeOptions are present */ -function run(testCase, args = [], setOutput = true) { +function run(testCase, args = [], setOutput = true, nodeArgs = []) { const cwd = path.resolve(testCase); const outputPath = path.resolve(testCase, 'bin'); + const processExecutor = nodeArgs.length ? execaNode : spawnSync; const argsWithOutput = setOutput ? args.concat('--output', outputPath) : args; - const result = spawnSync(WEBPACK_PATH, argsWithOutput, { + const result = processExecutor(WEBPACK_PATH, argsWithOutput, { cwd, reject: false, + nodeOptions: nodeArgs, stdio: ENABLE_LOG_COMPILATION ? 'inherit' : 'pipe', }); From d3e29368c40ee47e4f7a07c41281714645e20ea7 Mon Sep 17 00:00:00 2001 From: Rishabh Chawla Date: Mon, 17 Aug 2020 18:22:34 +0530 Subject: [PATCH 5/8] feat: serve integration (#1712) --- packages/serve/package.json | 8 +- packages/serve/src/index.ts | 9 +- packages/serve/src/startDevServer.ts | 4 +- packages/webpack-cli/__tests__/serve/index.js | 1 + .../webpack-cli/__tests__/serve/serve.test.js | 27 ++++++ .../__tests__/serve/webpack.config.js | 4 + packages/webpack-cli/package.json | 1 + test/serve/basic/serve-basic.test.js | 97 +++++++++---------- .../serve-custom-config.test.js | 87 ++++++++--------- 9 files changed, 139 insertions(+), 99 deletions(-) create mode 100644 packages/webpack-cli/__tests__/serve/index.js create mode 100644 packages/webpack-cli/__tests__/serve/serve.test.js create mode 100644 packages/webpack-cli/__tests__/serve/webpack.config.js diff --git a/packages/serve/package.json b/packages/serve/package.json index ab3821768a5..ab73f8f5126 100644 --- a/packages/serve/package.json +++ b/packages/serve/package.json @@ -13,8 +13,12 @@ "webpack-dev-server": "3.10.3" }, "peerDependencies": { - "webpack-cli": "4.x.x", - "webpack-dev-server": "3.x.x || 4.x.x" + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } }, "gitHead": "fb50f766851f500ca12867a2aa9de81fa6e368f9" } diff --git a/packages/serve/src/index.ts b/packages/serve/src/index.ts index 4fbb646fcf1..852c4fb2b1e 100644 --- a/packages/serve/src/index.ts +++ b/packages/serve/src/index.ts @@ -1,4 +1,3 @@ -import { devServer } from 'webpack-dev-server/bin/cli-flags'; import WebpackCLI from 'webpack-cli'; import logger from 'webpack-cli/lib/utils/logger'; import startDevServer from './startDevServer'; @@ -11,10 +10,16 @@ import startDevServer from './startDevServer'; * @returns {Function} invokes the devServer API */ export default function serve(...args: string[]): void { + let devServerFlags: object[]; + try { + devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer; + } catch (err) { + throw new Error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`); + } const cli = new WebpackCLI(); const core = cli.getCoreFlags(); - const parsedDevServerArgs = cli.argParser(devServer, args, true); + const parsedDevServerArgs = cli.argParser(devServerFlags, args, true); const devServerArgs = parsedDevServerArgs.opts; const parsedWebpackArgs = cli.argParser(core, parsedDevServerArgs.unknownArgs, true, process.title); const webpackArgs = parsedWebpackArgs.opts; diff --git a/packages/serve/src/startDevServer.ts b/packages/serve/src/startDevServer.ts index 8cbe238de76..00142402c5c 100644 --- a/packages/serve/src/startDevServer.ts +++ b/packages/serve/src/startDevServer.ts @@ -1,5 +1,3 @@ -import Server from 'webpack-dev-server/lib/Server'; - /** * * Starts the devServer @@ -10,6 +8,8 @@ import Server from 'webpack-dev-server/lib/Server'; * @returns {Void} */ export default function startDevServer(compiler, options): void { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const Server = require('webpack-dev-server/lib/Server'); const firstWpOpt = compiler.compilers ? compiler.compilers[0].options : compiler.options; const devServerOptions = firstWpOpt.devServer || {}; diff --git a/packages/webpack-cli/__tests__/serve/index.js b/packages/webpack-cli/__tests__/serve/index.js new file mode 100644 index 00000000000..6be02374db1 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/index.js @@ -0,0 +1 @@ +console.log('hello world'); diff --git a/packages/webpack-cli/__tests__/serve/serve.test.js b/packages/webpack-cli/__tests__/serve/serve.test.js new file mode 100644 index 00000000000..007cff90e95 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/serve.test.js @@ -0,0 +1,27 @@ +const { runServe } = require('../../../../test/utils/test-utils'); + +describe('Serve', () => { + const isWindows = process.platform === 'win32'; + + // TODO fix me on windows + if (isWindows) { + it('TODO: Fix on windows', () => { + expect(true).toBe(true); + }); + return; + } + + it('should run with cli', async () => { + const { stdout, stderr } = await runServe([], __dirname); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); + + it('should work with flags', async () => { + const { stdout, stderr } = await runServe(['--hot'], __dirname); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); +}); diff --git a/packages/webpack-cli/__tests__/serve/webpack.config.js b/packages/webpack-cli/__tests__/serve/webpack.config.js new file mode 100644 index 00000000000..955bbdd3364 --- /dev/null +++ b/packages/webpack-cli/__tests__/serve/webpack.config.js @@ -0,0 +1,4 @@ +module.exports = { + mode: 'development', + devtool: false, +}; diff --git a/packages/webpack-cli/package.json b/packages/webpack-cli/package.json index 462b40a767b..dbe3657b7fb 100644 --- a/packages/webpack-cli/package.json +++ b/packages/webpack-cli/package.json @@ -26,6 +26,7 @@ "@webpack-cli/package-utils": "^1.0.1-alpha.4", "@webpack-cli/info": "^1.0.1-alpha.4", "@webpack-cli/init": "^1.0.1-alpha.5", + "@webpack-cli/serve": "^1.0.1-alpha.5", "ansi-escapes": "^4.3.1", "colorette": "^1.2.1", "command-line-usage": "^6.1.0", diff --git a/test/serve/basic/serve-basic.test.js b/test/serve/basic/serve-basic.test.js index 2c489313431..67f39c94d36 100644 --- a/test/serve/basic/serve-basic.test.js +++ b/test/serve/basic/serve-basic.test.js @@ -19,62 +19,61 @@ describe('basic serve usage', () => { const isWindows = process.platform === 'win32'; + // TODO fix me on windows if (isWindows) { - // TODO fix me on windows - it('compiles without flags', () => { + it('TODO: Fix on windows', () => { expect(true).toBe(true); - - console.warn('TODO: fix `serve` test on windows'); - }); - } else { - it('should respect the --no-color flag', async () => { - const { stdout, stderr } = await runServe(['--help', '--no-color'], __dirname); - options.enabled = true; - expect(stdout).not.toContain(yellow(usageText)); - expect(stdout).toContain(descriptionText); - expect(stderr).toHaveLength(0); }); + return; + } - it('should not invoke info subcommand', async () => { - const { stdout, stderr } = await runServe(['--client-log-level', 'info'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('should respect the --no-color flag', async () => { + const { stdout, stderr } = await runServe(['--help', '--no-color'], __dirname); + options.enabled = true; + expect(stdout).not.toContain(yellow(usageText)); + expect(stdout).toContain(descriptionText); + expect(stderr).toHaveLength(0); + }); - it('compiles without flags', async () => { - const { stdout, stderr } = await runServe(['--port', port], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('should not invoke info subcommand', async () => { + const { stdout, stderr } = await runServe(['--client-log-level', 'info'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses hot flag to alter bundle', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('compiles without flags', async () => { + const { stdout, stderr } = await runServe(['--port', port], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses no-hot flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - expect(stderr).toHaveLength(0); - }); + it('uses hot flag to alter bundle', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('uses hot flag and progress flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot', '--progress'], testPath); - expect(stdout).toContain('main.js'); - expect(stdout).toContain('hot/dev-server.js'); - // progress flag makes use of stderr - expect(stderr).not.toHaveLength(0); - }); + it('uses no-hot flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + expect(stderr).toHaveLength(0); + }); - it('throws error on unknown flag', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); - expect(stdout).toHaveLength(0); - expect(stderr).toContain('Unknown argument: --unknown-flag'); - }); - } + it('uses hot flag and progress flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot', '--progress'], testPath); + expect(stdout).toContain('main.js'); + expect(stdout).toContain('hot/dev-server.js'); + // progress flag makes use of stderr + expect(stderr).not.toHaveLength(0); + }); + + it('throws error on unknown flag', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--unknown-flag'], testPath); + expect(stdout).toHaveLength(0); + expect(stderr).toContain('Unknown argument: --unknown-flag'); + }); }); diff --git a/test/serve/with-custom-port/serve-custom-config.test.js b/test/serve/with-custom-port/serve-custom-config.test.js index 7025374304c..15088616b6a 100644 --- a/test/serve/with-custom-port/serve-custom-config.test.js +++ b/test/serve/with-custom-port/serve-custom-config.test.js @@ -15,54 +15,53 @@ describe('serve with devServer in config', () => { const isWindows = process.platform === 'win32'; + // TODO fix me on windows if (isWindows) { - // TODO fix me on windows - it('compiles without flags', () => { + it('TODO: Fix on windows', () => { expect(true).toBe(true); - - console.warn('TODO: fix `serve` test on windows'); - }); - } else { - it('Should pick up the host and port from config', async () => { - const { stdout, stderr } = await runServe([], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain('http://0.0.0.0:1234'); - expect(stderr).toBeFalsy(); }); + return; + } - it('Port flag should override the config port', async () => { - const { stdout, stderr } = await runServe(['--port', port], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); + it('Should pick up the host and port from config', async () => { + const { stdout, stderr } = await runServe([], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain('http://0.0.0.0:1234'); + expect(stderr).toBeFalsy(); + }); - it('Passing hot flag works alongside other server config', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - // HMR is being used - expect(stdout).toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); + it('Port flag should override the config port', async () => { + const { stdout, stderr } = await runServe(['--port', port], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); - it('works fine when no-hot flag is passed alongside other server config', async () => { - const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); - // Should output the correct bundle file - expect(stdout).toContain('main.js'); - // HMR is not being used - expect(stdout).not.toContain('hot/dev-server.js'); - // Runs at correct host and port - expect(stdout).toContain(`http://0.0.0.0:${port}`); - expect(stderr).toBeFalsy(); - }); - } + it('Passing hot flag works alongside other server config', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--hot'], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + // HMR is being used + expect(stdout).toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); + + it('works fine when no-hot flag is passed alongside other server config', async () => { + const { stdout, stderr } = await runServe(['--port', port, '--no-hot'], testPath); + // Should output the correct bundle file + expect(stdout).toContain('main.js'); + // HMR is not being used + expect(stdout).not.toContain('hot/dev-server.js'); + // Runs at correct host and port + expect(stdout).toContain(`http://0.0.0.0:${port}`); + expect(stderr).toBeFalsy(); + }); }); From 08aaff3ccc249749da742e2e9c0bb09f82cc1001 Mon Sep 17 00:00:00 2001 From: James George Date: Wed, 19 Aug 2020 15:46:14 +0530 Subject: [PATCH 6/8] refactor: cliExecuter consumes runCLI (#1754) --- packages/webpack-cli/__tests__/cli-executer.test.js | 10 +++++----- packages/webpack-cli/lib/runner.js | 10 ---------- packages/webpack-cli/lib/utils/cli-executer.js | 4 ++-- 3 files changed, 7 insertions(+), 17 deletions(-) delete mode 100644 packages/webpack-cli/lib/runner.js diff --git a/packages/webpack-cli/__tests__/cli-executer.test.js b/packages/webpack-cli/__tests__/cli-executer.test.js index a67b63bc293..74bdde7c346 100644 --- a/packages/webpack-cli/__tests__/cli-executer.test.js +++ b/packages/webpack-cli/__tests__/cli-executer.test.js @@ -1,8 +1,8 @@ -jest.mock('../lib/runner'); +jest.mock('../lib/bootstrap'); jest.mock('enquirer'); -const runner = require('../lib/runner'); -runner.mockImplementation(() => {}); +const runCLI = require('../lib/bootstrap'); +runCLI.mockImplementation(() => {}); describe('CLI Executer', () => { let cliExecuter = null; @@ -48,8 +48,8 @@ describe('CLI Executer', () => { await cliExecuter(); // ensure that the webpack runCLI is called - expect(runner.mock.calls.length).toEqual(1); - expect(runner.mock.calls[0]).toEqual([[], ['--config', 'test1', '--entry', 'test2', '--progress']]); + expect(runCLI.mock.calls.length).toEqual(1); + expect(runCLI.mock.calls[0][0]).toEqual(['--config', 'test1', '--entry', 'test2', '--progress']); // check that webpack options are actually being displayed that // the user can select from diff --git a/packages/webpack-cli/lib/runner.js b/packages/webpack-cli/lib/runner.js deleted file mode 100644 index 50295954e04..00000000000 --- a/packages/webpack-cli/lib/runner.js +++ /dev/null @@ -1,10 +0,0 @@ -const execa = require('execa'); -const cliPath = require.resolve('./bootstrap.js'); - -function runner(nodeArgs, cliArgs) { - execa('node', [...nodeArgs, cliPath, ...cliArgs], { stdio: 'inherit' }).catch((e) => { - process.exit(e.exitCode); - }); -} - -module.exports = runner; diff --git a/packages/webpack-cli/lib/utils/cli-executer.js b/packages/webpack-cli/lib/utils/cli-executer.js index fbb2ef6895c..8c4a89cd95b 100644 --- a/packages/webpack-cli/lib/utils/cli-executer.js +++ b/packages/webpack-cli/lib/utils/cli-executer.js @@ -2,7 +2,7 @@ const { MultiSelect, Input } = require('enquirer'); const { cyan } = require('colorette'); const logger = require('./logger'); const cliArgs = require('./cli-flags').core; -const runner = require('../runner'); +const runCLI = require('../bootstrap'); async function prompter() { const args = []; @@ -56,7 +56,7 @@ async function run() { const args = await prompter(); process.stdout.write('\n'); logger.info('Executing CLI\n'); - await runner([], args); + await runCLI(args); } catch (err) { logger.error(`Action Interrupted, use ${cyan('webpack-cli help')} to see possible options.`); } From 83f73b056e224301b871bee5e9b7254e64e84e95 Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 19 Aug 2020 15:50:55 +0530 Subject: [PATCH 7/8] fix: use appropriate exit codes (#1755) --- README.md | 9 +++++++++ packages/package-utils/__tests__/packageUtils.test.ts | 2 +- packages/package-utils/src/packageUtils.ts | 2 +- packages/utils/src/modify-config-helper.ts | 6 +++--- packages/utils/src/npm-packages-exists.ts | 2 +- packages/webpack-cli/lib/groups/HelpGroup.js | 4 ++-- packages/webpack-cli/lib/utils/process-log.js | 2 +- 7 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b65ccaf496a..7d69c4b4bf9 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ - [Utilities](#utilities) - [Getting started](#getting-started) - [webpack CLI Scaffolds](#webpack-cli-scaffolds) +- [Exit codes and their meanings](#exit-codes-and-their-meanings) - [Contributing and Internal Documentation](#contributing-and-internal-documentation) - [Open Collective](#open-collective) @@ -96,6 +97,14 @@ With v3 of webpack CLI, we introduced scaffolding as an integral part of the CLI You can read more about [Scaffolding](https://webpack.js.org/guides/scaffolding), learn [How to compose a webpack-scaffold?](https://webpack.js.org/contribute/writing-a-scaffold) or generate one with [webpack-scaffold-starter](https://github.com/rishabh3112/webpack-scaffold-starter). +## Exit codes and their meanings + +| Exit Code | Description | +| --------- | -------------------------------------------------- | +| `0` | Success | +| `1` | Warnings/Errors from webpack | +| `2` | Configuration/options problem or an internal error | + ## Contributing and Internal Documentation The webpack family welcomes any contributor, small or big. We are happy to elaborate, guide you through the source code and find issues you might want to work on! To get started have a look at our [documentation on contributing](./.github/CONTRIBUTING.md). diff --git a/packages/package-utils/__tests__/packageUtils.test.ts b/packages/package-utils/__tests__/packageUtils.test.ts index 64bba4600b1..e853fa2ac0a 100644 --- a/packages/package-utils/__tests__/packageUtils.test.ts +++ b/packages/package-utils/__tests__/packageUtils.test.ts @@ -177,7 +177,7 @@ describe('packageUtils', () => { // runCommand should not be called, because the installation is not confirmed expect((runCommand as jest.Mock).mock.calls.length).toEqual(0); expect((prompt as jest.Mock).mock.calls[0][0][0].message).toMatch(/Would you like to install test-package\?/); - expect(process.exitCode).toEqual(-1); + expect(process.exitCode).toEqual(2); }); }); }); diff --git a/packages/package-utils/src/packageUtils.ts b/packages/package-utils/src/packageUtils.ts index 09b9f0330f5..167d7bee322 100644 --- a/packages/package-utils/src/packageUtils.ts +++ b/packages/package-utils/src/packageUtils.ts @@ -98,5 +98,5 @@ export async function promptInstallation(packageName: string, preMessage?: Funct return exports.packageExists(packageName); } // eslint-disable-next-line require-atomic-updates - process.exitCode = -1; + process.exitCode = 2; } diff --git a/packages/utils/src/modify-config-helper.ts b/packages/utils/src/modify-config-helper.ts index eb3681b0d3f..913d0d21e84 100644 --- a/packages/utils/src/modify-config-helper.ts +++ b/packages/utils/src/modify-config-helper.ts @@ -86,7 +86,7 @@ export function modifyHelperUtil( } catch (err) { console.error(red('\nYour package.json was incorrectly formatted.\n')); Error.stackTraceLimit = 0; - process.exitCode = -1; + process.exitCode = 2; } env.registerStub(generator, generatorName); @@ -108,7 +108,7 @@ export function modifyHelperUtil( red("\nPlease make sure to use 'this.config.set('configuration', this.configuration);' at the end of the generator.\n"), ); Error.stackTraceLimit = 0; - process.exitCode = -1; + process.exitCode = 2; } try { // the configuration stored in .yo-rc.json should already be in the correct @@ -126,7 +126,7 @@ export function modifyHelperUtil( red('\nYour yeoman configuration file (.yo-rc.json) was incorrectly formatted. Deleting it may fix the problem.\n'), ); Error.stackTraceLimit = 0; - process.exitCode = -1; + process.exitCode = 2; } const transformConfig = Object.assign( diff --git a/packages/utils/src/npm-packages-exists.ts b/packages/utils/src/npm-packages-exists.ts index a5b3d4b7d11..3233a8027bb 100644 --- a/packages/utils/src/npm-packages-exists.ts +++ b/packages/utils/src/npm-packages-exists.ts @@ -61,7 +61,7 @@ export function npmPackagesExists(pkg: string[]): void { .catch((err: Error): void => { console.error(err.stack || err); // eslint-disable-next-line no-process-exit - process.exit(0); + process.exit(2); }) .then(resolvePackagesIfReady); }); diff --git a/packages/webpack-cli/lib/groups/HelpGroup.js b/packages/webpack-cli/lib/groups/HelpGroup.js index c78612e8b54..b72ab8ea325 100644 --- a/packages/webpack-cli/lib/groups/HelpGroup.js +++ b/packages/webpack-cli/lib/groups/HelpGroup.js @@ -58,13 +58,13 @@ class HelpGroup { } } catch (e) { logger.error('Error: External package not found.'); - process.exitCode = 1; + process.exit(2); } } if (commandsUsed.length > 1) { logger.error('You provided multiple commands. Please use only one command at a time.\n'); - process.exit(1); + process.exit(2); } if (invalidArgs.length > 0) { diff --git a/packages/webpack-cli/lib/utils/process-log.js b/packages/webpack-cli/lib/utils/process-log.js index 17dfa0a263f..45933841849 100644 --- a/packages/webpack-cli/lib/utils/process-log.js +++ b/packages/webpack-cli/lib/utils/process-log.js @@ -2,7 +2,7 @@ const logger = require('./logger'); function logErrorAndExit(error) { if (error && error.stack) logger.error(error.stack); - process.exit(1); + process.exit(error.exitCode); } process.on('uncaughtException', (error) => { From d3ed19a96811b98caa0ea0de0f8d7e76fe06879d Mon Sep 17 00:00:00 2001 From: Nitin Kumar Date: Wed, 19 Aug 2020 16:23:07 +0530 Subject: [PATCH 8/8] feat: add --config-name flag (#1753) --- packages/webpack-cli/README.md | 1 + .../webpack-cli/lib/groups/ConfigGroup.js | 11 +++++ packages/webpack-cli/lib/utils/cli-flags.js | 8 ++++ test/config-name/config-name.test.js | 46 +++++++++++++++++++ test/config-name/src/first.js | 1 + test/config-name/src/second.js | 1 + test/config-name/src/third.js | 1 + test/config-name/webpack.config.js | 26 +++++++++++ 8 files changed, 95 insertions(+) create mode 100644 test/config-name/config-name.test.js create mode 100644 test/config-name/src/first.js create mode 100644 test/config-name/src/second.js create mode 100644 test/config-name/src/third.js create mode 100644 test/config-name/webpack.config.js diff --git a/packages/webpack-cli/README.md b/packages/webpack-cli/README.md index 4751e251fec..637ac81ea74 100644 --- a/packages/webpack-cli/README.md +++ b/packages/webpack-cli/README.md @@ -38,6 +38,7 @@ yarn add webpack-cli --dev ``` --entry string[] The entry point(s) of your application. -c, --config string Provide path to a webpack configuration file + --config-name string[] Name of the configuration to use -m, --merge string Merge a configuration file using webpack-merge --progress Print compilation progress during build --color Enables colors on console diff --git a/packages/webpack-cli/lib/groups/ConfigGroup.js b/packages/webpack-cli/lib/groups/ConfigGroup.js index 5e8953759be..fbfc3b4ccd4 100644 --- a/packages/webpack-cli/lib/groups/ConfigGroup.js +++ b/packages/webpack-cli/lib/groups/ConfigGroup.js @@ -5,6 +5,7 @@ const { extensions, jsVariants } = require('interpret'); const GroupHelper = require('../utils/GroupHelper'); const rechoir = require('rechoir'); const MergeError = require('../utils/errors/MergeError'); +const logger = require('../utils/logger'); // Order defines the priority, in increasing order // example - config file lookup will be in order of .webpack/webpack.config.development.js -> webpack.config.development.js -> webpack.config.js @@ -117,6 +118,16 @@ class ConfigGroup extends GroupHelper { const newOptions = configOptions(formattedEnv, argv); // When config function returns a promise, resolve it, if not it's resolved by default newOptionsObject['options'] = await Promise.resolve(newOptions); + } else if (Array.isArray(configOptions) && this.args.configName) { + // In case of exporting multiple configurations, If you pass a name to --config-name flag, + // webpack will only build that specific configuration. + const namedOptions = configOptions.filter((opt) => this.args.configName.includes(opt.name)); + if (namedOptions.length === 0) { + logger.error(`Configuration with name "${this.args.configName}" was not found.`); + process.exit(2); + } else { + newOptionsObject['options'] = namedOptions; + } } else { if (Array.isArray(configOptions) && !configOptions.length) { newOptionsObject['options'] = {}; diff --git a/packages/webpack-cli/lib/utils/cli-flags.js b/packages/webpack-cli/lib/utils/cli-flags.js index b11a1debb08..0e4afea8193 100644 --- a/packages/webpack-cli/lib/utils/cli-flags.js +++ b/packages/webpack-cli/lib/utils/cli-flags.js @@ -124,6 +124,14 @@ module.exports = { description: 'Provide path to a webpack configuration file e.g. ./webpack.config.js', link: 'https://webpack.js.org/configuration/', }, + { + name: 'config-name', + usage: '--config-name ', + type: String, + multiple: true, + group: CONFIG_GROUP, + description: 'Name of the configuration to use', + }, { name: 'color', usage: '--color', diff --git a/test/config-name/config-name.test.js b/test/config-name/config-name.test.js new file mode 100644 index 00000000000..53b66537ecf --- /dev/null +++ b/test/config-name/config-name.test.js @@ -0,0 +1,46 @@ +'use strict'; + +const { run } = require('../utils/test-utils'); +const { stat } = require('fs'); +const { resolve } = require('path'); + +describe('--config-name flag', () => { + it('should select only the config whose name is passed with --config-name', (done) => { + const { stderr, stdout } = run(__dirname, ['--config-name', 'first'], false); + expect(stderr).toBeFalsy(); + expect(stdout).toContain('Child first'); + expect(stdout).not.toContain('Child second'); + expect(stdout).not.toContain('Child third'); + + stat(resolve(__dirname, './dist/dist-first.js'), (err, stats) => { + expect(err).toBe(null); + expect(stats.isFile()).toBe(true); + done(); + }); + }); + + it('should work with multiple values for --config-name', (done) => { + const { stderr, stdout } = run(__dirname, ['--config-name', 'first', '--config-name', 'third'], false); + expect(stderr).toBeFalsy(); + expect(stdout).toContain('Child first'); + expect(stdout).not.toContain('Child second'); + expect(stdout).toContain('Child third'); + + stat(resolve(__dirname, './dist/dist-first.js'), (err, stats) => { + expect(err).toBe(null); + expect(stats.isFile()).toBe(true); + done(); + }); + stat(resolve(__dirname, './dist/dist-third.js'), (err, stats) => { + expect(err).toBe(null); + expect(stats.isFile()).toBe(true); + done(); + }); + }); + + it('should log error if invalid config name is provided', () => { + const { stderr, stdout } = run(__dirname, ['--config-name', 'test'], false); + expect(stderr).toContain('Configuration with name "test" was not found.'); + expect(stdout).toBeFalsy(); + }); +}); diff --git a/test/config-name/src/first.js b/test/config-name/src/first.js new file mode 100644 index 00000000000..3dfd0a1def0 --- /dev/null +++ b/test/config-name/src/first.js @@ -0,0 +1 @@ +console.log('first config'); diff --git a/test/config-name/src/second.js b/test/config-name/src/second.js new file mode 100644 index 00000000000..15c0f734c34 --- /dev/null +++ b/test/config-name/src/second.js @@ -0,0 +1 @@ +console.log('second config'); diff --git a/test/config-name/src/third.js b/test/config-name/src/third.js new file mode 100644 index 00000000000..1047ae56bdc --- /dev/null +++ b/test/config-name/src/third.js @@ -0,0 +1 @@ +console.log('third config'); diff --git a/test/config-name/webpack.config.js b/test/config-name/webpack.config.js new file mode 100644 index 00000000000..515d077d3e2 --- /dev/null +++ b/test/config-name/webpack.config.js @@ -0,0 +1,26 @@ +module.exports = [ + { + output: { + filename: './dist-first.js', + }, + name: 'first', + entry: './src/first.js', + mode: 'development', + }, + { + output: { + filename: './dist-second.js', + }, + name: 'second', + entry: './src/second.js', + mode: 'production', + }, + { + output: { + filename: './dist-third.js', + }, + name: 'third', + entry: './src/third.js', + mode: 'none', + }, +];