#!/usr/bin/env node process.exitCode = 0; /** * @param {string} command process to run * @param {string[]} args commandline arguments * @returns {Promise} promise */ const runCommand = (command, args) => { const cp = require("child_process"); return new Promise((resolve, reject) => { const executedCommand = cp.spawn(command, args, { stdio: "inherit", shell: true }); executedCommand.on("error", error => { reject(error); }); executedCommand.on("exit", code => { if (code === 0) { resolve(); } else { reject(); } }); }); }; /** * @param {string} packageName name of the package * @returns {boolean} is the package installed? */ const isInstalled = packageName => { try { require.resolve(packageName); return true; } catch (err) { return false; } }; /** * @typedef {Object} CliOption * @property {string} name display name * @property {string} package npm package name * @property {string} binName name of the executable file * @property {string} alias shortcut for choice * @property {boolean} installed currently installed? * @property {boolean} recommended is recommended * @property {string} url homepage * @property {string} description description */ /** @type {CliOption[]} */ const CLIs = [ { name: "webpack-cli", package: "webpack-cli", binName: "webpack-cli", alias: "cli", installed: isInstalled("webpack-cli"), recommended: true, url: "https://github.com/webpack/webpack-cli", description: "The original webpack full-featured CLI." }, { name: "webpack-command", package: "webpack-command", binName: "webpack-command", alias: "command", installed: isInstalled("webpack-command"), recommended: false, url: "https://github.com/webpack-contrib/webpack-command", description: "A lightweight, opinionated webpack CLI." } ]; const installedClis = CLIs.filter(cli => cli.installed); if (installedClis.length === 0) { const path = require("path"); const fs = require("fs"); const readLine = require("readline"); let notify = "One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:"; for (const item of CLIs) { if (item.recommended) { notify += `\n - ${item.name} (${item.url})\n ${item.description}`; } } console.error(notify); const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock")); const packageManager = isYarn ? "yarn" : "npm"; const installOptions = [isYarn ? "add" : "install", "-D"]; console.error( `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join( " " )}".` ); let question = `Do you want to install 'webpack-cli' (yes/no): `; const questionInterface = readLine.createInterface({ input: process.stdin, output: process.stderr }); questionInterface.question(question, answer => { questionInterface.close(); const normalizedAnswer = answer.toLowerCase().startsWith("y"); if (!normalizedAnswer) { console.error( "You need to install 'webpack-cli' to use webpack via CLI.\n" + "You can also install the CLI manually." ); process.exitCode = 1; return; } const packageName = "webpack-cli"; console.log( `Installing '${packageName}' (running '${packageManager} ${installOptions.join( " " )} ${packageName}')...` ); runCommand(packageManager, installOptions.concat(packageName)) .then(() => { require(packageName); //eslint-disable-line }) .catch(error => { console.error(error); process.exitCode = 1; }); }); } else if (installedClis.length === 1) { const path = require("path"); const pkgPath = require.resolve(`${installedClis[0].package}/package.json`); // eslint-disable-next-line node/no-missing-require const pkg = require(pkgPath); // eslint-disable-next-line node/no-missing-require require(path.resolve( path.dirname(pkgPath), pkg.bin[installedClis[0].binName] )); } else { console.warn( `You have installed ${installedClis .map(item => item.name) .join( " and " )} 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.` ); process.exitCode = 1; }