Skip to content

Commit 800c46e

Browse files
authored
Merge pull request webpack#7252 from webpack/feat-cli-support-webpack-command
feat(cli): support `webpack-command`
2 parents 4c8a8fd + 4c12b79 commit 800c46e

File tree

2 files changed

+128
-44
lines changed

2 files changed

+128
-44
lines changed

bin/webpack.js

Lines changed: 128 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
#!/usr/bin/env node
2-
function runCommand(command, options) {
2+
3+
process.exitCode = 0;
4+
5+
/**
6+
* @param {string} command process to run
7+
* @param {string[]} args commandline arguments
8+
* @returns {Promise<void>} promise
9+
*/
10+
const runCommand = (command, args) => {
311
const cp = require("child_process");
412
return new Promise((resolve, reject) => {
5-
const executedCommand = cp.spawn(command, options, {
13+
const executedCommand = cp.spawn(command, args, {
614
stdio: "inherit",
715
shell: true
816
});
@@ -13,69 +21,146 @@ function runCommand(command, options) {
1321

1422
executedCommand.on("exit", code => {
1523
if (code === 0) {
16-
resolve(true);
24+
resolve();
1725
} else {
1826
reject();
1927
}
2028
});
2129
});
22-
}
30+
};
2331

24-
let webpackCliInstalled = false;
25-
try {
26-
require.resolve("webpack-cli");
27-
webpackCliInstalled = true;
28-
} catch (err) {
29-
webpackCliInstalled = false;
30-
}
32+
/**
33+
* @param {string} packageName name of the package
34+
* @returns {boolean} is the package installed?
35+
*/
36+
const isInstalled = packageName => {
37+
try {
38+
require.resolve(packageName);
3139

32-
if (!webpackCliInstalled) {
40+
return true;
41+
} catch (err) {
42+
return false;
43+
}
44+
};
45+
46+
/**
47+
* @typedef {Object} CliOption
48+
* @property {string} name display name
49+
* @property {string} package npm package name
50+
* @property {string} alias shortcut for choice
51+
* @property {boolean} installed currently installed?
52+
* @property {string} url homepage
53+
* @property {string} description description
54+
*/
55+
56+
/** @type {CliOption[]} */
57+
const CLIs = [
58+
{
59+
name: "webpack-cli",
60+
package: "webpack-cli",
61+
alias: "cli",
62+
installed: isInstalled("webpack-cli"),
63+
url: "https://github.com/webpack/webpack-cli",
64+
description: "The original webpack full-featured CLI."
65+
},
66+
{
67+
name: "webpack-command",
68+
package: "webpack-command",
69+
alias: "command",
70+
installed: isInstalled("webpack-command"),
71+
url: "https://github.com/webpack-contrib/webpack-command",
72+
description: "A lightweight, opinionated webpack CLI."
73+
}
74+
];
75+
76+
const installedClis = CLIs.filter(cli => cli.installed);
77+
78+
if (installedClis.length === 0) {
3379
const path = require("path");
3480
const fs = require("fs");
3581
const readLine = require("readline");
36-
const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));
3782

38-
const packageManager = isYarn ? "yarn" : "npm";
39-
const options = ["install", "-D", "webpack-cli"];
83+
let notify =
84+
"One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:";
4085

41-
if (isYarn) {
42-
options[0] = "add";
86+
for (const item of CLIs) {
87+
notify += `\n - ${item.name} (${item.url})\n ${item.description}`;
4388
}
4489

45-
const commandToBeRun = `${packageManager} ${options.join(" ")}`;
90+
console.error(notify);
4691

47-
const question = `Would you like to install webpack-cli? (That will run ${commandToBeRun}) (yes/NO)`;
92+
const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));
93+
94+
const packageManager = isYarn ? "yarn" : "npm";
95+
const installOptions = [isYarn ? "add" : "install", "-D"];
96+
97+
console.error(
98+
`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
99+
" "
100+
)}".`
101+
);
102+
103+
let question = `Which one do you like to install (${CLIs.map(
104+
item => item.name
105+
).join("/")}):\n`;
48106

49-
console.error("The CLI moved into a separate package: webpack-cli");
50107
const questionInterface = readLine.createInterface({
51108
input: process.stdin,
52-
output: process.stdout
109+
output: process.stderr
53110
});
54111
questionInterface.question(question, answer => {
55112
questionInterface.close();
56-
switch (answer.toLowerCase()) {
57-
case "y":
58-
case "yes":
59-
case "1": {
60-
runCommand(packageManager, options)
61-
.then(result => {
62-
return require("webpack-cli"); //eslint-disable-line
63-
})
64-
.catch(error => {
65-
console.error(error);
66-
process.exitCode = 1;
67-
});
68-
break;
69-
}
70-
default: {
71-
console.error(
72-
"It needs to be installed alongside webpack to use the CLI"
73-
);
74-
process.exitCode = 1;
75-
break;
76-
}
113+
114+
const normalizedAnswer = answer.toLowerCase();
115+
const selectedPackage = CLIs.find(item => {
116+
return item.name === normalizedAnswer || item.alias === normalizedAnswer;
117+
});
118+
119+
if (!normalizedAnswer) {
120+
console.error(
121+
"One CLI needs to be installed alongside webpack to use the CLI."
122+
);
123+
process.exitCode = 1;
124+
125+
return;
126+
} else if (!selectedPackage) {
127+
console.error(
128+
"No matching choice.\n" +
129+
"One CLI needs to be installed alongside webpack to use the CLI.\n" +
130+
"Try to installing your CLI of choice manually."
131+
);
132+
process.exitCode = 1;
133+
134+
return;
77135
}
136+
137+
const packageName = selectedPackage.package;
138+
139+
console.log(
140+
`Installing '${
141+
selectedPackage.name
142+
}' (running '${packageManager} ${installOptions.join(
143+
" "
144+
)} ${packageName}')...`
145+
);
146+
147+
runCommand(packageManager, installOptions.concat(packageName))
148+
.then(() => {
149+
require(packageName); //eslint-disable-line
150+
})
151+
.catch(error => {
152+
console.error(error);
153+
process.exitCode = 1;
154+
});
78155
});
156+
} else if (installedClis.length === 1) {
157+
require(installedClis[0].package); // eslint-disable-line
79158
} else {
80-
require("webpack-cli"); // eslint-disable-line
159+
console.warn(
160+
`You have installed ${installedClis
161+
.map(item => item.name)
162+
.join(
163+
" and "
164+
)} together. To work with the "webpack" command you need only one CLI package, please remove one of them or use them directly via their binary.`
165+
);
81166
}

declarations.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
declare module "*.json";
2-
declare module "webpack-cli";
32

43
// Deprecated NodeJS API usages in Webpack
54
declare namespace NodeJS {

0 commit comments

Comments
 (0)