Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: the --help option is working without webpack-dev-server #2267

Merged
merged 5 commits into from Dec 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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