webpack.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. #!/usr/bin/env node
  2. /*
  3. MIT License http://www.opensource.org/licenses/mit-license.php
  4. Author Tobias Koppers @sokra
  5. */
  6. (function() {
  7. // wrap in IIFE to be able to use return
  8. const importLocal = require("import-local");
  9. // Prefer the local installation of webpack-cli
  10. if (importLocal(__filename)) {
  11. return;
  12. }
  13. require("v8-compile-cache");
  14. const ErrorHelpers = require("./errorHelpers");
  15. const NON_COMPILATION_ARGS = [
  16. "init",
  17. "migrate",
  18. /*
  19. "add",
  20. "remove",
  21. "update",
  22. "make",
  23. */
  24. "serve",
  25. "generate-loader",
  26. "generate-plugin",
  27. "info"
  28. ];
  29. const NON_COMPILATION_CMD = process.argv.find(arg => {
  30. if (arg === "serve") {
  31. global.process.argv = global.process.argv.filter(a => a !== "serve");
  32. process.argv = global.process.argv;
  33. }
  34. return NON_COMPILATION_ARGS.find(a => a === arg);
  35. });
  36. if (NON_COMPILATION_CMD) {
  37. // eslint-disable-next-line
  38. require("../lib/index")(NON_COMPILATION_CMD, process.argv);
  39. return;
  40. }
  41. const yargs = require("yargs").usage(`webpack-cli ${
  42. require("../package.json").version
  43. }
  44. Usage: webpack-cli [options]
  45. webpack-cli [options] --entry <entry> --output <output>
  46. webpack-cli [options] <entries...> --output <output>
  47. webpack-cli <command> [options]
  48. For more information, see https://webpack.js.org/api/cli/.`);
  49. require("./config-yargs")(yargs);
  50. const DISPLAY_GROUP = "Stats options:";
  51. const BASIC_GROUP = "Basic options:";
  52. yargs.options({
  53. silent: {
  54. type: "boolean",
  55. describe: "Prevent output from being displayed in stdout"
  56. },
  57. json: {
  58. type: "boolean",
  59. alias: "j",
  60. describe: "Prints the result as JSON."
  61. },
  62. progress: {
  63. type: "boolean",
  64. describe: "Print compilation progress in percentage",
  65. group: BASIC_GROUP
  66. },
  67. color: {
  68. type: "boolean",
  69. alias: "colors",
  70. default: function supportsColor() {
  71. return require("supports-color").supportsColor;
  72. },
  73. group: DISPLAY_GROUP,
  74. describe: "Enables/Disables colors on the console"
  75. },
  76. "sort-modules-by": {
  77. type: "string",
  78. group: DISPLAY_GROUP,
  79. describe: "Sorts the modules list by property in module"
  80. },
  81. "sort-chunks-by": {
  82. type: "string",
  83. group: DISPLAY_GROUP,
  84. describe: "Sorts the chunks list by property in chunk"
  85. },
  86. "sort-assets-by": {
  87. type: "string",
  88. group: DISPLAY_GROUP,
  89. describe: "Sorts the assets list by property in asset"
  90. },
  91. "hide-modules": {
  92. type: "boolean",
  93. group: DISPLAY_GROUP,
  94. describe: "Hides info about modules"
  95. },
  96. "display-exclude": {
  97. type: "string",
  98. group: DISPLAY_GROUP,
  99. describe: "Exclude modules in the output"
  100. },
  101. "display-modules": {
  102. type: "boolean",
  103. group: DISPLAY_GROUP,
  104. describe: "Display even excluded modules in the output"
  105. },
  106. "display-max-modules": {
  107. type: "number",
  108. group: DISPLAY_GROUP,
  109. describe: "Sets the maximum number of visible modules in output"
  110. },
  111. "display-chunks": {
  112. type: "boolean",
  113. group: DISPLAY_GROUP,
  114. describe: "Display chunks in the output"
  115. },
  116. "display-entrypoints": {
  117. type: "boolean",
  118. group: DISPLAY_GROUP,
  119. describe: "Display entry points in the output"
  120. },
  121. "display-origins": {
  122. type: "boolean",
  123. group: DISPLAY_GROUP,
  124. describe: "Display origins of chunks in the output"
  125. },
  126. "display-cached": {
  127. type: "boolean",
  128. group: DISPLAY_GROUP,
  129. describe: "Display also cached modules in the output"
  130. },
  131. "display-cached-assets": {
  132. type: "boolean",
  133. group: DISPLAY_GROUP,
  134. describe: "Display also cached assets in the output"
  135. },
  136. "display-reasons": {
  137. type: "boolean",
  138. group: DISPLAY_GROUP,
  139. describe: "Display reasons about module inclusion in the output"
  140. },
  141. "display-depth": {
  142. type: "boolean",
  143. group: DISPLAY_GROUP,
  144. describe: "Display distance from entry point for each module"
  145. },
  146. "display-used-exports": {
  147. type: "boolean",
  148. group: DISPLAY_GROUP,
  149. describe:
  150. "Display information about used exports in modules (Tree Shaking)"
  151. },
  152. "display-provided-exports": {
  153. type: "boolean",
  154. group: DISPLAY_GROUP,
  155. describe: "Display information about exports provided from modules"
  156. },
  157. "display-optimization-bailout": {
  158. type: "boolean",
  159. group: DISPLAY_GROUP,
  160. describe:
  161. "Display information about why optimization bailed out for modules"
  162. },
  163. "display-error-details": {
  164. type: "boolean",
  165. group: DISPLAY_GROUP,
  166. describe: "Display details about errors"
  167. },
  168. display: {
  169. type: "string",
  170. choices: [
  171. "",
  172. "verbose",
  173. "detailed",
  174. "normal",
  175. "minimal",
  176. "errors-only",
  177. "none"
  178. ],
  179. group: DISPLAY_GROUP,
  180. describe: "Select display preset"
  181. },
  182. verbose: {
  183. type: "boolean",
  184. group: DISPLAY_GROUP,
  185. describe: "Show more details"
  186. },
  187. "info-verbosity": {
  188. type: "string",
  189. default: "info",
  190. choices: ["none", "info", "verbose"],
  191. group: DISPLAY_GROUP,
  192. describe:
  193. "Controls the output of lifecycle messaging e.g. Started watching files..."
  194. },
  195. "build-delimiter": {
  196. type: "string",
  197. group: DISPLAY_GROUP,
  198. describe: "Display custom text after build output"
  199. }
  200. });
  201. // yargs will terminate the process early when the user uses help or version.
  202. // This causes large help outputs to be cut short (https://github.com/nodejs/node/wiki/API-changes-between-v0.10-and-v4#process).
  203. // To prevent this we use the yargs.parse API and exit the process normally
  204. yargs.parse(process.argv.slice(2), (err, argv, output) => {
  205. Error.stackTraceLimit = 30;
  206. // arguments validation failed
  207. if (err && output) {
  208. console.error(output);
  209. process.exitCode = 1;
  210. return;
  211. }
  212. // help or version info
  213. if (output) {
  214. console.log(output);
  215. return;
  216. }
  217. if (argv.verbose) {
  218. argv["display"] = "verbose";
  219. }
  220. let options;
  221. try {
  222. options = require("./convert-argv")(argv);
  223. } catch (err) {
  224. if (err.name !== "ValidationError") {
  225. throw err;
  226. }
  227. const stack = ErrorHelpers.cleanUpWebpackOptions(err.stack, err.message);
  228. const message = err.message + "\n" + stack;
  229. if (argv.color) {
  230. console.error(`\u001b[1m\u001b[31m${message}\u001b[39m\u001b[22m`);
  231. } else {
  232. console.error(message);
  233. }
  234. process.exitCode = 1;
  235. return;
  236. }
  237. /**
  238. * When --silent flag is present, an object with a no-op write method is
  239. * used in place of process.stout
  240. */
  241. const stdout = argv.silent
  242. ? {
  243. write: () => {}
  244. } // eslint-disable-line
  245. : process.stdout;
  246. function ifArg(name, fn, init) {
  247. if (Array.isArray(argv[name])) {
  248. if (init) init();
  249. argv[name].forEach(fn);
  250. } else if (typeof argv[name] !== "undefined") {
  251. if (init) init();
  252. fn(argv[name], -1);
  253. }
  254. }
  255. function processOptions(options) {
  256. // process Promise
  257. if (typeof options.then === "function") {
  258. options.then(processOptions).catch(function(err) {
  259. console.error(err.stack || err);
  260. process.exit(1); // eslint-disable-line
  261. });
  262. return;
  263. }
  264. const firstOptions = [].concat(options)[0];
  265. const statsPresetToOptions = require("webpack").Stats.presetToOptions;
  266. let outputOptions = options.stats;
  267. if (
  268. typeof outputOptions === "boolean" ||
  269. typeof outputOptions === "string"
  270. ) {
  271. outputOptions = statsPresetToOptions(outputOptions);
  272. } else if (!outputOptions) {
  273. outputOptions = {};
  274. }
  275. ifArg("display", function(preset) {
  276. outputOptions = statsPresetToOptions(preset);
  277. });
  278. outputOptions = Object.create(outputOptions);
  279. if (Array.isArray(options) && !outputOptions.children) {
  280. outputOptions.children = options.map(o => o.stats);
  281. }
  282. if (typeof outputOptions.context === "undefined")
  283. outputOptions.context = firstOptions.context;
  284. ifArg("env", function(value) {
  285. if (outputOptions.env) {
  286. outputOptions._env = value;
  287. }
  288. });
  289. ifArg("json", function(bool) {
  290. if (bool) {
  291. outputOptions.json = bool;
  292. outputOptions.modules = bool;
  293. }
  294. });
  295. if (typeof outputOptions.colors === "undefined")
  296. outputOptions.colors = require("supports-color").stdout;
  297. ifArg("sort-modules-by", function(value) {
  298. outputOptions.modulesSort = value;
  299. });
  300. ifArg("sort-chunks-by", function(value) {
  301. outputOptions.chunksSort = value;
  302. });
  303. ifArg("sort-assets-by", function(value) {
  304. outputOptions.assetsSort = value;
  305. });
  306. ifArg("display-exclude", function(value) {
  307. outputOptions.exclude = value;
  308. });
  309. if (!outputOptions.json) {
  310. if (typeof outputOptions.cached === "undefined")
  311. outputOptions.cached = false;
  312. if (typeof outputOptions.cachedAssets === "undefined")
  313. outputOptions.cachedAssets = false;
  314. ifArg("display-chunks", function(bool) {
  315. if (bool) {
  316. outputOptions.modules = false;
  317. outputOptions.chunks = true;
  318. outputOptions.chunkModules = true;
  319. }
  320. });
  321. ifArg("display-entrypoints", function(bool) {
  322. outputOptions.entrypoints = bool;
  323. });
  324. ifArg("display-reasons", function(bool) {
  325. if (bool) outputOptions.reasons = true;
  326. });
  327. ifArg("display-depth", function(bool) {
  328. if (bool) outputOptions.depth = true;
  329. });
  330. ifArg("display-used-exports", function(bool) {
  331. if (bool) outputOptions.usedExports = true;
  332. });
  333. ifArg("display-provided-exports", function(bool) {
  334. if (bool) outputOptions.providedExports = true;
  335. });
  336. ifArg("display-optimization-bailout", function(bool) {
  337. if (bool) outputOptions.optimizationBailout = bool;
  338. });
  339. ifArg("display-error-details", function(bool) {
  340. if (bool) outputOptions.errorDetails = true;
  341. });
  342. ifArg("display-origins", function(bool) {
  343. if (bool) outputOptions.chunkOrigins = true;
  344. });
  345. ifArg("display-max-modules", function(value) {
  346. outputOptions.maxModules = +value;
  347. });
  348. ifArg("display-cached", function(bool) {
  349. if (bool) outputOptions.cached = true;
  350. });
  351. ifArg("display-cached-assets", function(bool) {
  352. if (bool) outputOptions.cachedAssets = true;
  353. });
  354. if (!outputOptions.exclude)
  355. outputOptions.exclude = [
  356. "node_modules",
  357. "bower_components",
  358. "components"
  359. ];
  360. if (argv["display-modules"]) {
  361. outputOptions.maxModules = Infinity;
  362. outputOptions.exclude = undefined;
  363. outputOptions.modules = true;
  364. }
  365. }
  366. ifArg("hide-modules", function(bool) {
  367. if (bool) {
  368. outputOptions.modules = false;
  369. outputOptions.chunkModules = false;
  370. }
  371. });
  372. ifArg("info-verbosity", function(value) {
  373. outputOptions.infoVerbosity = value;
  374. });
  375. ifArg("build-delimiter", function(value) {
  376. outputOptions.buildDelimiter = value;
  377. });
  378. const webpack = require("webpack");
  379. let lastHash = null;
  380. let compiler;
  381. try {
  382. compiler = webpack(options);
  383. } catch (err) {
  384. if (err.name === "WebpackOptionsValidationError") {
  385. if (argv.color)
  386. console.error(
  387. `\u001b[1m\u001b[31m${err.message}\u001b[39m\u001b[22m`
  388. );
  389. else console.error(err.message);
  390. // eslint-disable-next-line no-process-exit
  391. process.exit(1);
  392. }
  393. throw err;
  394. }
  395. if (argv.progress) {
  396. const ProgressPlugin = require("webpack").ProgressPlugin;
  397. new ProgressPlugin({
  398. profile: argv.profile
  399. }).apply(compiler);
  400. }
  401. if (outputOptions.infoVerbosity === "verbose") {
  402. compiler.hooks.beforeCompile.tap("WebpackInfo", compilation => {
  403. console.log("\nCompilation starting…\n");
  404. });
  405. compiler.hooks.afterCompile.tap("WebpackInfo", compilation => {
  406. console.log("\nCompilation finished\n");
  407. });
  408. }
  409. function compilerCallback(err, stats) {
  410. if (!options.watch || err) {
  411. // Do not keep cache anymore
  412. compiler.purgeInputFileSystem();
  413. }
  414. if (err) {
  415. lastHash = null;
  416. console.error(err.stack || err);
  417. if (err.details) console.error(err.details);
  418. process.exit(1); // eslint-disable-line
  419. }
  420. if (outputOptions.json) {
  421. stdout.write(
  422. JSON.stringify(stats.toJson(outputOptions), null, 2) + "\n"
  423. );
  424. } else if (stats.hash !== lastHash) {
  425. lastHash = stats.hash;
  426. const statsString = stats.toString(outputOptions);
  427. const delimiter = outputOptions.buildDelimiter
  428. ? `${outputOptions.buildDelimiter}\n`
  429. : "";
  430. if (statsString) stdout.write(`${statsString}\n${delimiter}`);
  431. }
  432. if (!options.watch && stats.hasErrors()) {
  433. process.exitCode = 2;
  434. }
  435. }
  436. if (firstOptions.watch || options.watch) {
  437. const watchOptions =
  438. firstOptions.watchOptions ||
  439. firstOptions.watch ||
  440. options.watch ||
  441. {};
  442. if (watchOptions.stdin) {
  443. process.stdin.on("end", function(_) {
  444. process.exit(); // eslint-disable-line
  445. });
  446. process.stdin.resume();
  447. }
  448. compiler.watch(watchOptions, compilerCallback);
  449. if (outputOptions.infoVerbosity !== "none")
  450. console.log("\nWebpack is watching the files…\n");
  451. } else compiler.run(compilerCallback);
  452. }
  453. processOptions(options);
  454. });
  455. })();