Skip to content

Commit

Permalink
fix(webpack-cli): throw error for invalid args with HelpGroup
Browse files Browse the repository at this point in the history
  • Loading branch information
snitin315 committed Apr 16, 2020
1 parent 1c8d0ce commit ffc896b
Show file tree
Hide file tree
Showing 11 changed files with 118 additions and 67 deletions.
6 changes: 4 additions & 2 deletions packages/webpack-cli/lib/bootstrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ async function runCLI(cli, commandIsUsed) {
const versionFlagExists = isFlagPresent(process.argv, 'version') || isFlagPresent(process.argv, '-v');

if (helpFlagExists) {
cli.runHelp(process.argv);
args = normalizeFlags(process.argv, commands);
cli.runHelp(args);
return;
} else if (versionFlagExists) {
cli.runVersion(commandIsUsed);
args = normalizeFlags(process.argv, commands);
cli.runVersion(args, commandIsUsed);
return;
}

Expand Down
25 changes: 15 additions & 10 deletions packages/webpack-cli/lib/groups/HelpGroup.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ const { core, commands } = require('../utils/cli-flags');
const commandLineUsage = require('command-line-usage');

class HelpGroup {
outputHelp(isCommand = true, subject) {
if (subject) {
outputHelp(isCommand = true, subject, invalidArgs) {
if (subject && invalidArgs.length === 0) {
const info = isCommand ? commands : core;
// Contains object with details about given subject
const options = info.find((commandOrFlag) => {
Expand Down Expand Up @@ -34,20 +34,19 @@ class HelpGroup {
});
process.stdout.write(flags);
}
} else if (invalidArgs.length > 0) {
console.warn(chalk.yellow(`\nYou provided an invalid option '${invalidArgs[0]}'.`));
process.stdout.write(this.run().outputOptions.help);
} else {
process.stdout.write(this.run().outputOptions.help);
}
process.stdout.write('\n Made with ♥️ by the webpack team \n');
}

outputVersion(externalPkg) {
const commandsUsed = () => {
return process.argv.filter((val) => commands.find(({ name }) => name === val));
};

if (externalPkg && commandsUsed().length === 1) {
outputVersion(externalPkg, commandsUsed, invalidArgs) {
if (externalPkg && commandsUsed.length === 1 && invalidArgs.length === 0) {
try {
if (commandsUsed().includes(externalPkg.name)) {
if (commandsUsed.includes(externalPkg.name)) {
const { name, version } = require(`@webpack-cli/${externalPkg.name}/package.json`);
process.stdout.write(`\n${name} ${version}`);
} else {
Expand All @@ -60,11 +59,17 @@ class HelpGroup {
}
}

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

if (invalidArgs.length > 0) {
console.error(chalk.red(`\nError: Invalid Option '${invalidArgs[0]}'.`));
console.info(chalk.cyan('Run webpack --help to see available commands and arguments.\n'));
process.exit(-2);
}

const pkgJSON = require('../../package.json');
const webpack = require('webpack');
process.stdout.write(`\nwebpack-cli ${pkgJSON.version}`);
Expand Down
10 changes: 10 additions & 0 deletions packages/webpack-cli/lib/utils/unknown-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const commandNames = require('./commands').names;
const flagNames = require('./core-flags').names;

module.exports = {
commands: [...commandNames],
flags: [...flagNames],
allNames: [...commandNames, ...flagNames],
hasUnknownArgs: (args, ...names) =>
args.filter((e) => !names.includes(e) && !e.includes('--color') && e !== 'version' && e !== '-v' && !e.includes('help')),
};
16 changes: 9 additions & 7 deletions packages/webpack-cli/lib/webpack-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,19 +302,21 @@ class WebpackCLI extends GroupHelper {

runHelp(args) {
const HelpGroup = require('./groups/HelpGroup');
const commandNames = require('./utils/commands').names;
const flagNames = require('./utils/core-flags').names;
const allNames = [...commandNames, ...flagNames];
const { commands, allNames, hasUnknownArgs } = require('./utils/unknown-args');
const subject = allNames.filter((name) => {
return args.includes(name);
})[0];
const isCommand = commandNames.includes(subject);
return new HelpGroup().outputHelp(isCommand, subject);
const invalidArgs = hasUnknownArgs(args, ...allNames);
const isCommand = commands.includes(subject);
return new HelpGroup().outputHelp(isCommand, subject, invalidArgs);
}

runVersion(externalPkg) {
runVersion(args, externalPkg) {
const HelpGroup = require('./groups/HelpGroup');
return new HelpGroup().outputVersion(externalPkg);
const { commands, allNames, hasUnknownArgs } = require('./utils/unknown-args');
const commandsUsed = args.filter((val) => commands.includes(val));
const invalidArgs = hasUnknownArgs(args, ...allNames);
return new HelpGroup().outputVersion(externalPkg, commandsUsed, invalidArgs);
}
}

Expand Down
16 changes: 7 additions & 9 deletions test/help/help-commands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ const { run } = require('../utils/test-utils');
const helpHeader = 'The build tool for modern web applications';

describe('commands help', () => {
it('shows default help with invalid command', () => {
const { stdout, stderr } = run(__dirname, ['--help', 'myCommand'], false);
expect(stdout).toContain(helpHeader);
expect(stderr).toHaveLength(0);
it('throws error for invalid command with --help flag', () => {
const { stderr } = run(__dirname, ['--help', 'myCommand'], false);
expect(stderr).toContain(`You provided an invalid option 'myCommand'`);
});
it('shows command help with valid command', () => {
const { stdout, stderr } = run(__dirname, ['--help', 'init'], false);
expect(stdout).not.toContain(helpHeader);
expect(stdout).toContain('webpack init | init <scaffold>');
expect(stderr).toHaveLength(0);

it('throws error for invalid command with help command', () => {
const { stderr } = run(__dirname, ['help', 'myCommand'], false);
expect(stderr).toContain(`You provided an invalid option 'myCommand'`);
});

it('gives precedence to earlier command in case of multiple commands', () => {
Expand Down
13 changes: 9 additions & 4 deletions test/help/help-flags.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ const { run } = require('../utils/test-utils');
const helpHeader = 'The build tool for modern web applications';

describe('commands help', () => {
it('shows default help with invalid flag', () => {
const { stdout, stderr } = run(__dirname, ['--help', '--my-flag'], false);
expect(stdout).toContain(helpHeader);
expect(stderr).toHaveLength(0);
it('throws error for invalid flag with --help flag', () => {
const { stderr } = run(__dirname, ['--help', '--my-flag'], false);
expect(stderr).toContain(`You provided an invalid option '--my-flag'`);
});

it('throws error for invalid flag with help command', () => {
const { stderr } = run(__dirname, ['help', '--my-flag'], false);
expect(stderr).toContain(`You provided an invalid option '--my-flag'`);
});

it('shows flag help with valid flag', () => {
const { stdout, stderr } = run(__dirname, ['--help', '--merge'], false);
expect(stdout).not.toContain(helpHeader);
Expand Down
31 changes: 16 additions & 15 deletions test/help/help-multi-args.test.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
'use strict';

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

it('outputs info with multiple arguments using dashes and with precedence', () => {
const { stdout, stderr } = run(__dirname, ['--target', 'browser', '--help']);
expect(stdout).toContain(outputDescription);
expect(stderr).toHaveLength(0);
describe('help cmd with multiple arguments', () => {
commands.forEach((cmd) => {
it(`shows cmd help with ${cmd.name}`, () => {
const { stdout, stderr } = run(__dirname, ['--help', `${cmd.name}`], false);
expect(stdout).not.toContain(helpHeader);
expect(stdout).toContain(`${cmd.name}`);
expect(stdout).toContain(`${cmd.usage}`);
expect(stdout).toContain(`${cmd.description}`);
expect(stderr).toHaveLength(0);
});
});

it('outputs info with multiple commands and with precedence', () => {
const { stdout, stderr } = run(__dirname, ['init', 'help']);
expect(stdout).toContain(createDescription);
it('should output help for --version by taking precedence', () => {
const { stdout, stderr } = run(__dirname, ['--help', '--version'], false);
expect(stdout).not.toContain(helpHeader);
expect(stdout).toContain('webpack --version');
expect(stderr).toHaveLength(0);
});
});
1 change: 1 addition & 0 deletions test/help/help-single-arg.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ describe('single help flag', () => {
expect(stdout).toContain(example);
expect(stderr).toHaveLength(0);
});

it('outputs help info with command syntax', () => {
const { stdout, stderr } = run(__dirname, ['help'], false);
expect(stdout).toContain(helpHeader);
Expand Down
28 changes: 23 additions & 5 deletions test/version/version-external-packages.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,53 @@ const cliPkgJSON = require('../../packages/webpack-cli/package.json');

describe('version flag with external packages', () => {
it('outputs version with init', () => {
const { stdout, stderr } = run(__dirname, ['init', '--version']);
const { stdout, stderr } = run(__dirname, ['init', '--version'], false);
expect(stdout).toContain(initPkgJSON.version);
expect(stdout).toContain(cliPkgJSON.version);
expect(stderr).toHaveLength(0);
});

it('outputs version with info', () => {
const { stdout, stderr } = run(__dirname, ['info', '--version']);
const { stdout, stderr } = run(__dirname, ['info', '--version'], false);
expect(stdout).toContain(infoPkgJSON.version);
expect(stdout).toContain(cliPkgJSON.version);
expect(stderr).toHaveLength(0);
});

it('outputs version with serve', () => {
const { stdout, stderr } = run(__dirname, ['serve', '--version']);
const { stdout, stderr } = run(__dirname, ['serve', '--version'], false);
expect(stdout).toContain(servePkgJSON.version);
expect(stdout).toContain(cliPkgJSON.version);
expect(stderr).toHaveLength(0);
});

it('outputs version with migrate', () => {
const { stdout, stderr } = run(__dirname, ['migrate', '--version']);
const { stdout, stderr } = run(__dirname, ['migrate', '--version'], false);
expect(stdout).toContain(migratePkgJSON.version);
expect(stdout).toContain(cliPkgJSON.version);
expect(stderr).toHaveLength(0);
});

it(' should throw error for multiple commands', () => {
const { stderr } = run(__dirname, ['init', 'migrate', '--version']);
const { stderr } = run(__dirname, ['init', 'migrate', '--version'], false);
expect(stderr).toContain('You provided multiple commands.');
});

it(' should throw error if invalid argument is present with --version flag', () => {
const { stderr, stdout } = run(__dirname, ['init', 'abc', '--version'], false);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});

it(' should throw error if invalid argument is present with version command', () => {
const { stderr, stdout } = run(__dirname, ['init', 'abc', 'version'], false);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});

it(' should throw error if invalid argument is present with -v alias', () => {
const { stderr, stdout } = run(__dirname, ['init', 'abc', '-v'], false);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});
});
33 changes: 21 additions & 12 deletions test/version/version-multi-args.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,8 @@ const { run } = require('../utils/test-utils');
const pkgJSON = require('../../packages/webpack-cli/package.json');

describe('version flag with multiple arguments', () => {
it('outputs version with mixed syntax', () => {
const { stdout, stderr } = run(__dirname, ['--version', '--target', 'browser']);
expect(stdout).toContain(pkgJSON.version);
expect(stderr).toHaveLength(0);
});

it('does not output version with help command', () => {
const { stdout, stderr } = run(__dirname, ['version', 'help']);
const { stdout, stderr } = run(__dirname, ['version', 'help'], false);
expect(stdout).not.toContain(pkgJSON.version);

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

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

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

it('outputs version with multiple dashed args and has precedence', () => {
const { stdout, stderr } = run(__dirname, ['--target', 'browser', '--version']);
expect(stdout).toContain(pkgJSON.version);
expect(stderr).toHaveLength(0);
it('throws error if invalid arg is passed with version command', () => {
const { stdout, stderr } = run(__dirname, ['version', 'abc'], false);
expect(stdout).not.toContain(pkgJSON.version);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});

it('throws error if invalid arg is passed with --version flag', () => {
const { stdout, stderr } = run(__dirname, ['--version', 'abc'], false);
expect(stdout).not.toContain(pkgJSON.version);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});

it('throws error if invalid arg is passed with -v alias', () => {
const { stdout, stderr } = run(__dirname, ['-v', 'abc'], false);
expect(stdout).not.toContain(pkgJSON.version);
expect(stderr).toContain(`Error: Invalid Option 'abc'`);
expect(stdout).toContain('Run webpack --help to see available commands and arguments');
});
});
6 changes: 3 additions & 3 deletions test/version/version-single-arg.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@ const pkgJSON = require('../../packages/webpack-cli/package.json');

describe('single version flag', () => {
it('outputs versions with command syntax', () => {
const { stdout, stderr } = run(__dirname, ['version']);
const { stdout, stderr } = run(__dirname, ['version'], false);
expect(stdout).toContain(pkgJSON.version);
expect(stderr).toHaveLength(0);
});

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

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

0 comments on commit ffc896b

Please sign in to comment.