UNPKG

3.9 kBJavaScriptView Raw
1#!/usr/bin/env node
2
3/**
4 * @param {string} command process to run
5 * @param {string[]} args command line arguments
6 * @returns {Promise<void>} promise
7 */
8const runCommand = (command, args) => {
9 const cp = require("child_process");
10 return new Promise((resolve, reject) => {
11 const executedCommand = cp.spawn(command, args, {
12 stdio: "inherit",
13 shell: true
14 });
15
16 executedCommand.on("error", error => {
17 reject(error);
18 });
19
20 executedCommand.on("exit", code => {
21 if (code === 0) {
22 resolve();
23 } else {
24 reject();
25 }
26 });
27 });
28};
29
30/**
31 * @param {string} packageName name of the package
32 * @returns {boolean} is the package installed?
33 */
34const isInstalled = packageName => {
35 if (process.versions.pnp) {
36 return true;
37 }
38
39 const path = require("path");
40 const fs = require("graceful-fs");
41
42 let dir = __dirname;
43
44 do {
45 try {
46 if (
47 fs.statSync(path.join(dir, "node_modules", packageName)).isDirectory()
48 ) {
49 return true;
50 }
51 } catch (_error) {
52 // Nothing
53 }
54 } while (dir !== (dir = path.dirname(dir)));
55
56 return false;
57};
58
59/**
60 * @param {CliOption} cli options
61 * @returns {void}
62 */
63const runCli = cli => {
64 const path = require("path");
65 const pkgPath = require.resolve(`${cli.package}/package.json`);
66 // eslint-disable-next-line node/no-missing-require
67 const pkg = require(pkgPath);
68 // eslint-disable-next-line node/no-missing-require
69 require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
70};
71
72/**
73 * @typedef {Object} CliOption
74 * @property {string} name display name
75 * @property {string} package npm package name
76 * @property {string} binName name of the executable file
77 * @property {boolean} installed currently installed?
78 * @property {string} url homepage
79 */
80
81/** @type {CliOption} */
82const cli = {
83 name: "webpack-cli",
84 package: "webpack-cli",
85 binName: "webpack-cli",
86 installed: isInstalled("webpack-cli"),
87 url: "https://github.com/webpack/webpack-cli"
88};
89
90if (!cli.installed) {
91 const path = require("path");
92 const fs = require("graceful-fs");
93 const readLine = require("readline");
94
95 const notify =
96 "CLI for webpack must be installed.\n" + ` ${cli.name} (${cli.url})\n`;
97
98 console.error(notify);
99
100 let packageManager;
101
102 if (fs.existsSync(path.resolve(process.cwd(), "yarn.lock"))) {
103 packageManager = "yarn";
104 } else if (fs.existsSync(path.resolve(process.cwd(), "pnpm-lock.yaml"))) {
105 packageManager = "pnpm";
106 } else {
107 packageManager = "npm";
108 }
109
110 const installOptions = [packageManager === "yarn" ? "add" : "install", "-D"];
111
112 console.error(
113 `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
114 " "
115 )} ${cli.package}".`
116 );
117
118 const question = `Do you want to install 'webpack-cli' (yes/no): `;
119
120 const questionInterface = readLine.createInterface({
121 input: process.stdin,
122 output: process.stderr
123 });
124
125 // In certain scenarios (e.g. when STDIN is not in terminal mode), the callback function will not be
126 // executed. Setting the exit code here to ensure the script exits correctly in those cases. The callback
127 // function is responsible for clearing the exit code if the user wishes to install webpack-cli.
128 process.exitCode = 1;
129 questionInterface.question(question, answer => {
130 questionInterface.close();
131
132 const normalizedAnswer = answer.toLowerCase().startsWith("y");
133
134 if (!normalizedAnswer) {
135 console.error(
136 "You need to install 'webpack-cli' to use webpack via CLI.\n" +
137 "You can also install the CLI manually."
138 );
139
140 return;
141 }
142 process.exitCode = 0;
143
144 console.log(
145 `Installing '${
146 cli.package
147 }' (running '${packageManager} ${installOptions.join(" ")} ${
148 cli.package
149 }')...`
150 );
151
152 runCommand(packageManager, installOptions.concat(cli.package))
153 .then(() => {
154 runCli(cli);
155 })
156 .catch(error => {
157 console.error(error);
158 process.exitCode = 1;
159 });
160 });
161} else {
162 runCli(cli);
163}