DefaultStatsPrinterPlugin.js 41 KB

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