webpack.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env node
  2. process.exitCode = 0;
  3. /**
  4. * @param {string} command process to run
  5. * @param {string[]} args commandline arguments
  6. * @returns {Promise<void>} promise
  7. */
  8. const 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. executedCommand.on("error", error => {
  16. reject(error);
  17. });
  18. executedCommand.on("exit", code => {
  19. if (code === 0) {
  20. resolve();
  21. } else {
  22. reject();
  23. }
  24. });
  25. });
  26. };
  27. /**
  28. * @param {string} packageName name of the package
  29. * @returns {boolean} is the package installed?
  30. */
  31. const isInstalled = packageName => {
  32. try {
  33. require.resolve(packageName);
  34. return true;
  35. } catch (err) {
  36. return false;
  37. }
  38. };
  39. /**
  40. * @typedef {Object} CliOption
  41. * @property {string} name display name
  42. * @property {string} package npm package name
  43. * @property {string} binName name of the executable file
  44. * @property {string} alias shortcut for choice
  45. * @property {boolean} installed currently installed?
  46. * @property {boolean} recommended is recommended
  47. * @property {string} url homepage
  48. * @property {string} description description
  49. */
  50. /** @type {CliOption[]} */
  51. const CLIs = [
  52. {
  53. name: "webpack-cli",
  54. package: "webpack-cli",
  55. binName: "webpack-cli",
  56. alias: "cli",
  57. installed: isInstalled("webpack-cli"),
  58. recommended: true,
  59. url: "https://github.com/webpack/webpack-cli",
  60. description: "The original webpack full-featured CLI."
  61. },
  62. {
  63. name: "webpack-command",
  64. package: "webpack-command",
  65. binName: "webpack-command",
  66. alias: "command",
  67. installed: isInstalled("webpack-command"),
  68. recommended: false,
  69. url: "https://github.com/webpack-contrib/webpack-command",
  70. description: "A lightweight, opinionated webpack CLI."
  71. }
  72. ];
  73. const installedClis = CLIs.filter(cli => cli.installed);
  74. if (installedClis.length === 0) {
  75. const path = require("path");
  76. const fs = require("fs");
  77. const readLine = require("readline");
  78. let notify =
  79. "One CLI for webpack must be installed. These are recommended choices, delivered as separate packages:";
  80. for (const item of CLIs) {
  81. if (item.recommended) {
  82. notify += `\n - ${item.name} (${item.url})\n ${item.description}`;
  83. }
  84. }
  85. console.error(notify);
  86. const isYarn = fs.existsSync(path.resolve(process.cwd(), "yarn.lock"));
  87. const packageManager = isYarn ? "yarn" : "npm";
  88. const installOptions = [isYarn ? "add" : "install", "-D"];
  89. console.error(
  90. `We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join(
  91. " "
  92. )}".`
  93. );
  94. let question = `Do you want to install 'webpack-cli' (yes/no): `;
  95. const questionInterface = readLine.createInterface({
  96. input: process.stdin,
  97. output: process.stderr
  98. });
  99. questionInterface.question(question, answer => {
  100. questionInterface.close();
  101. const normalizedAnswer = answer.toLowerCase().startsWith("y");
  102. if (!normalizedAnswer) {
  103. console.error(
  104. "You need to install 'webpack-cli' to use webpack via CLI.\n" +
  105. "You can also install the CLI manually."
  106. );
  107. process.exitCode = 1;
  108. return;
  109. }
  110. const packageName = "webpack-cli";
  111. console.log(
  112. `Installing '${packageName}' (running '${packageManager} ${installOptions.join(
  113. " "
  114. )} ${packageName}')...`
  115. );
  116. runCommand(packageManager, installOptions.concat(packageName))
  117. .then(() => {
  118. require(packageName); //eslint-disable-line
  119. })
  120. .catch(error => {
  121. console.error(error);
  122. process.exitCode = 1;
  123. });
  124. });
  125. } else if (installedClis.length === 1) {
  126. const path = require("path");
  127. const pkgPath = require.resolve(`${installedClis[0].package}/package.json`);
  128. // eslint-disable-next-line node/no-missing-require
  129. const pkg = require(pkgPath);
  130. // eslint-disable-next-line node/no-missing-require
  131. require(path.resolve(
  132. path.dirname(pkgPath),
  133. pkg.bin[installedClis[0].binName]
  134. ));
  135. } else {
  136. console.warn(
  137. `You have installed ${installedClis
  138. .map(item => item.name)
  139. .join(
  140. " and "
  141. )} 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.`
  142. );
  143. process.exitCode = 1;
  144. }