Skip to content

Commit 1dae54d

Browse files
fix: the --help option is working without webpack-dev-server (#2267)
1 parent 952a188 commit 1dae54d

File tree

13 files changed

+129
-116
lines changed

13 files changed

+129
-116
lines changed

packages/generators/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import addonGenerator from './addon-generator';
55
import initGenerator from './init-generator';
66

77
class GeneratorsCommand {
8-
apply(cli): void {
8+
async apply(cli): Promise<void> {
99
const { logger } = cli;
1010

11-
cli.makeCommand(
11+
await cli.makeCommand(
1212
{
1313
name: 'loader [output-path]',
1414
alias: 'l',

packages/info/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ const DEFAULT_DETAILS: Information = {
3030
};
3131

3232
class InfoCommand {
33-
apply(cli): void {
34-
cli.makeCommand(
33+
async apply(cli): Promise<void> {
34+
await cli.makeCommand(
3535
{
3636
name: 'info',
3737
alias: 'i',

packages/init/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import { initGenerator } from '@webpack-cli/generators';
22
import { modifyHelperUtil, npmPackagesExists } from '@webpack-cli/utils';
33

44
class InitCommand {
5-
apply(cli): void {
6-
cli.makeCommand(
5+
async apply(cli): Promise<void> {
6+
await cli.makeCommand(
77
{
88
name: 'init [scaffold...]',
99
alias: 'c',

packages/migrate/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,10 @@ function runMigration(currentConfigPath: string, outputConfigPath: string, logge
149149
}
150150

151151
class MigrationCommand {
152-
apply(cli): void {
152+
async apply(cli): Promise<void> {
153153
const { logger } = cli;
154154

155-
cli.makeCommand(
155+
await cli.makeCommand(
156156
{
157157
name: 'migrate <config-path> [new-config-path]',
158158
alias: 'm',

packages/serve/src/index.ts

+30-32
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,43 @@ import startDevServer from './startDevServer';
22

33
class ServeCommand {
44
async apply(cli): Promise<void> {
5-
const { logger, utils } = cli;
6-
const isPackageExist = utils.getPkg('webpack-dev-server');
7-
8-
if (!isPackageExist) {
9-
try {
10-
await utils.promptInstallation('webpack-dev-server', () => {
11-
// TODO colors
12-
logger.error("For using this command you need to install: 'webpack-dev-server' package");
13-
});
14-
} catch (error) {
15-
logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands.");
16-
process.exit(2);
17-
}
18-
}
19-
20-
let devServerFlags = [];
21-
22-
try {
23-
// eslint-disable-next-line node/no-extraneous-require
24-
require('webpack-dev-server');
25-
// eslint-disable-next-line node/no-extraneous-require
26-
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
27-
} catch (err) {
28-
logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`);
29-
process.exit(2);
30-
}
31-
32-
const builtInOptions = cli.getBuiltInOptions();
33-
34-
cli.makeCommand(
5+
const { logger } = cli;
6+
7+
await cli.makeCommand(
358
{
369
name: 'serve',
3710
alias: 's',
3811
description: 'Run the webpack dev server.',
3912
usage: '[options]',
4013
pkg: '@webpack-cli/serve',
14+
dependencies: ['webpack-dev-server'],
15+
},
16+
() => {
17+
let devServerFlags = [];
18+
19+
try {
20+
// eslint-disable-next-line
21+
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
22+
} catch (error) {
23+
logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${error}`);
24+
process.exit(2);
25+
}
26+
27+
const builtInOptions = cli.getBuiltInOptions();
28+
29+
return [...builtInOptions, ...devServerFlags];
4130
},
42-
[...builtInOptions, ...devServerFlags],
4331
async (program) => {
32+
const builtInOptions = cli.getBuiltInOptions();
33+
let devServerFlags = [];
34+
35+
try {
36+
// eslint-disable-next-line
37+
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
38+
} catch (error) {
39+
// Nothing, to prevent future updates
40+
}
41+
4442
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4543
const webpackOptions: Record<string, any> = {};
4644
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -89,7 +87,7 @@ class ServeCommand {
8987
let servers;
9088

9189
if (cli.needWatchStdin(compiler) || devServerOptions.stdin) {
92-
// TODO
90+
// TODO remove in the next major release
9391
// Compatibility with old `stdin` option for `webpack-dev-server`
9492
// Should be removed for the next major release on both sides
9593
if (devServerOptions.stdin) {

packages/webpack-cli/lib/utils/prompt-installation.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,21 @@ async function promptInstallation(packageName, preMessage) {
1818
process.exit(2);
1919
}
2020

21-
// yarn uses 'add' command, rest npm and pnpm both use 'install'
22-
const options = [packageManager === 'yarn' ? 'add' : 'install', '-D', packageName];
23-
24-
const commandToBeRun = `${packageManager} ${options.join(' ')}`;
25-
2621
if (preMessage) {
2722
preMessage();
2823
}
2924

25+
// yarn uses 'add' command, rest npm and pnpm both use 'install'
26+
const commandToBeRun = `${packageManager} ${[packageManager === 'yarn' ? 'add' : 'install', '-D', packageName].join(' ')}`;
27+
3028
let installConfirm;
3129

3230
try {
3331
({ installConfirm } = await prompt([
3432
{
3533
type: 'confirm',
3634
name: 'installConfirm',
37-
message: `Would you like to install '${packageName}' package? (That will run '${green(commandToBeRun)}')`,
35+
message: `Would you like to install '${green(packageName)}' package? (That will run '${green(commandToBeRun)}')`,
3836
initial: 'Y',
3937
stdout: process.stderr,
4038
},

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

+66-32
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class WebpackCLI {
2929
this.utils = { toKebabCase, getPkg, promptInstallation };
3030
}
3131

32-
makeCommand(commandOptions, optionsForCommand = [], action) {
33-
const command = program.command(commandOptions.name, {
32+
async makeCommand(commandOptions, options, action) {
33+
const command = this.program.command(commandOptions.name, {
3434
noHelp: commandOptions.noHelp,
3535
hidden: commandOptions.hidden,
3636
isDefault: commandOptions.isDefault,
@@ -56,8 +56,50 @@ class WebpackCLI {
5656
command.pkg = 'webpack-cli';
5757
}
5858

59-
if (optionsForCommand.length > 0) {
60-
optionsForCommand.forEach((optionForCommand) => {
59+
const { forHelp } = this.program;
60+
61+
let allDependenciesInstalled = true;
62+
63+
if (commandOptions.dependencies && commandOptions.dependencies.length > 0) {
64+
for (const dependency of commandOptions.dependencies) {
65+
const isPkgExist = getPkg(dependency);
66+
67+
if (isPkgExist) {
68+
continue;
69+
} else if (!isPkgExist && forHelp) {
70+
allDependenciesInstalled = false;
71+
continue;
72+
}
73+
74+
try {
75+
await promptInstallation(dependency, () => {
76+
logger.error(
77+
`For using '${green(commandOptions.name)}' command you need to install: '${green(dependency)}' package`,
78+
);
79+
});
80+
} catch (error) {
81+
logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands.");
82+
logger.error(error);
83+
process.exit(2);
84+
}
85+
}
86+
}
87+
88+
if (options) {
89+
if (typeof options === 'function') {
90+
if (forHelp && !allDependenciesInstalled) {
91+
command.description(
92+
`${commandOptions.description} To see all available options you need to install ${commandOptions.dependencies
93+
.map((dependency) => `'${dependency}'`)
94+
.join(',')}.`,
95+
);
96+
options = [];
97+
} else {
98+
options = options();
99+
}
100+
}
101+
102+
options.forEach((optionForCommand) => {
61103
this.makeOption(command, optionForCommand);
62104
});
63105
}
@@ -271,29 +313,11 @@ class WebpackCLI {
271313
await this.bundleCommand(options);
272314
});
273315
} else if (commandName === helpCommandOptions.name || commandName === helpCommandOptions.alias) {
274-
this.makeCommand(
275-
{
276-
name: 'help [command]',
277-
alias: 'h',
278-
description: 'Display help for commands and options',
279-
usage: '[command]',
280-
},
281-
[],
282-
// Stub for the `help` command
283-
() => {},
284-
);
316+
// Stub for the `help` command
317+
this.makeCommand(helpCommandOptions, [], () => {});
285318
} else if (commandName === versionCommandOptions.name || commandName === helpCommandOptions.alias) {
286-
this.makeCommand(
287-
{
288-
name: 'version [commands...]',
289-
alias: 'v',
290-
description: "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands",
291-
usage: '[commands...]',
292-
},
293-
[],
294-
// Stub for the `help` command
295-
() => {},
296-
);
319+
// Stub for the `help` command
320+
this.makeCommand(versionCommandOptions, [], () => {});
297321
} else {
298322
const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find(
299323
(externalBuiltInCommandInfo) =>
@@ -310,11 +334,7 @@ class WebpackCLI {
310334

311335
if (pkg !== 'webpack-cli' && !getPkg(pkg)) {
312336
if (!allowToInstall) {
313-
const isOptions = commandName.startsWith('-');
314-
315-
logger.error(`Unknown ${isOptions ? 'option' : 'command'} '${commandName}'`);
316-
logger.error("Run 'webpack --help' to see available commands and options");
317-
process.exit(2);
337+
return;
318338
}
319339

320340
try {
@@ -464,6 +484,12 @@ class WebpackCLI {
464484
(command) => command.name() === possibleCommandName || command.alias() === possibleCommandName,
465485
);
466486

487+
if (!foundCommand) {
488+
logger.error(`Unknown command '${possibleCommandName}'`);
489+
logger.error("Run 'webpack --help' to see available commands and options");
490+
process.exit(2);
491+
}
492+
467493
try {
468494
const { name, version } = require(`${foundCommand.pkg}/package.json`);
469495

@@ -481,7 +507,7 @@ class WebpackCLI {
481507
logger.raw(`webpack-cli ${pkgJSON.version}`);
482508

483509
if (getPkg('webpack-dev-server')) {
484-
// eslint-disable-next-line node/no-extraneous-require
510+
// eslint-disable-next-line
485511
const { version } = require('webpack-dev-server/package.json');
486512

487513
logger.raw(`webpack-dev-server ${version}`);
@@ -547,6 +573,12 @@ class WebpackCLI {
547573
} else {
548574
const [name, ...optionsWithoutCommandName] = options;
549575

576+
if (name.startsWith('-')) {
577+
logger.error(`Unknown option '${name}'`);
578+
logger.error("Run 'webpack --help' to see available commands and options");
579+
process.exit(2);
580+
}
581+
550582
optionsWithoutCommandName.forEach((option) => {
551583
logger.error(`Unknown option '${option}'`);
552584
logger.error("Run 'webpack --help' to see available commands and options");
@@ -636,6 +668,8 @@ class WebpackCLI {
636668
}
637669
}
638670

671+
this.program.forHelp = true;
672+
639673
const optionsForHelp = [].concat(opts.help && !isDefault ? [commandName] : []).concat(options);
640674

641675
await outputHelp(optionsForHelp, isVerbose, program);

test/help/help.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ describe('help', () => {
219219
const { exitCode, stderr, stdout } = run(__dirname, ['help', 'myCommand'], false);
220220

221221
expect(exitCode).toBe(2);
222-
expect(stderr).toContain("Unknown command 'myCommand'");
222+
expect(stderr).toContain("Can't find and load command 'myCommand'");
223223
expect(stderr).toContain("Run 'webpack --help' to see available commands and options");
224224
expect(stdout).toBeFalsy();
225225
});
@@ -228,7 +228,7 @@ describe('help', () => {
228228
const { exitCode, stderr, stdout } = run(__dirname, ['help', 'verbose'], false);
229229

230230
expect(exitCode).toBe(2);
231-
expect(stderr).toContain("Unknown command 'verbose'");
231+
expect(stderr).toContain("Can't find and load command 'verbose'");
232232
expect(stderr).toContain("Run 'webpack --help' to see available commands and options");
233233
expect(stdout).toBeFalsy();
234234
});

test/optimization/optimization.test.js

-18
This file was deleted.

test/optimization/src/index.js

-1
This file was deleted.

test/optimization/webpack.config.js

-8
This file was deleted.

test/utils/test-utils.js

+13-3
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ const { Writable } = require('readable-stream');
88
const concat = require('concat-stream');
99
const { version } = require('webpack');
1010
const stripAnsi = require('strip-ansi');
11-
const { version: devServerVersion } = require('webpack-dev-server/package.json');
11+
12+
const isWebpack5 = version.startsWith('5');
13+
14+
let devServerVersion;
15+
16+
try {
17+
// eslint-disable-next-line
18+
devServerVersion = require('webpack-dev-server/package.json').version;
19+
} catch (error) {
20+
// Nothing
21+
}
22+
23+
const isDevServer4 = devServerVersion && devServerVersion.startsWith('4');
1224

1325
const WEBPACK_PATH = path.resolve(__dirname, '../../packages/webpack-cli/bin/cli.js');
1426
const ENABLE_LOG_COMPILATION = process.env.ENABLE_PIPE || false;
15-
const isWebpack5 = version.startsWith('5');
16-
const isDevServer4 = devServerVersion.startsWith('4');
1727
const isWindows = process.platform === 'win32';
1828

1929
const hyphenToUpperCase = (name) => {

0 commit comments

Comments
 (0)