Skip to content

Commit 3a05fca

Browse files
committed
fix(webpack-cli): throw error for invalid args with HelpGroup
1 parent 13bf7e5 commit 3a05fca

File tree

11 files changed

+114
-84
lines changed

11 files changed

+114
-84
lines changed

packages/webpack-cli/lib/bootstrap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ async function runCLI(cli, commandIsUsed) {
5151
cli.runHelp(process.argv);
5252
return;
5353
} else if (versionFlagExists) {
54-
cli.runVersion(commandIsUsed);
54+
cli.runVersion(process.argv, commandIsUsed);
5555
return;
5656
}
5757

packages/webpack-cli/lib/groups/HelpGroup.js

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,16 @@ const { core, commands } = require('../utils/cli-flags');
33
const commandLineUsage = require('command-line-usage');
44

55
class HelpGroup {
6-
outputHelp(isCommand = true, subject) {
7-
if (subject) {
6+
outputHelp(isCommand = true, subject, args) {
7+
const [, , ...rawArgs] = args;
8+
const commandsUsed = rawArgs.filter((val) => commands.find(({ name }) => name === val));
9+
const flagsUsed = rawArgs.filter((val) => core.find(({ name }) => name === val.slice(2)));
10+
const argsUsed = [...commandsUsed, ...flagsUsed];
11+
const invalidArgs = rawArgs.filter(
12+
(e) => !argsUsed.includes(e) && !e.includes('--color') && e !== 'version' && e !== '-v' && e !== 'help',
13+
);
14+
15+
if (subject && invalidArgs.length === 0) {
816
const info = isCommand ? commands : core;
917
// Contains object with details about given subject
1018
const options = info.find((commandOrFlag) => {
@@ -34,20 +42,27 @@ class HelpGroup {
3442
});
3543
process.stdout.write(flags);
3644
}
45+
} else if (invalidArgs.length > 0) {
46+
console.warn(chalk.yellow(`\nYou provided an invalid option '${invalidArgs[0]}'.`));
47+
process.stdout.write(this.run().outputOptions.help);
3748
} else {
3849
process.stdout.write(this.run().outputOptions.help);
3950
}
4051
process.stdout.write('\n Made with ♥️ by the webpack team \n');
4152
}
4253

43-
outputVersion(externalPkg) {
44-
const commandsUsed = () => {
45-
return process.argv.filter((val) => commands.find(({ name }) => name === val));
46-
};
54+
outputVersion(args, externalPkg) {
55+
const [, , ...rawArgs] = args;
56+
const commandsUsed = rawArgs.filter((val) => commands.find(({ name }) => name === val));
57+
const flagsUsed = rawArgs.filter((val) => core.find(({ name }) => name === val.slice(2)));
58+
const argsUsed = [...commandsUsed, ...flagsUsed];
59+
const invalidArgs = rawArgs.filter(
60+
(e) => !argsUsed.includes(e) && !e.includes('--color') && e !== 'version' && e !== '-v' && e !== 'help',
61+
);
4762

48-
if (externalPkg && commandsUsed().length === 1) {
63+
if (externalPkg && commandsUsed.length === 1 && invalidArgs.length === 0) {
4964
try {
50-
if (commandsUsed().includes(externalPkg.name)) {
65+
if (commandsUsed.includes(externalPkg.name)) {
5166
const { name, version } = require(`@webpack-cli/${externalPkg.name}/package.json`);
5267
process.stdout.write(`\n${name} ${version}`);
5368
} else {
@@ -60,11 +75,17 @@ class HelpGroup {
6075
}
6176
}
6277

63-
if (commandsUsed().length > 1) {
78+
if (commandsUsed.length > 1) {
6479
console.error(chalk.red('\nYou provided multiple commands. Please use only one command at a time.\n'));
6580
process.exit();
6681
}
6782

83+
if (invalidArgs.length > 0) {
84+
console.error(chalk.red(`\nError: Invalid Option '${invalidArgs[0]}'.`));
85+
console.info(chalk.cyan('Run webpack --help to see available commands and arguments.\n'));
86+
process.exit(-2);
87+
}
88+
6889
const pkgJSON = require('../../package.json');
6990
const webpack = require('webpack');
7091
process.stdout.write(`\nwebpack-cli ${pkgJSON.version}`);

packages/webpack-cli/lib/webpack-cli.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,12 @@ class WebpackCLI extends GroupHelper {
309309
return args.includes(name);
310310
})[0];
311311
const isCommand = commandNames.includes(subject);
312-
return new HelpGroup().outputHelp(isCommand, subject);
312+
return new HelpGroup().outputHelp(isCommand, subject, args);
313313
}
314314

315-
runVersion(externalPkg) {
315+
runVersion(args, externalPkg) {
316316
const HelpGroup = require('./groups/HelpGroup');
317-
return new HelpGroup().outputVersion(externalPkg);
317+
return new HelpGroup().outputVersion(args, externalPkg);
318318
}
319319
}
320320

test/help/help-commands.test.js

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,14 @@ const { run } = require('../utils/test-utils');
44
const helpHeader = 'The build tool for modern web applications';
55

66
describe('commands help', () => {
7-
it('shows default help with invalid command', () => {
8-
const { stdout, stderr } = run(__dirname, ['--help', 'myCommand'], false);
9-
expect(stdout).toContain(helpHeader);
10-
expect(stderr).toHaveLength(0);
7+
it('throws error for invalid command with --help flag', () => {
8+
const { stderr } = run(__dirname, ['--help', 'myCommand'], false);
9+
expect(stderr).toContain(`You provided an invalid option 'myCommand'`);
1110
});
12-
it('shows command help with valid command', () => {
13-
const { stdout, stderr } = run(__dirname, ['--help', 'init'], false);
14-
expect(stdout).not.toContain(helpHeader);
15-
expect(stdout).toContain('webpack init | init <scaffold>');
16-
expect(stderr).toHaveLength(0);
11+
12+
it('throws error for invalid command with help command', () => {
13+
const { stderr } = run(__dirname, ['help', 'myCommand'], false);
14+
expect(stderr).toContain(`You provided an invalid option 'myCommand'`);
1715
});
1816

1917
it('gives precedence to earlier command in case of multiple commands', () => {

test/help/help-flags.test.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,16 @@ const { run } = require('../utils/test-utils');
44
const helpHeader = 'The build tool for modern web applications';
55

66
describe('commands help', () => {
7-
it('shows default help with invalid flag', () => {
8-
const { stdout, stderr } = run(__dirname, ['--help', '--my-flag'], false);
9-
expect(stdout).toContain(helpHeader);
10-
expect(stderr).toHaveLength(0);
7+
it('throws error for invalid flag with --help flag', () => {
8+
const { stderr } = run(__dirname, ['--help', '--my-flag'], false);
9+
expect(stderr).toContain(`You provided an invalid option '--my-flag'`);
10+
});
11+
12+
it('throws error for invalid flag with help command', () => {
13+
const { stderr } = run(__dirname, ['help', '--my-flag'], false);
14+
expect(stderr).toContain(`You provided an invalid option '--my-flag'`);
1115
});
16+
1217
it('shows flag help with valid flag', () => {
1318
const { stdout, stderr } = run(__dirname, ['--help', '--merge'], false);
1419
expect(stdout).not.toContain(helpHeader);

test/help/help-multi-args.test.js

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,25 @@
11
'use strict';
22

33
const { run } = require('../utils/test-utils');
4-
const outputDescription = 'Output location of the file generated by webpack';
5-
const createDescription = 'Initialize a new webpack configuration';
6-
describe('help flag with multiple arguments', () => {
7-
it('outputs info with dashed syntax', () => {
8-
const { stdout, stderr } = run(__dirname, ['--help', '--target', 'browser']);
9-
expect(stdout).toContain(outputDescription);
10-
expect(stderr).toHaveLength(0);
11-
});
4+
const { commands } = require('../../packages/webpack-cli/lib/utils/cli-flags');
5+
const helpHeader = 'The build tool for modern web applications';
126

13-
it('outputs info with multiple arguments using dashes and with precedence', () => {
14-
const { stdout, stderr } = run(__dirname, ['--target', 'browser', '--help']);
15-
expect(stdout).toContain(outputDescription);
16-
expect(stderr).toHaveLength(0);
7+
describe('help cmd with multiple arguments', () => {
8+
commands.forEach((cmd) => {
9+
it(`shows cmd help with ${cmd.name}`, () => {
10+
const { stdout, stderr } = run(__dirname, ['--help', `${cmd.name}`], false);
11+
expect(stdout).not.toContain(helpHeader);
12+
expect(stdout).toContain(`${cmd.name}`);
13+
expect(stdout).toContain(`${cmd.usage}`);
14+
expect(stdout).toContain(`${cmd.description}`);
15+
expect(stderr).toHaveLength(0);
16+
});
1717
});
1818

19-
it('outputs info with multiple commands and with precedence', () => {
20-
const { stdout, stderr } = run(__dirname, ['init', 'help']);
21-
expect(stdout).toContain(createDescription);
19+
it('should output help for --version by taking precedence', () => {
20+
const { stdout, stderr } = run(__dirname, ['--help', '--version'], false);
21+
expect(stdout).not.toContain(helpHeader);
22+
expect(stdout).toContain('webpack --version');
2223
expect(stderr).toHaveLength(0);
2324
});
2425
});

test/help/help-single-arg.test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('single help flag', () => {
1818
expect(stdout).toContain(example);
1919
expect(stderr).toHaveLength(0);
2020
});
21+
2122
it('outputs help info with command syntax', () => {
2223
const { stdout, stderr } = run(__dirname, ['help'], false);
2324
expect(stdout).toContain(helpHeader);

test/version/version-external-packages.test.js

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,35 +9,53 @@ const cliPkgJSON = require('../../packages/webpack-cli/package.json');
99

1010
describe('version flag with external packages', () => {
1111
it('outputs version with init', () => {
12-
const { stdout, stderr } = run(__dirname, ['init', '--version']);
12+
const { stdout, stderr } = run(__dirname, ['init', '--version'], false);
1313
expect(stdout).toContain(initPkgJSON.version);
1414
expect(stdout).toContain(cliPkgJSON.version);
1515
expect(stderr).toHaveLength(0);
1616
});
1717

1818
it('outputs version with info', () => {
19-
const { stdout, stderr } = run(__dirname, ['info', '--version']);
19+
const { stdout, stderr } = run(__dirname, ['info', '--version'], false);
2020
expect(stdout).toContain(infoPkgJSON.version);
2121
expect(stdout).toContain(cliPkgJSON.version);
2222
expect(stderr).toHaveLength(0);
2323
});
2424

2525
it('outputs version with serve', () => {
26-
const { stdout, stderr } = run(__dirname, ['serve', '--version']);
26+
const { stdout, stderr } = run(__dirname, ['serve', '--version'], false);
2727
expect(stdout).toContain(servePkgJSON.version);
2828
expect(stdout).toContain(cliPkgJSON.version);
2929
expect(stderr).toHaveLength(0);
3030
});
3131

3232
it('outputs version with migrate', () => {
33-
const { stdout, stderr } = run(__dirname, ['migrate', '--version']);
33+
const { stdout, stderr } = run(__dirname, ['migrate', '--version'], false);
3434
expect(stdout).toContain(migratePkgJSON.version);
3535
expect(stdout).toContain(cliPkgJSON.version);
3636
expect(stderr).toHaveLength(0);
3737
});
3838

3939
it(' should throw error for multiple commands', () => {
40-
const { stderr } = run(__dirname, ['init', 'migrate', '--version']);
40+
const { stderr } = run(__dirname, ['init', 'migrate', '--version'], false);
4141
expect(stderr).toContain('You provided multiple commands.');
4242
});
43+
44+
it(' should throw error if invalid argument is present with --version flag', () => {
45+
const { stderr, stdout } = run(__dirname, ['init', 'abc', '--version'], false);
46+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
47+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
48+
});
49+
50+
it(' should throw error if invalid argument is present with version command', () => {
51+
const { stderr, stdout } = run(__dirname, ['init', 'abc', 'version'], false);
52+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
53+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
54+
});
55+
56+
it(' should throw error if invalid argument is present with -v alias', () => {
57+
const { stderr, stdout } = run(__dirname, ['init', 'abc', '-v'], false);
58+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
59+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
60+
});
4361
});

test/version/version-multi-args.test.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,8 @@ const { run } = require('../utils/test-utils');
44
const pkgJSON = require('../../packages/webpack-cli/package.json');
55

66
describe('version flag with multiple arguments', () => {
7-
it('outputs version with mixed syntax', () => {
8-
const { stdout, stderr } = run(__dirname, ['--version', '--target', 'browser']);
9-
expect(stdout).toContain(pkgJSON.version);
10-
expect(stderr).toHaveLength(0);
11-
});
12-
137
it('does not output version with help command', () => {
14-
const { stdout, stderr } = run(__dirname, ['version', 'help']);
8+
const { stdout, stderr } = run(__dirname, ['version', 'help'], false);
159
expect(stdout).not.toContain(pkgJSON.version);
1610

1711
const uniqueIdentifier = 'Made with ♥️ by the webpack team';
@@ -20,17 +14,32 @@ describe('version flag with multiple arguments', () => {
2014
});
2115

2216
it('does not output version with help dashed', () => {
23-
const { stdout, stderr } = run(__dirname, ['version', '--help']);
17+
const { stdout, stderr } = run(__dirname, ['version', '--help'], false);
2418
expect(stdout).not.toContain(pkgJSON.version);
2519

2620
const uniqueIdentifier = 'Made with ♥️ by the webpack team';
2721
expect(stdout).toContain(uniqueIdentifier);
2822
expect(stderr).toHaveLength(0);
2923
});
3024

31-
it('outputs version with multiple dashed args and has precedence', () => {
32-
const { stdout, stderr } = run(__dirname, ['--target', 'browser', '--version']);
33-
expect(stdout).toContain(pkgJSON.version);
34-
expect(stderr).toHaveLength(0);
25+
it('throws error if invalid arg is passed with version command', () => {
26+
const { stdout, stderr } = run(__dirname, ['version', 'abc'], false);
27+
expect(stdout).not.toContain(pkgJSON.version);
28+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
29+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
30+
});
31+
32+
it('throws error if invalid arg is passed with --version flag', () => {
33+
const { stdout, stderr } = run(__dirname, ['--version', 'abc'], false);
34+
expect(stdout).not.toContain(pkgJSON.version);
35+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
36+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
37+
});
38+
39+
it('throws error if invalid arg is passed with -v alias', () => {
40+
const { stdout, stderr } = run(__dirname, ['-v', 'abc'], false);
41+
expect(stdout).not.toContain(pkgJSON.version);
42+
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
43+
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
3544
});
3645
});

test/version/version-single-arg.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ const pkgJSON = require('../../packages/webpack-cli/package.json');
55

66
describe('single version flag', () => {
77
it('outputs versions with command syntax', () => {
8-
const { stdout, stderr } = run(__dirname, ['version']);
8+
const { stdout, stderr } = run(__dirname, ['version'], false);
99
expect(stdout).toContain(pkgJSON.version);
1010
expect(stderr).toHaveLength(0);
1111
});
1212

1313
it('outputs versions with dashed syntax', () => {
14-
const { stdout, stderr } = run(__dirname, ['--version']);
14+
const { stdout, stderr } = run(__dirname, ['--version'], false);
1515
expect(stdout).toContain(pkgJSON.version);
1616
expect(stderr).toHaveLength(0);
1717
});
1818

1919
it('outputs versions with alias syntax', () => {
20-
const { stdout, stderr } = run(__dirname, ['-v']);
20+
const { stdout, stderr } = run(__dirname, ['-v'], false);
2121
expect(stdout).toContain(pkgJSON.version);
2222
expect(stderr).toHaveLength(0);
2323
});

0 commit comments

Comments
 (0)