DefaultStatsPrinterPlugin.js 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @typedef {import("../Compiler")} Compiler */
  7. /** @typedef {import("./StatsPrinter")} StatsPrinter */
  8. /** @typedef {import("./StatsPrinter").StatsPrinterContext} StatsPrinterContext */
  9. const DATA_URI_CONTENT_LENGTH = 16;
  10. const MAX_MODULE_IDENTIFIER_LENGTH = 80;
  11. const plural = (n, singular, plural) => (n === 1 ? singular : plural);
  12. /**
  13. * @param {Record<string, number>} sizes sizes by source type
  14. * @param {Object} options options
  15. * @param {(number) => string=} options.formatSize size formatter
  16. * @returns {string} text
  17. */
  18. const printSizes = (sizes, { formatSize = n => `${n}` }) => {
  19. const keys = Object.keys(sizes);
  20. if (keys.length > 1) {
  21. return keys.map(key => `${formatSize(sizes[key])} (${key})`).join(" ");
  22. } else if (keys.length === 1) {
  23. return formatSize(sizes[keys[0]]);
  24. }
  25. };
  26. const getResourceName = resource => {
  27. const dataUrl = /^data:[^,]+,/.exec(resource);
  28. if (!dataUrl) return resource;
  29. const len = dataUrl[0].length + DATA_URI_CONTENT_LENGTH;
  30. if (resource.length < len) return resource;
  31. return `${resource.slice(
  32. 0,
  33. Math.min(resource.length - /* '..'.length */ 2, len)
  34. )}..`;
  35. };
  36. const getModuleName = name => {
  37. const [, prefix, resource] = /^(.*!)?([^!]*)$/.exec(name);
  38. if (resource.length > MAX_MODULE_IDENTIFIER_LENGTH) {
  39. const truncatedResource = `${resource.slice(
  40. 0,
  41. Math.min(
  42. resource.length - /* '...(truncated)'.length */ 14,
  43. MAX_MODULE_IDENTIFIER_LENGTH
  44. )
  45. )}...(truncated)`;
  46. return [prefix, getResourceName(truncatedResource)];
  47. }
  48. return [prefix, getResourceName(resource)];
  49. };
  50. const mapLines = (str, fn) => str.split("\n").map(fn).join("\n");
  51. /**
  52. * @param {number} n a number
  53. * @returns {string} number as two digit string, leading 0
  54. */
  55. const twoDigit = n => (n >= 10 ? `${n}` : `0${n}`);
  56. const isValidId = id => {
  57. return typeof id === "number" || id;
  58. };
  59. const moreCount = (list, count) => {
  60. return list && list.length > 0 ? `+ ${count}` : `${count}`;
  61. };
  62. /** @type {Record<string, (thing: any, context: StatsPrinterContext, printer: StatsPrinter) => string | void>} */
  63. const SIMPLE_PRINTERS = {
  64. "compilation.summary!": (
  65. _,
  66. {
  67. type,
  68. bold,
  69. green,
  70. red,
  71. yellow,
  72. formatDateTime,
  73. formatTime,
  74. compilation: {
  75. name,
  76. hash,
  77. version,
  78. time,
  79. builtAt,
  80. errorsCount,
  81. warningsCount
  82. }
  83. }
  84. ) => {
  85. const root = type === "compilation.summary!";
  86. const warningsMessage =
  87. warningsCount > 0
  88. ? yellow(
  89. `${warningsCount} ${plural(warningsCount, "warning", "warnings")}`
  90. )
  91. : "";
  92. const errorsMessage =
  93. errorsCount > 0
  94. ? red(`${errorsCount} ${plural(errorsCount, "error", "errors")}`)
  95. : "";
  96. const timeMessage = root && time ? ` in ${formatTime(time)}` : "";
  97. const hashMessage = hash ? ` (${hash})` : "";
  98. const builtAtMessage =
  99. root && builtAt ? `${formatDateTime(builtAt)}: ` : "";
  100. const versionMessage = root && version ? `webpack ${version}` : "";
  101. const nameMessage =
  102. root && name
  103. ? bold(name)
  104. : name
  105. ? `Child ${bold(name)}`
  106. : root
  107. ? ""
  108. : "Child";
  109. const subjectMessage =
  110. nameMessage && versionMessage
  111. ? `${nameMessage} (${versionMessage})`
  112. : versionMessage || nameMessage || "webpack";
  113. let statusMessage;
  114. if (errorsMessage && warningsMessage) {
  115. statusMessage = `compiled with ${errorsMessage} and ${warningsMessage}`;
  116. } else if (errorsMessage) {
  117. statusMessage = `compiled with ${errorsMessage}`;
  118. } else if (warningsMessage) {
  119. statusMessage = `compiled with ${warningsMessage}`;
  120. } else if (errorsCount === 0 && warningsCount === 0) {
  121. statusMessage = `compiled ${green("successfully")}`;
  122. } else {
  123. statusMessage = `compiled`;
  124. }
  125. if (
  126. builtAtMessage ||
  127. versionMessage ||
  128. errorsMessage ||
  129. warningsMessage ||
  130. (errorsCount === 0 && warningsCount === 0) ||
  131. timeMessage ||
  132. hashMessage
  133. )
  134. return `${builtAtMessage}${subjectMessage} ${statusMessage}${timeMessage}${hashMessage}`;
  135. },
  136. "compilation.filteredWarningDetailsCount": count =>
  137. count
  138. ? `${count} ${plural(
  139. count,
  140. "warning has",
  141. "warnings have"
  142. )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.`
  143. : undefined,
  144. "compilation.filteredErrorDetailsCount": (count, { yellow }) =>
  145. count
  146. ? yellow(
  147. `${count} ${plural(
  148. count,
  149. "error has",
  150. "errors have"
  151. )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.`
  152. )
  153. : undefined,
  154. "compilation.env": (env, { bold }) =>
  155. env
  156. ? `Environment (--env): ${bold(JSON.stringify(env, null, 2))}`
  157. : undefined,
  158. "compilation.publicPath": (publicPath, { bold }) =>
  159. `PublicPath: ${bold(publicPath || "(none)")}`,
  160. "compilation.entrypoints": (entrypoints, context, printer) =>
  161. Array.isArray(entrypoints)
  162. ? undefined
  163. : printer.print(context.type, Object.values(entrypoints), {
  164. ...context,
  165. chunkGroupKind: "Entrypoint"
  166. }),
  167. "compilation.namedChunkGroups": (namedChunkGroups, context, printer) => {
  168. if (!Array.isArray(namedChunkGroups)) {
  169. const {
  170. compilation: { entrypoints }
  171. } = context;
  172. let chunkGroups = Object.values(namedChunkGroups);
  173. if (entrypoints) {
  174. chunkGroups = chunkGroups.filter(
  175. group =>
  176. !Object.prototype.hasOwnProperty.call(entrypoints, group.name)
  177. );
  178. }
  179. return printer.print(context.type, chunkGroups, {
  180. ...context,
  181. chunkGroupKind: "Chunk Group"
  182. });
  183. }
  184. },
  185. "compilation.assetsByChunkName": () => "",
  186. "compilation.filteredModules": (
  187. filteredModules,
  188. { compilation: { modules } }
  189. ) =>
  190. filteredModules > 0
  191. ? `${moreCount(modules, filteredModules)} ${plural(
  192. filteredModules,
  193. "module",
  194. "modules"
  195. )}`
  196. : undefined,
  197. "compilation.filteredAssets": (filteredAssets, { compilation: { assets } }) =>
  198. filteredAssets > 0
  199. ? `${moreCount(assets, filteredAssets)} ${plural(
  200. filteredAssets,
  201. "asset",
  202. "assets"
  203. )}`
  204. : undefined,
  205. "compilation.logging": (logging, context, printer) =>
  206. Array.isArray(logging)
  207. ? undefined
  208. : printer.print(
  209. context.type,
  210. Object.entries(logging).map(([name, value]) => ({ ...value, name })),
  211. context
  212. ),
  213. "compilation.warningsInChildren!": (_, { yellow, compilation }) => {
  214. if (
  215. !compilation.children &&
  216. compilation.warningsCount > 0 &&
  217. compilation.warnings
  218. ) {
  219. const childWarnings =
  220. compilation.warningsCount - compilation.warnings.length;
  221. if (childWarnings > 0) {
  222. return yellow(
  223. `${childWarnings} ${plural(
  224. childWarnings,
  225. "WARNING",
  226. "WARNINGS"
  227. )} in child compilations${
  228. compilation.children
  229. ? ""
  230. : " (Use 'stats.children: true' resp. '--stats-children' for more details)"
  231. }`
  232. );
  233. }
  234. }
  235. },
  236. "compilation.errorsInChildren!": (_, { red, compilation }) => {
  237. if (
  238. !compilation.children &&
  239. compilation.errorsCount > 0 &&
  240. compilation.errors
  241. ) {
  242. const childErrors = compilation.errorsCount - compilation.errors.length;
  243. if (childErrors > 0) {
  244. return red(
  245. `${childErrors} ${plural(
  246. childErrors,
  247. "ERROR",
  248. "ERRORS"
  249. )} in child compilations${
  250. compilation.children
  251. ? ""
  252. : " (Use 'stats.children: true' resp. '--stats-children' for more details)"
  253. }`
  254. );
  255. }
  256. }
  257. },
  258. "asset.type": type => type,
  259. "asset.name": (name, { formatFilename, asset: { isOverSizeLimit } }) =>
  260. formatFilename(name, isOverSizeLimit),
  261. "asset.size": (
  262. size,
  263. { asset: { isOverSizeLimit }, yellow, green, formatSize }
  264. ) => (isOverSizeLimit ? yellow(formatSize(size)) : formatSize(size)),
  265. "asset.emitted": (emitted, { green, formatFlag }) =>
  266. emitted ? green(formatFlag("emitted")) : undefined,
  267. "asset.comparedForEmit": (comparedForEmit, { yellow, formatFlag }) =>
  268. comparedForEmit ? yellow(formatFlag("compared for emit")) : undefined,
  269. "asset.cached": (cached, { green, formatFlag }) =>
  270. cached ? green(formatFlag("cached")) : undefined,
  271. "asset.isOverSizeLimit": (isOverSizeLimit, { yellow, formatFlag }) =>
  272. isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
  273. "asset.info.immutable": (immutable, { green, formatFlag }) =>
  274. immutable ? green(formatFlag("immutable")) : undefined,
  275. "asset.info.javascriptModule": (javascriptModule, { formatFlag }) =>
  276. javascriptModule ? formatFlag("javascript module") : undefined,
  277. "asset.info.sourceFilename": (sourceFilename, { formatFlag }) =>
  278. sourceFilename
  279. ? formatFlag(
  280. sourceFilename === true
  281. ? "from source file"
  282. : `from: ${sourceFilename}`
  283. )
  284. : undefined,
  285. "asset.info.development": (development, { green, formatFlag }) =>
  286. development ? green(formatFlag("dev")) : undefined,
  287. "asset.info.hotModuleReplacement": (
  288. hotModuleReplacement,
  289. { green, formatFlag }
  290. ) => (hotModuleReplacement ? green(formatFlag("hmr")) : undefined),
  291. "asset.separator!": () => "\n",
  292. "asset.filteredRelated": (filteredRelated, { asset: { related } }) =>
  293. filteredRelated > 0
  294. ? `${moreCount(related, filteredRelated)} related ${plural(
  295. filteredRelated,
  296. "asset",
  297. "assets"
  298. )}`
  299. : undefined,
  300. "asset.filteredChildren": (filteredChildren, { asset: { children } }) =>
  301. filteredChildren > 0
  302. ? `${moreCount(children, filteredChildren)} ${plural(
  303. filteredChildren,
  304. "asset",
  305. "assets"
  306. )}`
  307. : undefined,
  308. assetChunk: (id, { formatChunkId }) => formatChunkId(id),
  309. assetChunkName: name => name,
  310. assetChunkIdHint: name => name,
  311. "module.type": type => (type !== "module" ? type : undefined),
  312. "module.id": (id, { formatModuleId }) =>
  313. isValidId(id) ? formatModuleId(id) : undefined,
  314. "module.name": (name, { bold }) => {
  315. const [prefix, resource] = getModuleName(name);
  316. return `${prefix || ""}${bold(resource || "")}`;
  317. },
  318. "module.identifier": identifier => undefined,
  319. "module.layer": (layer, { formatLayer }) =>
  320. layer ? formatLayer(layer) : undefined,
  321. "module.sizes": printSizes,
  322. "module.chunks[]": (id, { formatChunkId }) => formatChunkId(id),
  323. "module.depth": (depth, { formatFlag }) =>
  324. depth !== null ? formatFlag(`depth ${depth}`) : undefined,
  325. "module.cacheable": (cacheable, { formatFlag, red }) =>
  326. cacheable === false ? red(formatFlag("not cacheable")) : undefined,
  327. "module.orphan": (orphan, { formatFlag, yellow }) =>
  328. orphan ? yellow(formatFlag("orphan")) : undefined,
  329. "module.runtime": (runtime, { formatFlag, yellow }) =>
  330. runtime ? yellow(formatFlag("runtime")) : undefined,
  331. "module.optional": (optional, { formatFlag, yellow }) =>
  332. optional ? yellow(formatFlag("optional")) : undefined,
  333. "module.dependent": (dependent, { formatFlag, cyan }) =>
  334. dependent ? cyan(formatFlag("dependent")) : undefined,
  335. "module.built": (built, { formatFlag, yellow }) =>
  336. built ? yellow(formatFlag("built")) : undefined,
  337. "module.codeGenerated": (codeGenerated, { formatFlag, yellow }) =>
  338. codeGenerated ? yellow(formatFlag("code generated")) : undefined,
  339. "module.buildTimeExecuted": (buildTimeExecuted, { formatFlag, green }) =>
  340. buildTimeExecuted ? green(formatFlag("build time executed")) : undefined,
  341. "module.cached": (cached, { formatFlag, green }) =>
  342. cached ? green(formatFlag("cached")) : undefined,
  343. "module.assets": (assets, { formatFlag, magenta }) =>
  344. assets && assets.length
  345. ? magenta(
  346. formatFlag(
  347. `${assets.length} ${plural(assets.length, "asset", "assets")}`
  348. )
  349. )
  350. : undefined,
  351. "module.warnings": (warnings, { formatFlag, yellow }) =>
  352. warnings === true
  353. ? yellow(formatFlag("warnings"))
  354. : warnings
  355. ? yellow(
  356. formatFlag(`${warnings} ${plural(warnings, "warning", "warnings")}`)
  357. )
  358. : undefined,
  359. "module.errors": (errors, { formatFlag, red }) =>
  360. errors === true
  361. ? red(formatFlag("errors"))
  362. : errors
  363. ? red(formatFlag(`${errors} ${plural(errors, "error", "errors")}`))
  364. : undefined,
  365. "module.providedExports": (providedExports, { formatFlag, cyan }) => {
  366. if (Array.isArray(providedExports)) {
  367. if (providedExports.length === 0) return cyan(formatFlag("no exports"));
  368. return cyan(formatFlag(`exports: ${providedExports.join(", ")}`));
  369. }
  370. },
  371. "module.usedExports": (usedExports, { formatFlag, cyan, module }) => {
  372. if (usedExports !== true) {
  373. if (usedExports === null) return cyan(formatFlag("used exports unknown"));
  374. if (usedExports === false) return cyan(formatFlag("module unused"));
  375. if (Array.isArray(usedExports)) {
  376. if (usedExports.length === 0)
  377. return cyan(formatFlag("no exports used"));
  378. const providedExportsCount = Array.isArray(module.providedExports)
  379. ? module.providedExports.length
  380. : null;
  381. if (
  382. providedExportsCount !== null &&
  383. providedExportsCount === usedExports.length
  384. ) {
  385. return cyan(formatFlag("all exports used"));
  386. } else {
  387. return cyan(
  388. formatFlag(`only some exports used: ${usedExports.join(", ")}`)
  389. );
  390. }
  391. }
  392. }
  393. },
  394. "module.optimizationBailout[]": (optimizationBailout, { yellow }) =>
  395. yellow(optimizationBailout),
  396. "module.issuerPath": (issuerPath, { module }) =>
  397. module.profile ? undefined : "",
  398. "module.profile": profile => undefined,
  399. "module.filteredModules": (filteredModules, { module: { modules } }) =>
  400. filteredModules > 0
  401. ? `${moreCount(modules, filteredModules)} nested ${plural(
  402. filteredModules,
  403. "module",
  404. "modules"
  405. )}`
  406. : undefined,
  407. "module.filteredReasons": (filteredReasons, { module: { reasons } }) =>
  408. filteredReasons > 0
  409. ? `${moreCount(reasons, filteredReasons)} ${plural(
  410. filteredReasons,
  411. "reason",
  412. "reasons"
  413. )}`
  414. : undefined,
  415. "module.filteredChildren": (filteredChildren, { module: { children } }) =>
  416. filteredChildren > 0
  417. ? `${moreCount(children, filteredChildren)} ${plural(
  418. filteredChildren,
  419. "module",
  420. "modules"
  421. )}`
  422. : undefined,
  423. "module.separator!": () => "\n",
  424. "moduleIssuer.id": (id, { formatModuleId }) => formatModuleId(id),
  425. "moduleIssuer.profile.total": (value, { formatTime }) => formatTime(value),
  426. "moduleReason.type": type => type,
  427. "moduleReason.userRequest": (userRequest, { cyan }) =>
  428. cyan(getResourceName(userRequest)),
  429. "moduleReason.moduleId": (moduleId, { formatModuleId }) =>
  430. isValidId(moduleId) ? formatModuleId(moduleId) : undefined,
  431. "moduleReason.module": (module, { magenta }) => magenta(module),
  432. "moduleReason.loc": loc => loc,
  433. "moduleReason.explanation": (explanation, { cyan }) => cyan(explanation),
  434. "moduleReason.active": (active, { formatFlag }) =>
  435. active ? undefined : formatFlag("inactive"),
  436. "moduleReason.resolvedModule": (module, { magenta }) => magenta(module),
  437. "moduleReason.filteredChildren": (
  438. filteredChildren,
  439. { moduleReason: { children } }
  440. ) =>
  441. filteredChildren > 0
  442. ? `${moreCount(children, filteredChildren)} ${plural(
  443. filteredChildren,
  444. "reason",
  445. "reasons"
  446. )}`
  447. : undefined,
  448. "module.profile.total": (value, { formatTime }) => formatTime(value),
  449. "module.profile.resolving": (value, { formatTime }) =>
  450. `resolving: ${formatTime(value)}`,
  451. "module.profile.restoring": (value, { formatTime }) =>
  452. `restoring: ${formatTime(value)}`,
  453. "module.profile.integration": (value, { formatTime }) =>
  454. `integration: ${formatTime(value)}`,
  455. "module.profile.building": (value, { formatTime }) =>
  456. `building: ${formatTime(value)}`,
  457. "module.profile.storing": (value, { formatTime }) =>
  458. `storing: ${formatTime(value)}`,
  459. "module.profile.additionalResolving": (value, { formatTime }) =>
  460. value ? `additional resolving: ${formatTime(value)}` : undefined,
  461. "module.profile.additionalIntegration": (value, { formatTime }) =>
  462. value ? `additional integration: ${formatTime(value)}` : undefined,
  463. "chunkGroup.kind!": (_, { chunkGroupKind }) => chunkGroupKind,
  464. "chunkGroup.separator!": () => "\n",
  465. "chunkGroup.name": (name, { bold }) => bold(name),
  466. "chunkGroup.isOverSizeLimit": (isOverSizeLimit, { formatFlag, yellow }) =>
  467. isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
  468. "chunkGroup.assetsSize": (size, { formatSize }) =>
  469. size ? formatSize(size) : undefined,
  470. "chunkGroup.auxiliaryAssetsSize": (size, { formatSize }) =>
  471. size ? `(${formatSize(size)})` : undefined,
  472. "chunkGroup.filteredAssets": (n, { chunkGroup: { assets } }) =>
  473. n > 0
  474. ? `${moreCount(assets, n)} ${plural(n, "asset", "assets")}`
  475. : undefined,
  476. "chunkGroup.filteredAuxiliaryAssets": (
  477. n,
  478. { chunkGroup: { auxiliaryAssets } }
  479. ) =>
  480. n > 0
  481. ? `${moreCount(auxiliaryAssets, n)} auxiliary ${plural(
  482. n,
  483. "asset",
  484. "assets"
  485. )}`
  486. : undefined,
  487. "chunkGroup.is!": () => "=",
  488. "chunkGroupAsset.name": (asset, { green }) => green(asset),
  489. "chunkGroupAsset.size": (size, { formatSize, chunkGroup }) =>
  490. chunkGroup.assets.length > 1 ||
  491. (chunkGroup.auxiliaryAssets && chunkGroup.auxiliaryAssets.length > 0)
  492. ? formatSize(size)
  493. : undefined,
  494. "chunkGroup.children": (children, context, printer) =>
  495. Array.isArray(children)
  496. ? undefined
  497. : printer.print(
  498. context.type,
  499. Object.keys(children).map(key => ({
  500. type: key,
  501. children: children[key]
  502. })),
  503. context
  504. ),
  505. "chunkGroupChildGroup.type": type => `${type}:`,
  506. "chunkGroupChild.assets[]": (file, { formatFilename }) =>
  507. formatFilename(file),
  508. "chunkGroupChild.chunks[]": (id, { formatChunkId }) => formatChunkId(id),
  509. "chunkGroupChild.name": name => (name ? `(name: ${name})` : undefined),
  510. "chunk.id": (id, { formatChunkId }) => formatChunkId(id),
  511. "chunk.files[]": (file, { formatFilename }) => formatFilename(file),
  512. "chunk.names[]": name => name,
  513. "chunk.idHints[]": name => name,
  514. "chunk.runtime[]": name => name,
  515. "chunk.sizes": (sizes, context) => printSizes(sizes, context),
  516. "chunk.parents[]": (parents, context) =>
  517. context.formatChunkId(parents, "parent"),
  518. "chunk.siblings[]": (siblings, context) =>
  519. context.formatChunkId(siblings, "sibling"),
  520. "chunk.children[]": (children, context) =>
  521. context.formatChunkId(children, "child"),
  522. "chunk.childrenByOrder": (childrenByOrder, context, printer) =>
  523. Array.isArray(childrenByOrder)
  524. ? undefined
  525. : printer.print(
  526. context.type,
  527. Object.keys(childrenByOrder).map(key => ({
  528. type: key,
  529. children: childrenByOrder[key]
  530. })),
  531. context
  532. ),
  533. "chunk.childrenByOrder[].type": type => `${type}:`,
  534. "chunk.childrenByOrder[].children[]": (id, { formatChunkId }) =>
  535. isValidId(id) ? formatChunkId(id) : undefined,
  536. "chunk.entry": (entry, { formatFlag, yellow }) =>
  537. entry ? yellow(formatFlag("entry")) : undefined,
  538. "chunk.initial": (initial, { formatFlag, yellow }) =>
  539. initial ? yellow(formatFlag("initial")) : undefined,
  540. "chunk.rendered": (rendered, { formatFlag, green }) =>
  541. rendered ? green(formatFlag("rendered")) : undefined,
  542. "chunk.recorded": (recorded, { formatFlag, green }) =>
  543. recorded ? green(formatFlag("recorded")) : undefined,
  544. "chunk.reason": (reason, { yellow }) => (reason ? yellow(reason) : undefined),
  545. "chunk.filteredModules": (filteredModules, { chunk: { modules } }) =>
  546. filteredModules > 0
  547. ? `${moreCount(modules, filteredModules)} chunk ${plural(
  548. filteredModules,
  549. "module",
  550. "modules"
  551. )}`
  552. : undefined,
  553. "chunk.separator!": () => "\n",
  554. "chunkOrigin.request": request => request,
  555. "chunkOrigin.moduleId": (moduleId, { formatModuleId }) =>
  556. isValidId(moduleId) ? formatModuleId(moduleId) : undefined,
  557. "chunkOrigin.moduleName": (moduleName, { bold }) => bold(moduleName),
  558. "chunkOrigin.loc": loc => loc,
  559. "error.compilerPath": (compilerPath, { bold }) =>
  560. compilerPath ? bold(`(${compilerPath})`) : undefined,
  561. "error.chunkId": (chunkId, { formatChunkId }) =>
  562. isValidId(chunkId) ? formatChunkId(chunkId) : undefined,
  563. "error.chunkEntry": (chunkEntry, { formatFlag }) =>
  564. chunkEntry ? formatFlag("entry") : undefined,
  565. "error.chunkInitial": (chunkInitial, { formatFlag }) =>
  566. chunkInitial ? formatFlag("initial") : undefined,
  567. "error.file": (file, { bold }) => bold(file),
  568. "error.moduleName": (moduleName, { bold }) => {
  569. return moduleName.includes("!")
  570. ? `${bold(moduleName.replace(/^(\s|\S)*!/, ""))} (${moduleName})`
  571. : `${bold(moduleName)}`;
  572. },
  573. "error.loc": (loc, { green }) => green(loc),
  574. "error.message": (message, { bold, formatError }) =>
  575. message.includes("\u001b[") ? message : bold(formatError(message)),
  576. "error.details": (details, { formatError }) => formatError(details),
  577. "error.filteredDetails": filteredDetails =>
  578. filteredDetails ? `+ ${filteredDetails} hidden lines` : undefined,
  579. "error.stack": stack => stack,
  580. "error.moduleTrace": moduleTrace => undefined,
  581. "error.separator!": () => "\n",
  582. "loggingEntry(error).loggingEntry.message": (message, { red }) =>
  583. mapLines(message, x => `<e> ${red(x)}`),
  584. "loggingEntry(warn).loggingEntry.message": (message, { yellow }) =>
  585. mapLines(message, x => `<w> ${yellow(x)}`),
  586. "loggingEntry(info).loggingEntry.message": (message, { green }) =>
  587. mapLines(message, x => `<i> ${green(x)}`),
  588. "loggingEntry(log).loggingEntry.message": (message, { bold }) =>
  589. mapLines(message, x => ` ${bold(x)}`),
  590. "loggingEntry(debug).loggingEntry.message": message =>
  591. mapLines(message, x => ` ${x}`),
  592. "loggingEntry(trace).loggingEntry.message": message =>
  593. mapLines(message, x => ` ${x}`),
  594. "loggingEntry(status).loggingEntry.message": (message, { magenta }) =>
  595. mapLines(message, x => `<s> ${magenta(x)}`),
  596. "loggingEntry(profile).loggingEntry.message": (message, { magenta }) =>
  597. mapLines(message, x => `<p> ${magenta(x)}`),
  598. "loggingEntry(profileEnd).loggingEntry.message": (message, { magenta }) =>
  599. mapLines(message, x => `</p> ${magenta(x)}`),
  600. "loggingEntry(time).loggingEntry.message": (message, { magenta }) =>
  601. mapLines(message, x => `<t> ${magenta(x)}`),
  602. "loggingEntry(group).loggingEntry.message": (message, { cyan }) =>
  603. mapLines(message, x => `<-> ${cyan(x)}`),
  604. "loggingEntry(groupCollapsed).loggingEntry.message": (message, { cyan }) =>
  605. mapLines(message, x => `<+> ${cyan(x)}`),
  606. "loggingEntry(clear).loggingEntry": () => " -------",
  607. "loggingEntry(groupCollapsed).loggingEntry.children": () => "",
  608. "loggingEntry.trace[]": trace =>
  609. trace ? mapLines(trace, x => `| ${x}`) : undefined,
  610. "moduleTraceItem.originName": originName => originName,
  611. loggingGroup: loggingGroup =>
  612. loggingGroup.entries.length === 0 ? "" : undefined,
  613. "loggingGroup.debug": (flag, { red }) => (flag ? red("DEBUG") : undefined),
  614. "loggingGroup.name": (name, { bold }) => bold(`LOG from ${name}`),
  615. "loggingGroup.separator!": () => "\n",
  616. "loggingGroup.filteredEntries": filteredEntries =>
  617. filteredEntries > 0 ? `+ ${filteredEntries} hidden lines` : undefined,
  618. "moduleTraceDependency.loc": loc => loc
  619. };
  620. /** @type {Record<string, string | Function>} */
  621. const ITEM_NAMES = {
  622. "compilation.assets[]": "asset",
  623. "compilation.modules[]": "module",
  624. "compilation.chunks[]": "chunk",
  625. "compilation.entrypoints[]": "chunkGroup",
  626. "compilation.namedChunkGroups[]": "chunkGroup",
  627. "compilation.errors[]": "error",
  628. "compilation.warnings[]": "error",
  629. "compilation.logging[]": "loggingGroup",
  630. "compilation.children[]": "compilation",
  631. "asset.related[]": "asset",
  632. "asset.children[]": "asset",
  633. "asset.chunks[]": "assetChunk",
  634. "asset.auxiliaryChunks[]": "assetChunk",
  635. "asset.chunkNames[]": "assetChunkName",
  636. "asset.chunkIdHints[]": "assetChunkIdHint",
  637. "asset.auxiliaryChunkNames[]": "assetChunkName",
  638. "asset.auxiliaryChunkIdHints[]": "assetChunkIdHint",
  639. "chunkGroup.assets[]": "chunkGroupAsset",
  640. "chunkGroup.auxiliaryAssets[]": "chunkGroupAsset",
  641. "chunkGroupChild.assets[]": "chunkGroupAsset",
  642. "chunkGroupChild.auxiliaryAssets[]": "chunkGroupAsset",
  643. "chunkGroup.children[]": "chunkGroupChildGroup",
  644. "chunkGroupChildGroup.children[]": "chunkGroupChild",
  645. "module.modules[]": "module",
  646. "module.children[]": "module",
  647. "module.reasons[]": "moduleReason",
  648. "moduleReason.children[]": "moduleReason",
  649. "module.issuerPath[]": "moduleIssuer",
  650. "chunk.origins[]": "chunkOrigin",
  651. "chunk.modules[]": "module",
  652. "loggingGroup.entries[]": logEntry =>
  653. `loggingEntry(${logEntry.type}).loggingEntry`,
  654. "loggingEntry.children[]": logEntry =>
  655. `loggingEntry(${logEntry.type}).loggingEntry`,
  656. "error.moduleTrace[]": "moduleTraceItem",
  657. "moduleTraceItem.dependencies[]": "moduleTraceDependency"
  658. };
  659. const ERROR_PREFERRED_ORDER = [
  660. "compilerPath",
  661. "chunkId",
  662. "chunkEntry",
  663. "chunkInitial",
  664. "file",
  665. "separator!",
  666. "moduleName",
  667. "loc",
  668. "separator!",
  669. "message",
  670. "separator!",
  671. "details",
  672. "separator!",
  673. "filteredDetails",
  674. "separator!",
  675. "stack",
  676. "separator!",
  677. "missing",
  678. "separator!",
  679. "moduleTrace"
  680. ];
  681. /** @type {Record<string, string[]>} */
  682. const PREFERRED_ORDERS = {
  683. compilation: [
  684. "name",
  685. "hash",
  686. "version",
  687. "time",
  688. "builtAt",
  689. "env",
  690. "publicPath",
  691. "assets",
  692. "filteredAssets",
  693. "entrypoints",
  694. "namedChunkGroups",
  695. "chunks",
  696. "modules",
  697. "filteredModules",
  698. "children",
  699. "logging",
  700. "warnings",
  701. "warningsInChildren!",
  702. "filteredWarningDetailsCount",
  703. "errors",
  704. "errorsInChildren!",
  705. "filteredErrorDetailsCount",
  706. "summary!",
  707. "needAdditionalPass"
  708. ],
  709. asset: [
  710. "type",
  711. "name",
  712. "size",
  713. "chunks",
  714. "auxiliaryChunks",
  715. "emitted",
  716. "comparedForEmit",
  717. "cached",
  718. "info",
  719. "isOverSizeLimit",
  720. "chunkNames",
  721. "auxiliaryChunkNames",
  722. "chunkIdHints",
  723. "auxiliaryChunkIdHints",
  724. "related",
  725. "filteredRelated",
  726. "children",
  727. "filteredChildren"
  728. ],
  729. "asset.info": [
  730. "immutable",
  731. "sourceFilename",
  732. "javascriptModule",
  733. "development",
  734. "hotModuleReplacement"
  735. ],
  736. chunkGroup: [
  737. "kind!",
  738. "name",
  739. "isOverSizeLimit",
  740. "assetsSize",
  741. "auxiliaryAssetsSize",
  742. "is!",
  743. "assets",
  744. "filteredAssets",
  745. "auxiliaryAssets",
  746. "filteredAuxiliaryAssets",
  747. "separator!",
  748. "children"
  749. ],
  750. chunkGroupAsset: ["name", "size"],
  751. chunkGroupChildGroup: ["type", "children"],
  752. chunkGroupChild: ["assets", "chunks", "name"],
  753. module: [
  754. "type",
  755. "name",
  756. "identifier",
  757. "id",
  758. "layer",
  759. "sizes",
  760. "chunks",
  761. "depth",
  762. "cacheable",
  763. "orphan",
  764. "runtime",
  765. "optional",
  766. "dependent",
  767. "built",
  768. "codeGenerated",
  769. "cached",
  770. "assets",
  771. "failed",
  772. "warnings",
  773. "errors",
  774. "children",
  775. "filteredChildren",
  776. "providedExports",
  777. "usedExports",
  778. "optimizationBailout",
  779. "reasons",
  780. "filteredReasons",
  781. "issuerPath",
  782. "profile",
  783. "modules",
  784. "filteredModules"
  785. ],
  786. moduleReason: [
  787. "active",
  788. "type",
  789. "userRequest",
  790. "moduleId",
  791. "module",
  792. "resolvedModule",
  793. "loc",
  794. "explanation",
  795. "children",
  796. "filteredChildren"
  797. ],
  798. "module.profile": [
  799. "total",
  800. "separator!",
  801. "resolving",
  802. "restoring",
  803. "integration",
  804. "building",
  805. "storing",
  806. "additionalResolving",
  807. "additionalIntegration"
  808. ],
  809. chunk: [
  810. "id",
  811. "runtime",
  812. "files",
  813. "names",
  814. "idHints",
  815. "sizes",
  816. "parents",
  817. "siblings",
  818. "children",
  819. "childrenByOrder",
  820. "entry",
  821. "initial",
  822. "rendered",
  823. "recorded",
  824. "reason",
  825. "separator!",
  826. "origins",
  827. "separator!",
  828. "modules",
  829. "separator!",
  830. "filteredModules"
  831. ],
  832. chunkOrigin: ["request", "moduleId", "moduleName", "loc"],
  833. error: ERROR_PREFERRED_ORDER,
  834. warning: ERROR_PREFERRED_ORDER,
  835. "chunk.childrenByOrder[]": ["type", "children"],
  836. loggingGroup: [
  837. "debug",
  838. "name",
  839. "separator!",
  840. "entries",
  841. "separator!",
  842. "filteredEntries"
  843. ],
  844. loggingEntry: ["message", "trace", "children"]
  845. };
  846. const itemsJoinOneLine = items => items.filter(Boolean).join(" ");
  847. const itemsJoinOneLineBrackets = items =>
  848. items.length > 0 ? `(${items.filter(Boolean).join(" ")})` : undefined;
  849. const itemsJoinMoreSpacing = items => items.filter(Boolean).join("\n\n");
  850. const itemsJoinComma = items => items.filter(Boolean).join(", ");
  851. const itemsJoinCommaBrackets = items =>
  852. items.length > 0 ? `(${items.filter(Boolean).join(", ")})` : undefined;
  853. const itemsJoinCommaBracketsWithName = name => items =>
  854. items.length > 0
  855. ? `(${name}: ${items.filter(Boolean).join(", ")})`
  856. : undefined;
  857. /** @type {Record<string, (items: string[]) => string>} */
  858. const SIMPLE_ITEMS_JOINER = {
  859. "chunk.parents": itemsJoinOneLine,
  860. "chunk.siblings": itemsJoinOneLine,
  861. "chunk.children": itemsJoinOneLine,
  862. "chunk.names": itemsJoinCommaBrackets,
  863. "chunk.idHints": itemsJoinCommaBracketsWithName("id hint"),
  864. "chunk.runtime": itemsJoinCommaBracketsWithName("runtime"),
  865. "chunk.files": itemsJoinComma,
  866. "chunk.childrenByOrder": itemsJoinOneLine,
  867. "chunk.childrenByOrder[].children": itemsJoinOneLine,
  868. "chunkGroup.assets": itemsJoinOneLine,
  869. "chunkGroup.auxiliaryAssets": itemsJoinOneLineBrackets,
  870. "chunkGroupChildGroup.children": itemsJoinComma,
  871. "chunkGroupChild.assets": itemsJoinOneLine,
  872. "chunkGroupChild.auxiliaryAssets": itemsJoinOneLineBrackets,
  873. "asset.chunks": itemsJoinComma,
  874. "asset.auxiliaryChunks": itemsJoinCommaBrackets,
  875. "asset.chunkNames": itemsJoinCommaBracketsWithName("name"),
  876. "asset.auxiliaryChunkNames": itemsJoinCommaBracketsWithName("auxiliary name"),
  877. "asset.chunkIdHints": itemsJoinCommaBracketsWithName("id hint"),
  878. "asset.auxiliaryChunkIdHints":
  879. itemsJoinCommaBracketsWithName("auxiliary id hint"),
  880. "module.chunks": itemsJoinOneLine,
  881. "module.issuerPath": items =>
  882. items
  883. .filter(Boolean)
  884. .map(item => `${item} ->`)
  885. .join(" "),
  886. "compilation.errors": itemsJoinMoreSpacing,
  887. "compilation.warnings": itemsJoinMoreSpacing,
  888. "compilation.logging": itemsJoinMoreSpacing,
  889. "compilation.children": items => indent(itemsJoinMoreSpacing(items), " "),
  890. "moduleTraceItem.dependencies": itemsJoinOneLine,
  891. "loggingEntry.children": items =>
  892. indent(items.filter(Boolean).join("\n"), " ", false)
  893. };
  894. const joinOneLine = items =>
  895. items
  896. .map(item => item.content)
  897. .filter(Boolean)
  898. .join(" ");
  899. const joinInBrackets = items => {
  900. const res = [];
  901. let mode = 0;
  902. for (const item of items) {
  903. if (item.element === "separator!") {
  904. switch (mode) {
  905. case 0:
  906. case 1:
  907. mode += 2;
  908. break;
  909. case 4:
  910. res.push(")");
  911. mode = 3;
  912. break;
  913. }
  914. }
  915. if (!item.content) continue;
  916. switch (mode) {
  917. case 0:
  918. mode = 1;
  919. break;
  920. case 1:
  921. res.push(" ");
  922. break;
  923. case 2:
  924. res.push("(");
  925. mode = 4;
  926. break;
  927. case 3:
  928. res.push(" (");
  929. mode = 4;
  930. break;
  931. case 4:
  932. res.push(", ");
  933. break;
  934. }
  935. res.push(item.content);
  936. }
  937. if (mode === 4) res.push(")");
  938. return res.join("");
  939. };
  940. const indent = (str, prefix, noPrefixInFirstLine) => {
  941. const rem = str.replace(/\n([^\n])/g, "\n" + prefix + "$1");
  942. if (noPrefixInFirstLine) return rem;
  943. const ind = str[0] === "\n" ? "" : prefix;
  944. return ind + rem;
  945. };
  946. const joinExplicitNewLine = (items, indenter) => {
  947. let firstInLine = true;
  948. let first = true;
  949. return items
  950. .map(item => {
  951. if (!item || !item.content) return;
  952. let content = indent(item.content, first ? "" : indenter, !firstInLine);
  953. if (firstInLine) {
  954. content = content.replace(/^\n+/, "");
  955. }
  956. if (!content) return;
  957. first = false;
  958. const noJoiner = firstInLine || content.startsWith("\n");
  959. firstInLine = content.endsWith("\n");
  960. return noJoiner ? content : " " + content;
  961. })
  962. .filter(Boolean)
  963. .join("")
  964. .trim();
  965. };
  966. const joinError =
  967. error =>
  968. (items, { red, yellow }) =>
  969. `${error ? red("ERROR") : yellow("WARNING")} in ${joinExplicitNewLine(
  970. items,
  971. ""
  972. )}`;
  973. /** @type {Record<string, (items: ({ element: string, content: string })[], context: StatsPrinterContext) => string>} */
  974. const SIMPLE_ELEMENT_JOINERS = {
  975. compilation: items => {
  976. const result = [];
  977. let lastNeedMore = false;
  978. for (const item of items) {
  979. if (!item.content) continue;
  980. const needMoreSpace =
  981. item.element === "warnings" ||
  982. item.element === "filteredWarningDetailsCount" ||
  983. item.element === "errors" ||
  984. item.element === "filteredErrorDetailsCount" ||
  985. item.element === "logging";
  986. if (result.length !== 0) {
  987. result.push(needMoreSpace || lastNeedMore ? "\n\n" : "\n");
  988. }
  989. result.push(item.content);
  990. lastNeedMore = needMoreSpace;
  991. }
  992. if (lastNeedMore) result.push("\n");
  993. return result.join("");
  994. },
  995. asset: items =>
  996. joinExplicitNewLine(
  997. items.map(item => {
  998. if (
  999. (item.element === "related" || item.element === "children") &&
  1000. item.content
  1001. ) {
  1002. return {
  1003. ...item,
  1004. content: `\n${item.content}\n`
  1005. };
  1006. }
  1007. return item;
  1008. }),
  1009. " "
  1010. ),
  1011. "asset.info": joinOneLine,
  1012. module: (items, { module }) => {
  1013. let hasName = false;
  1014. return joinExplicitNewLine(
  1015. items.map(item => {
  1016. switch (item.element) {
  1017. case "id":
  1018. if (module.id === module.name) {
  1019. if (hasName) return false;
  1020. if (item.content) hasName = true;
  1021. }
  1022. break;
  1023. case "name":
  1024. if (hasName) return false;
  1025. if (item.content) hasName = true;
  1026. break;
  1027. case "providedExports":
  1028. case "usedExports":
  1029. case "optimizationBailout":
  1030. case "reasons":
  1031. case "issuerPath":
  1032. case "profile":
  1033. case "children":
  1034. case "modules":
  1035. if (item.content) {
  1036. return {
  1037. ...item,
  1038. content: `\n${item.content}\n`
  1039. };
  1040. }
  1041. break;
  1042. }
  1043. return item;
  1044. }),
  1045. " "
  1046. );
  1047. },
  1048. chunk: items => {
  1049. let hasEntry = false;
  1050. return (
  1051. "chunk " +
  1052. joinExplicitNewLine(
  1053. items.filter(item => {
  1054. switch (item.element) {
  1055. case "entry":
  1056. if (item.content) hasEntry = true;
  1057. break;
  1058. case "initial":
  1059. if (hasEntry) return false;
  1060. break;
  1061. }
  1062. return true;
  1063. }),
  1064. " "
  1065. )
  1066. );
  1067. },
  1068. "chunk.childrenByOrder[]": items => `(${joinOneLine(items)})`,
  1069. chunkGroup: items => joinExplicitNewLine(items, " "),
  1070. chunkGroupAsset: joinOneLine,
  1071. chunkGroupChildGroup: joinOneLine,
  1072. chunkGroupChild: joinOneLine,
  1073. // moduleReason: (items, { moduleReason }) => {
  1074. // let hasName = false;
  1075. // return joinOneLine(
  1076. // items.filter(item => {
  1077. // switch (item.element) {
  1078. // case "moduleId":
  1079. // if (moduleReason.moduleId === moduleReason.module && item.content)
  1080. // hasName = true;
  1081. // break;
  1082. // case "module":
  1083. // if (hasName) return false;
  1084. // break;
  1085. // case "resolvedModule":
  1086. // return (
  1087. // moduleReason.module !== moduleReason.resolvedModule &&
  1088. // item.content
  1089. // );
  1090. // }
  1091. // return true;
  1092. // })
  1093. // );
  1094. // },
  1095. moduleReason: (items, { moduleReason }) => {
  1096. let hasName = false;
  1097. return joinExplicitNewLine(
  1098. items.map(item => {
  1099. switch (item.element) {
  1100. case "moduleId":
  1101. if (moduleReason.moduleId === moduleReason.module && item.content)
  1102. hasName = true;
  1103. break;
  1104. case "module":
  1105. if (hasName) return false;
  1106. break;
  1107. case "resolvedModule":
  1108. if (moduleReason.module === moduleReason.resolvedModule)
  1109. return false;
  1110. break;
  1111. case "children":
  1112. if (item.content) {
  1113. return {
  1114. ...item,
  1115. content: `\n${item.content}\n`
  1116. };
  1117. }
  1118. break;
  1119. }
  1120. return item;
  1121. }),
  1122. " "
  1123. );
  1124. },
  1125. "module.profile": joinInBrackets,
  1126. moduleIssuer: joinOneLine,
  1127. chunkOrigin: items => "> " + joinOneLine(items),
  1128. "errors[].error": joinError(true),
  1129. "warnings[].error": joinError(false),
  1130. loggingGroup: items => joinExplicitNewLine(items, "").trimEnd(),
  1131. moduleTraceItem: items => " @ " + joinOneLine(items),
  1132. moduleTraceDependency: joinOneLine
  1133. };
  1134. const AVAILABLE_COLORS = {
  1135. bold: "\u001b[1m",
  1136. yellow: "\u001b[1m\u001b[33m",
  1137. red: "\u001b[1m\u001b[31m",
  1138. green: "\u001b[1m\u001b[32m",
  1139. cyan: "\u001b[1m\u001b[36m",
  1140. magenta: "\u001b[1m\u001b[35m"
  1141. };
  1142. const AVAILABLE_FORMATS = {
  1143. formatChunkId: (id, { yellow }, direction) => {
  1144. switch (direction) {
  1145. case "parent":
  1146. return `<{${yellow(id)}}>`;
  1147. case "sibling":
  1148. return `={${yellow(id)}}=`;
  1149. case "child":
  1150. return `>{${yellow(id)}}<`;
  1151. default:
  1152. return `{${yellow(id)}}`;
  1153. }
  1154. },
  1155. formatModuleId: id => `[${id}]`,
  1156. formatFilename: (filename, { green, yellow }, oversize) =>
  1157. (oversize ? yellow : green)(filename),
  1158. formatFlag: flag => `[${flag}]`,
  1159. formatLayer: layer => `(in ${layer})`,
  1160. formatSize: require("../SizeFormatHelpers").formatSize,
  1161. formatDateTime: (dateTime, { bold }) => {
  1162. const d = new Date(dateTime);
  1163. const x = twoDigit;
  1164. const date = `${d.getFullYear()}-${x(d.getMonth() + 1)}-${x(d.getDate())}`;
  1165. const time = `${x(d.getHours())}:${x(d.getMinutes())}:${x(d.getSeconds())}`;
  1166. return `${date} ${bold(time)}`;
  1167. },
  1168. formatTime: (
  1169. time,
  1170. { timeReference, bold, green, yellow, red },
  1171. boldQuantity
  1172. ) => {
  1173. const unit = " ms";
  1174. if (timeReference && time !== timeReference) {
  1175. const times = [
  1176. timeReference / 2,
  1177. timeReference / 4,
  1178. timeReference / 8,
  1179. timeReference / 16
  1180. ];
  1181. if (time < times[3]) return `${time}${unit}`;
  1182. else if (time < times[2]) return bold(`${time}${unit}`);
  1183. else if (time < times[1]) return green(`${time}${unit}`);
  1184. else if (time < times[0]) return yellow(`${time}${unit}`);
  1185. else return red(`${time}${unit}`);
  1186. } else {
  1187. return `${boldQuantity ? bold(time) : time}${unit}`;
  1188. }
  1189. },
  1190. formatError: (message, { green, yellow, red }) => {
  1191. if (message.includes("\u001b[")) return message;
  1192. const highlights = [
  1193. { regExp: /(Did you mean .+)/g, format: green },
  1194. {
  1195. regExp: /(Set 'mode' option to 'development' or 'production')/g,
  1196. format: green
  1197. },
  1198. { regExp: /(\(module has no exports\))/g, format: red },
  1199. { regExp: /\(possible exports: (.+)\)/g, format: green },
  1200. { regExp: /(?:^|\n)(.* doesn't exist)/g, format: red },
  1201. { regExp: /('\w+' option has not been set)/g, format: red },
  1202. {
  1203. regExp: /(Emitted value instead of an instance of Error)/g,
  1204. format: yellow
  1205. },
  1206. { regExp: /(Used? .+ instead)/gi, format: yellow },
  1207. { regExp: /\b(deprecated|must|required)\b/g, format: yellow },
  1208. {
  1209. regExp: /\b(BREAKING CHANGE)\b/gi,
  1210. format: red
  1211. },
  1212. {
  1213. regExp:
  1214. /\b(error|failed|unexpected|invalid|not found|not supported|not available|not possible|not implemented|doesn't support|conflict|conflicting|not existing|duplicate)\b/gi,
  1215. format: red
  1216. }
  1217. ];
  1218. for (const { regExp, format } of highlights) {
  1219. message = message.replace(regExp, (match, content) => {
  1220. return match.replace(content, format(content));
  1221. });
  1222. }
  1223. return message;
  1224. }
  1225. };
  1226. const RESULT_MODIFIER = {
  1227. "module.modules": result => {
  1228. return indent(result, "| ");
  1229. }
  1230. };
  1231. const createOrder = (array, preferredOrder) => {
  1232. const originalArray = array.slice();
  1233. const set = new Set(array);
  1234. const usedSet = new Set();
  1235. array.length = 0;
  1236. for (const element of preferredOrder) {
  1237. if (element.endsWith("!") || set.has(element)) {
  1238. array.push(element);
  1239. usedSet.add(element);
  1240. }
  1241. }
  1242. for (const element of originalArray) {
  1243. if (!usedSet.has(element)) {
  1244. array.push(element);
  1245. }
  1246. }
  1247. return array;
  1248. };
  1249. class DefaultStatsPrinterPlugin {
  1250. /**
  1251. * Apply the plugin
  1252. * @param {Compiler} compiler the compiler instance
  1253. * @returns {void}
  1254. */
  1255. apply(compiler) {
  1256. compiler.hooks.compilation.tap("DefaultStatsPrinterPlugin", compilation => {
  1257. compilation.hooks.statsPrinter.tap(
  1258. "DefaultStatsPrinterPlugin",
  1259. (stats, options, context) => {
  1260. // Put colors into context
  1261. stats.hooks.print
  1262. .for("compilation")
  1263. .tap("DefaultStatsPrinterPlugin", (compilation, context) => {
  1264. for (const color of Object.keys(AVAILABLE_COLORS)) {
  1265. let start;
  1266. if (options.colors) {
  1267. if (
  1268. typeof options.colors === "object" &&
  1269. typeof options.colors[color] === "string"
  1270. ) {
  1271. start = options.colors[color];
  1272. } else {
  1273. start = AVAILABLE_COLORS[color];
  1274. }
  1275. }
  1276. if (start) {
  1277. context[color] = str =>
  1278. `${start}${
  1279. typeof str === "string"
  1280. ? str.replace(
  1281. /((\u001b\[39m|\u001b\[22m|\u001b\[0m)+)/g,
  1282. `$1${start}`
  1283. )
  1284. : str
  1285. }\u001b[39m\u001b[22m`;
  1286. } else {
  1287. context[color] = str => str;
  1288. }
  1289. }
  1290. for (const format of Object.keys(AVAILABLE_FORMATS)) {
  1291. context[format] = (content, ...args) =>
  1292. AVAILABLE_FORMATS[format](content, context, ...args);
  1293. }
  1294. context.timeReference = compilation.time;
  1295. });
  1296. for (const key of Object.keys(SIMPLE_PRINTERS)) {
  1297. stats.hooks.print
  1298. .for(key)
  1299. .tap("DefaultStatsPrinterPlugin", (obj, ctx) =>
  1300. SIMPLE_PRINTERS[key](obj, ctx, stats)
  1301. );
  1302. }
  1303. for (const key of Object.keys(PREFERRED_ORDERS)) {
  1304. const preferredOrder = PREFERRED_ORDERS[key];
  1305. stats.hooks.sortElements
  1306. .for(key)
  1307. .tap("DefaultStatsPrinterPlugin", (elements, context) => {
  1308. createOrder(elements, preferredOrder);
  1309. });
  1310. }
  1311. for (const key of Object.keys(ITEM_NAMES)) {
  1312. const itemName = ITEM_NAMES[key];
  1313. stats.hooks.getItemName
  1314. .for(key)
  1315. .tap(
  1316. "DefaultStatsPrinterPlugin",
  1317. typeof itemName === "string" ? () => itemName : itemName
  1318. );
  1319. }
  1320. for (const key of Object.keys(SIMPLE_ITEMS_JOINER)) {
  1321. const joiner = SIMPLE_ITEMS_JOINER[key];
  1322. stats.hooks.printItems
  1323. .for(key)
  1324. .tap("DefaultStatsPrinterPlugin", joiner);
  1325. }
  1326. for (const key of Object.keys(SIMPLE_ELEMENT_JOINERS)) {
  1327. const joiner = SIMPLE_ELEMENT_JOINERS[key];
  1328. stats.hooks.printElements
  1329. .for(key)
  1330. .tap("DefaultStatsPrinterPlugin", joiner);
  1331. }
  1332. for (const key of Object.keys(RESULT_MODIFIER)) {
  1333. const modifier = RESULT_MODIFIER[key];
  1334. stats.hooks.result
  1335. .for(key)
  1336. .tap("DefaultStatsPrinterPlugin", modifier);
  1337. }
  1338. }
  1339. );
  1340. });
  1341. }
  1342. }
  1343. module.exports = DefaultStatsPrinterPlugin;