Skip to content

Commit

Permalink
fix: the --help option is working without webpack-dev-server (#2267)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-akait committed Dec 28, 2020
1 parent 952a188 commit 1dae54d
Show file tree
Hide file tree
Showing 13 changed files with 129 additions and 116 deletions.
4 changes: 2 additions & 2 deletions packages/generators/src/index.ts
Expand Up @@ -5,10 +5,10 @@ import addonGenerator from './addon-generator';
import initGenerator from './init-generator';

class GeneratorsCommand {
apply(cli): void {
async apply(cli): Promise<void> {
const { logger } = cli;

cli.makeCommand(
await cli.makeCommand(
{
name: 'loader [output-path]',
alias: 'l',
Expand Down
4 changes: 2 additions & 2 deletions packages/info/src/index.ts
Expand Up @@ -30,8 +30,8 @@ const DEFAULT_DETAILS: Information = {
};

class InfoCommand {
apply(cli): void {
cli.makeCommand(
async apply(cli): Promise<void> {
await cli.makeCommand(
{
name: 'info',
alias: 'i',
Expand Down
4 changes: 2 additions & 2 deletions packages/init/src/index.ts
Expand Up @@ -2,8 +2,8 @@ import { initGenerator } from '@webpack-cli/generators';
import { modifyHelperUtil, npmPackagesExists } from '@webpack-cli/utils';

class InitCommand {
apply(cli): void {
cli.makeCommand(
async apply(cli): Promise<void> {
await cli.makeCommand(
{
name: 'init [scaffold...]',
alias: 'c',
Expand Down
4 changes: 2 additions & 2 deletions packages/migrate/src/index.ts
Expand Up @@ -149,10 +149,10 @@ function runMigration(currentConfigPath: string, outputConfigPath: string, logge
}

class MigrationCommand {
apply(cli): void {
async apply(cli): Promise<void> {
const { logger } = cli;

cli.makeCommand(
await cli.makeCommand(
{
name: 'migrate <config-path> [new-config-path]',
alias: 'm',
Expand Down
62 changes: 30 additions & 32 deletions packages/serve/src/index.ts
Expand Up @@ -2,45 +2,43 @@ import startDevServer from './startDevServer';

class ServeCommand {
async apply(cli): Promise<void> {
const { logger, utils } = cli;
const isPackageExist = utils.getPkg('webpack-dev-server');

if (!isPackageExist) {
try {
await utils.promptInstallation('webpack-dev-server', () => {
// TODO colors
logger.error("For using this command you need to install: 'webpack-dev-server' package");
});
} catch (error) {
logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands.");
process.exit(2);
}
}

let devServerFlags = [];

try {
// eslint-disable-next-line node/no-extraneous-require
require('webpack-dev-server');
// eslint-disable-next-line node/no-extraneous-require
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
} catch (err) {
logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${err}`);
process.exit(2);
}

const builtInOptions = cli.getBuiltInOptions();

cli.makeCommand(
const { logger } = cli;

await cli.makeCommand(
{
name: 'serve',
alias: 's',
description: 'Run the webpack dev server.',
usage: '[options]',
pkg: '@webpack-cli/serve',
dependencies: ['webpack-dev-server'],
},
() => {
let devServerFlags = [];

try {
// eslint-disable-next-line
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
} catch (error) {
logger.error(`You need to install 'webpack-dev-server' for running 'webpack serve'.\n${error}`);
process.exit(2);
}

const builtInOptions = cli.getBuiltInOptions();

return [...builtInOptions, ...devServerFlags];
},
[...builtInOptions, ...devServerFlags],
async (program) => {
const builtInOptions = cli.getBuiltInOptions();
let devServerFlags = [];

try {
// eslint-disable-next-line
devServerFlags = require('webpack-dev-server/bin/cli-flags').devServer;
} catch (error) {
// Nothing, to prevent future updates
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const webpackOptions: Record<string, any> = {};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -89,7 +87,7 @@ class ServeCommand {
let servers;

if (cli.needWatchStdin(compiler) || devServerOptions.stdin) {
// TODO
// TODO remove in the next major release
// Compatibility with old `stdin` option for `webpack-dev-server`
// Should be removed for the next major release on both sides
if (devServerOptions.stdin) {
Expand Down
10 changes: 4 additions & 6 deletions packages/webpack-cli/lib/utils/prompt-installation.js
Expand Up @@ -18,23 +18,21 @@ async function promptInstallation(packageName, preMessage) {
process.exit(2);
}

// yarn uses 'add' command, rest npm and pnpm both use 'install'
const options = [packageManager === 'yarn' ? 'add' : 'install', '-D', packageName];

const commandToBeRun = `${packageManager} ${options.join(' ')}`;

if (preMessage) {
preMessage();
}

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

let installConfirm;

try {
({ installConfirm } = await prompt([
{
type: 'confirm',
name: 'installConfirm',
message: `Would you like to install '${packageName}' package? (That will run '${green(commandToBeRun)}')`,
message: `Would you like to install '${green(packageName)}' package? (That will run '${green(commandToBeRun)}')`,
initial: 'Y',
stdout: process.stderr,
},
Expand Down
98 changes: 66 additions & 32 deletions packages/webpack-cli/lib/webpack-cli.js
Expand Up @@ -29,8 +29,8 @@ class WebpackCLI {
this.utils = { toKebabCase, getPkg, promptInstallation };
}

makeCommand(commandOptions, optionsForCommand = [], action) {
const command = program.command(commandOptions.name, {
async makeCommand(commandOptions, options, action) {
const command = this.program.command(commandOptions.name, {
noHelp: commandOptions.noHelp,
hidden: commandOptions.hidden,
isDefault: commandOptions.isDefault,
Expand All @@ -56,8 +56,50 @@ class WebpackCLI {
command.pkg = 'webpack-cli';
}

if (optionsForCommand.length > 0) {
optionsForCommand.forEach((optionForCommand) => {
const { forHelp } = this.program;

let allDependenciesInstalled = true;

if (commandOptions.dependencies && commandOptions.dependencies.length > 0) {
for (const dependency of commandOptions.dependencies) {
const isPkgExist = getPkg(dependency);

if (isPkgExist) {
continue;
} else if (!isPkgExist && forHelp) {
allDependenciesInstalled = false;
continue;
}

try {
await promptInstallation(dependency, () => {
logger.error(
`For using '${green(commandOptions.name)}' command you need to install: '${green(dependency)}' package`,
);
});
} catch (error) {
logger.error("Action Interrupted, use 'webpack-cli help' to see possible commands.");
logger.error(error);
process.exit(2);
}
}
}

if (options) {
if (typeof options === 'function') {
if (forHelp && !allDependenciesInstalled) {
command.description(
`${commandOptions.description} To see all available options you need to install ${commandOptions.dependencies
.map((dependency) => `'${dependency}'`)
.join(',')}.`,
);
options = [];
} else {
options = options();
}
}

options.forEach((optionForCommand) => {
this.makeOption(command, optionForCommand);
});
}
Expand Down Expand Up @@ -271,29 +313,11 @@ class WebpackCLI {
await this.bundleCommand(options);
});
} else if (commandName === helpCommandOptions.name || commandName === helpCommandOptions.alias) {
this.makeCommand(
{
name: 'help [command]',
alias: 'h',
description: 'Display help for commands and options',
usage: '[command]',
},
[],
// Stub for the `help` command
() => {},
);
// Stub for the `help` command
this.makeCommand(helpCommandOptions, [], () => {});
} else if (commandName === versionCommandOptions.name || commandName === helpCommandOptions.alias) {
this.makeCommand(
{
name: 'version [commands...]',
alias: 'v',
description: "Output the version number of 'webpack', 'webpack-cli' and 'webpack-dev-server' and commands",
usage: '[commands...]',
},
[],
// Stub for the `help` command
() => {},
);
// Stub for the `help` command
this.makeCommand(versionCommandOptions, [], () => {});
} else {
const builtInExternalCommandInfo = externalBuiltInCommandsInfo.find(
(externalBuiltInCommandInfo) =>
Expand All @@ -310,11 +334,7 @@ class WebpackCLI {

if (pkg !== 'webpack-cli' && !getPkg(pkg)) {
if (!allowToInstall) {
const isOptions = commandName.startsWith('-');

logger.error(`Unknown ${isOptions ? 'option' : 'command'} '${commandName}'`);
logger.error("Run 'webpack --help' to see available commands and options");
process.exit(2);
return;
}

try {
Expand Down Expand Up @@ -464,6 +484,12 @@ class WebpackCLI {
(command) => command.name() === possibleCommandName || command.alias() === possibleCommandName,
);

if (!foundCommand) {
logger.error(`Unknown command '${possibleCommandName}'`);
logger.error("Run 'webpack --help' to see available commands and options");
process.exit(2);
}

try {
const { name, version } = require(`${foundCommand.pkg}/package.json`);

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

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

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

if (name.startsWith('-')) {
logger.error(`Unknown option '${name}'`);
logger.error("Run 'webpack --help' to see available commands and options");
process.exit(2);
}

optionsWithoutCommandName.forEach((option) => {
logger.error(`Unknown option '${option}'`);
logger.error("Run 'webpack --help' to see available commands and options");
Expand Down Expand Up @@ -636,6 +668,8 @@ class WebpackCLI {
}
}

this.program.forHelp = true;

const optionsForHelp = [].concat(opts.help && !isDefault ? [commandName] : []).concat(options);

await outputHelp(optionsForHelp, isVerbose, program);
Expand Down
4 changes: 2 additions & 2 deletions test/help/help.test.js
Expand Up @@ -219,7 +219,7 @@ describe('help', () => {
const { exitCode, stderr, stdout } = run(__dirname, ['help', 'myCommand'], false);

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

expect(exitCode).toBe(2);
expect(stderr).toContain("Unknown command 'verbose'");
expect(stderr).toContain("Can't find and load command 'verbose'");
expect(stderr).toContain("Run 'webpack --help' to see available commands and options");
expect(stdout).toBeFalsy();
});
Expand Down
18 changes: 0 additions & 18 deletions test/optimization/optimization.test.js

This file was deleted.

1 change: 0 additions & 1 deletion test/optimization/src/index.js

This file was deleted.

8 changes: 0 additions & 8 deletions test/optimization/webpack.config.js

This file was deleted.

16 changes: 13 additions & 3 deletions test/utils/test-utils.js
Expand Up @@ -8,12 +8,22 @@ const { Writable } = require('readable-stream');
const concat = require('concat-stream');
const { version } = require('webpack');
const stripAnsi = require('strip-ansi');
const { version: devServerVersion } = require('webpack-dev-server/package.json');

const isWebpack5 = version.startsWith('5');

let devServerVersion;

try {
// eslint-disable-next-line
devServerVersion = require('webpack-dev-server/package.json').version;
} catch (error) {
// Nothing
}

const isDevServer4 = devServerVersion && devServerVersion.startsWith('4');

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

const hyphenToUpperCase = (name) => {
Expand Down

0 comments on commit 1dae54d

Please sign in to comment.