webpack.js 4.2 KB

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