DefaultStatsPrinterPlugin.js 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. /** @import { ChunkId, ChunkName } from "../Chunk" */
  7. /** @import Compiler from "../Compiler" */
  8. /** @import { LogTypeEnum } from "../logging/Logger" */
  9. /**
  10. * @import {
  11. * KnownStatsAsset,
  12. * KnownStatsChunk,
  13. * KnownStatsChunkGroup,
  14. * KnownStatsChunkOrigin,
  15. * KnownStatsCompilation,
  16. * KnownStatsError,
  17. * KnownStatsLogging,
  18. * KnownStatsLoggingEntry,
  19. * KnownStatsModule,
  20. * KnownStatsModuleIssuer,
  21. * KnownStatsModuleReason,
  22. * KnownStatsModuleTraceDependency,
  23. * KnownStatsModuleTraceItem,
  24. * KnownStatsProfile
  25. * } from "./DefaultStatsFactoryPlugin"
  26. */
  27. /**
  28. * @import StatsPrinter, {
  29. * ColorFunction,
  30. * KnownStatsPrinterColorFunctions,
  31. * KnownStatsPrinterContext,
  32. * KnownStatsPrinterFormatters,
  33. * StatsPrinterContext,
  34. * StatsPrinterContextWithExtra
  35. * } from "./StatsPrinter"
  36. */
  37. const DATA_URI_CONTENT_LENGTH = 16;
  38. const MAX_MODULE_IDENTIFIER_LENGTH = 80;
  39. /**
  40. * Returns if n is 1, singular, else plural.
  41. * @param {number} n a number
  42. * @param {string} singular singular
  43. * @param {string} plural plural
  44. * @returns {string} if n is 1, singular, else plural
  45. */
  46. const plural = (n, singular, plural) => (n === 1 ? singular : plural);
  47. /**
  48. * Returns text.
  49. * @param {Record<string, number>} sizes sizes by source type
  50. * @param {StatsPrinterContext} options options
  51. * @returns {string | undefined} text
  52. */
  53. const printSizes = (sizes, { formatSize = (n) => `${n}` }) => {
  54. const keys = Object.keys(sizes);
  55. if (keys.length > 1) {
  56. return keys.map((key) => `${formatSize(sizes[key])} (${key})`).join(" ");
  57. } else if (keys.length === 1) {
  58. return formatSize(sizes[keys[0]]);
  59. }
  60. };
  61. /**
  62. * Gets resource name.
  63. * @param {string | null} resource resource
  64. * @returns {string} resource name for display
  65. */
  66. const getResourceName = (resource) => {
  67. if (!resource) return "";
  68. const dataUrl = /^data:[^,]+,/.exec(resource);
  69. if (!dataUrl) return resource;
  70. const len = dataUrl[0].length + DATA_URI_CONTENT_LENGTH;
  71. if (resource.length < len) return resource;
  72. return `${resource.slice(
  73. 0,
  74. Math.min(resource.length - /* '..'.length */ 2, len)
  75. )}..`;
  76. };
  77. /**
  78. * Returns prefix and module name.
  79. * @param {string} name module name
  80. * @returns {[string, string]} prefix and module name
  81. */
  82. const getModuleName = (name) => {
  83. const [, prefix, resource] =
  84. /** @type {[string, string, string]} */
  85. (/** @type {unknown} */ (/^(.*!)?([^!]*)$/.exec(name)));
  86. if (resource.length > MAX_MODULE_IDENTIFIER_LENGTH) {
  87. const truncatedResource = `${resource.slice(
  88. 0,
  89. Math.min(
  90. resource.length - /* '...(truncated)'.length */ 14,
  91. MAX_MODULE_IDENTIFIER_LENGTH
  92. )
  93. )}...(truncated)`;
  94. return [prefix, getResourceName(truncatedResource)];
  95. }
  96. return [prefix, getResourceName(resource)];
  97. };
  98. /**
  99. * Returns joined string.
  100. * @param {string} str string
  101. * @param {(item: string) => string} fn function to apply to each line
  102. * @returns {string} joined string
  103. */
  104. const mapLines = (str, fn) => str.split("\n").map(fn).join("\n");
  105. /**
  106. * Returns number as two digit string, leading 0.
  107. * @param {number} n a number
  108. * @returns {string} number as two digit string, leading 0
  109. */
  110. const twoDigit = (n) => (n >= 10 ? `${n}` : `0${n}`);
  111. /**
  112. * Checks whether this object is valid id.
  113. * @param {string | number | null} id an id
  114. * @returns {id is string | number} is i
  115. */
  116. const isValidId = (id) => {
  117. if (typeof id === "number" || id) {
  118. return true;
  119. }
  120. return false;
  121. };
  122. /**
  123. * Returns string representation of list.
  124. * @template T
  125. * @param {T[] | undefined} list of items
  126. * @param {number} count number of items to show
  127. * @returns {string} string representation of list
  128. */
  129. const moreCount = (list, count) =>
  130. list && list.length > 0 ? `+ ${count}` : `${count}`;
  131. /**
  132. * Defines the with required type used by this module.
  133. * @template T
  134. * @template {keyof T} K
  135. * @typedef {{ [P in K]-?: T[P] }} WithRequired
  136. */
  137. /**
  138. * Defines the define stats printer context type used by this module.
  139. * @template {keyof StatsPrinterContext} RequiredStatsPrinterContextKeys
  140. * @typedef {StatsPrinterContextWithExtra & WithRequired<StatsPrinterContext, "compilation" | RequiredStatsPrinterContextKeys>} DefineStatsPrinterContext
  141. */
  142. /**
  143. * Defines the simple printer type used by this module.
  144. * @template T
  145. * @template {keyof StatsPrinterContext} RequiredStatsPrinterContextKeys
  146. * @typedef {(thing: Exclude<T, undefined>, context: DefineStatsPrinterContext<RequiredStatsPrinterContextKeys>, printer: StatsPrinter) => string | undefined} SimplePrinter
  147. */
  148. /**
  149. * Defines the unpacked type used by this module.
  150. * @template T
  151. * @typedef {T extends (infer U)[] ? U : T} Unpacked
  152. */
  153. /**
  154. * Defines the property name type used by this module.
  155. * @template {object} O
  156. * @template {keyof O} K
  157. * @template {string} B
  158. * @typedef {K extends string ? `${B}.${K}` : never} PropertyName
  159. */
  160. /**
  161. * Defines the array property name type used by this module.
  162. * @template {object} O
  163. * @template {keyof O} K
  164. * @template {string} B
  165. * @typedef {K extends string ? `${B}.${K}[]` : never} ArrayPropertyName
  166. */
  167. /**
  168. * Defines the exclamation type used by this module.
  169. * @template {object} O
  170. * @template {string} K
  171. * @template {string} E
  172. * @typedef {{ [property in `${K}!`]?: SimplePrinter<O, "compilation" | E> }} Exclamation
  173. */
  174. /**
  175. * Defines the shared type used by this module.
  176. * @template {object} O
  177. * @template {string} B
  178. * @template {string} [R=B]
  179. * @typedef {{ [K in keyof O as PropertyName<O, K, B>]?: SimplePrinter<O[K], R> } &
  180. * { [K in keyof O as ArrayPropertyName<O, K, B>]?: Exclude<O[K], undefined> extends (infer I)[] ? SimplePrinter<I, R> : never }} Printers
  181. */
  182. /**
  183. * Defines the shared type used by this module.
  184. * @typedef {Printers<KnownStatsCompilation, "compilation"> &
  185. * { ["compilation.summary!"]?: SimplePrinter<KnownStatsCompilation, "compilation"> } &
  186. * { ["compilation.errorsInChildren!"]?: SimplePrinter<KnownStatsCompilation, "compilation"> } &
  187. * { ["compilation.warningsInChildren!"]?: SimplePrinter<KnownStatsCompilation, "compilation"> }} CompilationSimplePrinters
  188. */
  189. /**
  190. * @type {CompilationSimplePrinters}
  191. */
  192. const COMPILATION_SIMPLE_PRINTERS = {
  193. "compilation.summary!": (
  194. _,
  195. {
  196. type,
  197. bold,
  198. green,
  199. red,
  200. yellow,
  201. formatDateTime,
  202. formatTime,
  203. compilation: {
  204. name,
  205. hash,
  206. version,
  207. time,
  208. builtAt,
  209. errorsCount,
  210. warningsCount
  211. }
  212. }
  213. ) => {
  214. const root = type === "compilation.summary!";
  215. const warningsMessage =
  216. /** @type {number} */ (warningsCount) > 0
  217. ? yellow(
  218. `${warningsCount} ${plural(/** @type {number} */ (warningsCount), "warning", "warnings")}`
  219. )
  220. : "";
  221. const errorsMessage =
  222. /** @type {number} */ (errorsCount) > 0
  223. ? red(
  224. `${errorsCount} ${plural(/** @type {number} */ (errorsCount), "error", "errors")}`
  225. )
  226. : "";
  227. const timeMessage = root && time ? ` in ${formatTime(time)}` : "";
  228. const hashMessage = hash ? ` (${hash})` : "";
  229. const builtAtMessage =
  230. root && builtAt ? `${formatDateTime(builtAt)}: ` : "";
  231. const versionMessage = root && version ? `webpack ${version}` : "";
  232. const nameMessage =
  233. root && name
  234. ? bold(name)
  235. : name
  236. ? `Child ${bold(name)}`
  237. : root
  238. ? ""
  239. : "Child";
  240. const subjectMessage =
  241. nameMessage && versionMessage
  242. ? `${nameMessage} (${versionMessage})`
  243. : versionMessage || nameMessage || "webpack";
  244. /** @type {string} */
  245. let statusMessage;
  246. if (errorsMessage && warningsMessage) {
  247. statusMessage = `compiled with ${errorsMessage} and ${warningsMessage}`;
  248. } else if (errorsMessage) {
  249. statusMessage = `compiled with ${errorsMessage}`;
  250. } else if (warningsMessage) {
  251. statusMessage = `compiled with ${warningsMessage}`;
  252. } else if (errorsCount === 0 && warningsCount === 0) {
  253. statusMessage = `compiled ${green("successfully")}`;
  254. } else {
  255. statusMessage = "compiled";
  256. }
  257. if (
  258. builtAtMessage ||
  259. versionMessage ||
  260. errorsMessage ||
  261. warningsMessage ||
  262. (errorsCount === 0 && warningsCount === 0) ||
  263. timeMessage ||
  264. hashMessage
  265. ) {
  266. return `${builtAtMessage}${subjectMessage} ${statusMessage}${timeMessage}${hashMessage}`;
  267. }
  268. },
  269. "compilation.filteredWarningDetailsCount": (count) =>
  270. count
  271. ? `${count} ${plural(
  272. count,
  273. "warning has",
  274. "warnings have"
  275. )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.`
  276. : undefined,
  277. "compilation.filteredErrorDetailsCount": (count, { yellow }) =>
  278. count
  279. ? yellow(
  280. `${count} ${plural(
  281. count,
  282. "error has",
  283. "errors have"
  284. )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.`
  285. )
  286. : undefined,
  287. "compilation.env": (env, { bold }) =>
  288. env
  289. ? `Environment (--env): ${bold(JSON.stringify(env, null, 2))}`
  290. : undefined,
  291. "compilation.publicPath": (publicPath, { bold }) =>
  292. `PublicPath: ${bold(publicPath || "(none)")}`,
  293. "compilation.entrypoints": (entrypoints, context, printer) =>
  294. Array.isArray(entrypoints)
  295. ? undefined
  296. : printer.print(context.type, Object.values(entrypoints), {
  297. ...context,
  298. chunkGroupKind: "Entrypoint"
  299. }),
  300. "compilation.namedChunkGroups": (namedChunkGroups, context, printer) => {
  301. if (!Array.isArray(namedChunkGroups)) {
  302. const {
  303. compilation: { entrypoints }
  304. } = context;
  305. let chunkGroups = Object.values(namedChunkGroups);
  306. if (entrypoints) {
  307. chunkGroups = chunkGroups.filter(
  308. (group) =>
  309. !Object.prototype.hasOwnProperty.call(
  310. entrypoints,
  311. /** @type {string} */
  312. (group.name)
  313. )
  314. );
  315. }
  316. return printer.print(context.type, chunkGroups, {
  317. ...context,
  318. chunkGroupKind: "Chunk Group"
  319. });
  320. }
  321. },
  322. "compilation.assetsByChunkName": () => "",
  323. "compilation.filteredModules": (
  324. filteredModules,
  325. { compilation: { modules } }
  326. ) =>
  327. filteredModules > 0
  328. ? `${moreCount(modules, filteredModules)} ${plural(
  329. filteredModules,
  330. "module",
  331. "modules"
  332. )}`
  333. : undefined,
  334. "compilation.filteredAssets": (
  335. filteredAssets,
  336. { compilation: { assets } }
  337. ) =>
  338. filteredAssets > 0
  339. ? `${moreCount(assets, filteredAssets)} ${plural(
  340. filteredAssets,
  341. "asset",
  342. "assets"
  343. )}`
  344. : undefined,
  345. "compilation.logging": (logging, context, printer) =>
  346. Array.isArray(logging)
  347. ? undefined
  348. : printer.print(
  349. context.type,
  350. Object.entries(logging).map(([name, value]) => ({ ...value, name })),
  351. context
  352. ),
  353. "compilation.warningsInChildren!": (_, { yellow, compilation }) => {
  354. if (
  355. !compilation.children &&
  356. /** @type {number} */ (compilation.warningsCount) > 0 &&
  357. compilation.warnings
  358. ) {
  359. const childWarnings =
  360. /** @type {number} */ (compilation.warningsCount) -
  361. compilation.warnings.length;
  362. if (childWarnings > 0) {
  363. return yellow(
  364. `${childWarnings} ${plural(
  365. childWarnings,
  366. "WARNING",
  367. "WARNINGS"
  368. )} in child compilations${
  369. compilation.children
  370. ? ""
  371. : " (Use 'stats.children: true' resp. '--stats-children' for more details)"
  372. }`
  373. );
  374. }
  375. }
  376. },
  377. "compilation.errorsInChildren!": (_, { red, compilation }) => {
  378. if (
  379. !compilation.children &&
  380. /** @type {number} */ (compilation.errorsCount) > 0 &&
  381. compilation.errors
  382. ) {
  383. const childErrors =
  384. /** @type {number} */ (compilation.errorsCount) -
  385. compilation.errors.length;
  386. if (childErrors > 0) {
  387. return red(
  388. `${childErrors} ${plural(
  389. childErrors,
  390. "ERROR",
  391. "ERRORS"
  392. )} in child compilations${
  393. compilation.children
  394. ? ""
  395. : " (Use 'stats.children: true' resp. '--stats-children' for more details)"
  396. }`
  397. );
  398. }
  399. }
  400. }
  401. };
  402. /**
  403. * Defines the shared type used by this module.
  404. * @typedef {Printers<KnownStatsAsset, "asset"> &
  405. * Printers<KnownStatsAsset["info"], "asset.info"> &
  406. * Exclamation<KnownStatsAsset, "asset.separator", "asset"> &
  407. * { ["asset.filteredChildren"]?: SimplePrinter<number, "asset"> } &
  408. * { assetChunk?: SimplePrinter<ChunkId, "asset"> } &
  409. * { assetChunkName?: SimplePrinter<ChunkName, "asset"> } &
  410. * { assetChunkIdHint?: SimplePrinter<string, "asset"> }} AssetSimplePrinters
  411. */
  412. /** @type {AssetSimplePrinters} */
  413. const ASSET_SIMPLE_PRINTERS = {
  414. "asset.type": (type) => type,
  415. "asset.name": (name, { formatFilename, asset: { isOverSizeLimit } }) =>
  416. formatFilename(name, isOverSizeLimit),
  417. "asset.size": (size, { asset: { isOverSizeLimit }, yellow, formatSize }) =>
  418. isOverSizeLimit ? yellow(formatSize(size)) : formatSize(size),
  419. "asset.emitted": (emitted, { green, formatFlag }) =>
  420. emitted ? green(formatFlag("emitted")) : undefined,
  421. "asset.comparedForEmit": (comparedForEmit, { yellow, formatFlag }) =>
  422. comparedForEmit ? yellow(formatFlag("compared for emit")) : undefined,
  423. "asset.cached": (cached, { green, formatFlag }) =>
  424. cached ? green(formatFlag("cached")) : undefined,
  425. "asset.isOverSizeLimit": (isOverSizeLimit, { yellow, formatFlag }) =>
  426. isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
  427. "asset.info.immutable": (immutable, { green, formatFlag }) =>
  428. immutable ? green(formatFlag("immutable")) : undefined,
  429. "asset.info.javascriptModule": (javascriptModule, { formatFlag }) =>
  430. javascriptModule ? formatFlag("javascript module") : undefined,
  431. "asset.info.sourceFilename": (sourceFilename, { formatFlag }) =>
  432. sourceFilename ? formatFlag(`from: ${sourceFilename}`) : undefined,
  433. "asset.info.development": (development, { green, formatFlag }) =>
  434. development ? green(formatFlag("dev")) : undefined,
  435. "asset.info.hotModuleReplacement": (
  436. hotModuleReplacement,
  437. { green, formatFlag }
  438. ) => (hotModuleReplacement ? green(formatFlag("hmr")) : undefined),
  439. "asset.separator!": () => "\n",
  440. "asset.filteredRelated": (filteredRelated, { asset: { related } }) =>
  441. filteredRelated > 0
  442. ? `${moreCount(related, filteredRelated)} related ${plural(
  443. filteredRelated,
  444. "asset",
  445. "assets"
  446. )}`
  447. : undefined,
  448. "asset.filteredChildren": (filteredChildren, { asset: { children } }) =>
  449. filteredChildren > 0
  450. ? `${moreCount(children, filteredChildren)} ${plural(
  451. filteredChildren,
  452. "asset",
  453. "assets"
  454. )}`
  455. : undefined,
  456. assetChunk: (id, { formatChunkId }) => formatChunkId(id),
  457. assetChunkName: (name) => name || undefined,
  458. assetChunkIdHint: (name) => name || undefined
  459. };
  460. /**
  461. * Defines the shared type used by this module.
  462. * @typedef {Printers<KnownStatsModule, "module"> &
  463. * Exclamation<KnownStatsModule, "module.separator", "module"> &
  464. * { ["module.filteredChildren"]?: SimplePrinter<number, "module"> } &
  465. * { ["module.filteredReasons"]?: SimplePrinter<number, "module"> }} ModuleSimplePrinters
  466. */
  467. /** @type {ModuleSimplePrinters} */
  468. const MODULE_SIMPLE_PRINTERS = {
  469. "module.type": (type) => (type !== "module" ? type : undefined),
  470. "module.id": (id, { formatModuleId }) =>
  471. isValidId(id) ? formatModuleId(id) : undefined,
  472. "module.name": (name, { bold }) => {
  473. const [prefix, resource] = getModuleName(name);
  474. return `${prefix || ""}${bold(resource || "")}`;
  475. },
  476. "module.identifier": (_identifier) => undefined,
  477. "module.layer": (layer, { formatLayer }) =>
  478. layer ? formatLayer(layer) : undefined,
  479. "module.sizes": printSizes,
  480. "module.chunks[]": (id, { formatChunkId }) => formatChunkId(id),
  481. "module.depth": (depth, { formatFlag }) =>
  482. depth !== null ? formatFlag(`depth ${depth}`) : undefined,
  483. "module.cacheable": (cacheable, { formatFlag, red, module }) =>
  484. cacheable === false
  485. ? red(
  486. formatFlag(
  487. module.notCacheableReasons && module.notCacheableReasons.length > 0
  488. ? `not cacheable: ${module.notCacheableReasons.join(", ")}`
  489. : "not cacheable"
  490. )
  491. )
  492. : undefined,
  493. // rendered as part of the "module.cacheable" flag
  494. "module.notCacheableReasons": () => undefined,
  495. "module.orphan": (orphan, { formatFlag, yellow }) =>
  496. orphan ? yellow(formatFlag("orphan")) : undefined,
  497. // "module.runtime": (runtime, { formatFlag, yellow }) =>
  498. // runtime ? yellow(formatFlag("runtime")) : undefined,
  499. "module.optional": (optional, { formatFlag, yellow }) =>
  500. optional ? yellow(formatFlag("optional")) : undefined,
  501. "module.dependent": (dependent, { formatFlag, cyan }) =>
  502. dependent ? cyan(formatFlag("dependent")) : undefined,
  503. "module.built": (built, { formatFlag, yellow }) =>
  504. built ? yellow(formatFlag("built")) : undefined,
  505. "module.codeGenerated": (codeGenerated, { formatFlag, yellow }) =>
  506. codeGenerated ? yellow(formatFlag("code generated")) : undefined,
  507. "module.buildTimeExecuted": (buildTimeExecuted, { formatFlag, green }) =>
  508. buildTimeExecuted ? green(formatFlag("build time executed")) : undefined,
  509. "module.cached": (cached, { formatFlag, green }) =>
  510. cached ? green(formatFlag("cached")) : undefined,
  511. "module.assets": (assets, { formatFlag, magenta }) =>
  512. assets && assets.length
  513. ? magenta(
  514. formatFlag(
  515. `${assets.length} ${plural(assets.length, "asset", "assets")}`
  516. )
  517. )
  518. : undefined,
  519. "module.warnings": (warnings, { formatFlag, yellow }) =>
  520. warnings
  521. ? yellow(
  522. formatFlag(`${warnings} ${plural(warnings, "warning", "warnings")}`)
  523. )
  524. : undefined,
  525. "module.errors": (errors, { formatFlag, red }) =>
  526. errors
  527. ? red(formatFlag(`${errors} ${plural(errors, "error", "errors")}`))
  528. : undefined,
  529. "module.providedExports": (providedExports, { formatFlag, cyan }) => {
  530. if (Array.isArray(providedExports)) {
  531. if (providedExports.length === 0) return cyan(formatFlag("no exports"));
  532. return cyan(formatFlag(`exports: ${providedExports.join(", ")}`));
  533. }
  534. },
  535. "module.usedExports": (usedExports, { formatFlag, cyan, module }) => {
  536. if (usedExports !== true) {
  537. if (usedExports === null) return cyan(formatFlag("used exports unknown"));
  538. if (usedExports === false) return cyan(formatFlag("module unused"));
  539. if (Array.isArray(usedExports)) {
  540. if (usedExports.length === 0) {
  541. return cyan(formatFlag("no exports used"));
  542. }
  543. const providedExportsCount = Array.isArray(module.providedExports)
  544. ? module.providedExports.length
  545. : null;
  546. if (
  547. providedExportsCount !== null &&
  548. providedExportsCount === usedExports.length
  549. ) {
  550. return cyan(formatFlag("all exports used"));
  551. }
  552. return cyan(
  553. formatFlag(`only some exports used: ${usedExports.join(", ")}`)
  554. );
  555. }
  556. }
  557. },
  558. "module.optimizationBailout[]": (optimizationBailout, { yellow }) =>
  559. yellow(optimizationBailout),
  560. "module.issuerPath": (issuerPath, { module }) =>
  561. module.profile ? undefined : "",
  562. "module.profile": (_profile) => undefined,
  563. "module.filteredModules": (filteredModules, { module: { modules } }) =>
  564. filteredModules > 0
  565. ? `${moreCount(modules, filteredModules)} nested ${plural(
  566. filteredModules,
  567. "module",
  568. "modules"
  569. )}`
  570. : undefined,
  571. "module.filteredReasons": (filteredReasons, { module: { reasons } }) =>
  572. filteredReasons > 0
  573. ? `${moreCount(reasons, filteredReasons)} ${plural(
  574. filteredReasons,
  575. "reason",
  576. "reasons"
  577. )}`
  578. : undefined,
  579. "module.filteredChildren": (filteredChildren, { module: { children } }) =>
  580. filteredChildren > 0
  581. ? `${moreCount(children, filteredChildren)} ${plural(
  582. filteredChildren,
  583. "module",
  584. "modules"
  585. )}`
  586. : undefined,
  587. "module.separator!": () => "\n"
  588. };
  589. /**
  590. * Defines the module issuer printers type used by this module.
  591. * @typedef {Printers<KnownStatsModuleIssuer, "moduleIssuer"> & Printers<KnownStatsModuleIssuer["profile"], "moduleIssuer.profile", "moduleIssuer">} ModuleIssuerPrinters
  592. */
  593. /** @type {ModuleIssuerPrinters} */
  594. const MODULE_ISSUER_PRINTERS = {
  595. "moduleIssuer.id": (id, { formatModuleId }) => formatModuleId(id),
  596. "moduleIssuer.profile.total": (value, { formatTime }) => formatTime(value)
  597. };
  598. /**
  599. * Defines the module reasons printers type used by this module.
  600. * @typedef {Printers<KnownStatsModuleReason, "moduleReason"> & { ["moduleReason.filteredChildren"]?: SimplePrinter<number, "moduleReason"> }} ModuleReasonsPrinters
  601. */
  602. /** @type {ModuleReasonsPrinters} */
  603. const MODULE_REASON_PRINTERS = {
  604. "moduleReason.type": (type) => type || undefined,
  605. "moduleReason.userRequest": (userRequest, { cyan }) =>
  606. cyan(getResourceName(userRequest)),
  607. "moduleReason.moduleId": (moduleId, { formatModuleId }) =>
  608. isValidId(moduleId) ? formatModuleId(moduleId) : undefined,
  609. "moduleReason.module": (module, { magenta }) =>
  610. module ? magenta(module) : undefined,
  611. "moduleReason.loc": (loc) => loc || undefined,
  612. "moduleReason.explanation": (explanation, { cyan }) =>
  613. explanation ? cyan(explanation) : undefined,
  614. "moduleReason.active": (active, { formatFlag }) =>
  615. active ? undefined : formatFlag("inactive"),
  616. "moduleReason.resolvedModule": (module, { magenta }) =>
  617. module ? magenta(module) : undefined,
  618. "moduleReason.filteredChildren": (
  619. filteredChildren,
  620. { moduleReason: { children } }
  621. ) =>
  622. filteredChildren > 0
  623. ? `${moreCount(children, filteredChildren)} ${plural(
  624. filteredChildren,
  625. "reason",
  626. "reasons"
  627. )}`
  628. : undefined
  629. };
  630. /** @typedef {Printers<KnownStatsProfile, "module.profile", "profile">} ModuleProfilePrinters */
  631. /** @type {ModuleProfilePrinters} */
  632. const MODULE_PROFILE_PRINTERS = {
  633. "module.profile.total": (value, { formatTime }) => formatTime(value),
  634. "module.profile.resolving": (value, { formatTime }) =>
  635. `resolving: ${formatTime(value)}`,
  636. "module.profile.restoring": (value, { formatTime }) =>
  637. `restoring: ${formatTime(value)}`,
  638. "module.profile.integration": (value, { formatTime }) =>
  639. `integration: ${formatTime(value)}`,
  640. "module.profile.building": (value, { formatTime }) =>
  641. `building: ${formatTime(value)}`,
  642. "module.profile.storing": (value, { formatTime }) =>
  643. `storing: ${formatTime(value)}`,
  644. "module.profile.additionalResolving": (value, { formatTime }) =>
  645. value ? `additional resolving: ${formatTime(value)}` : undefined,
  646. "module.profile.additionalIntegration": (value, { formatTime }) =>
  647. value ? `additional integration: ${formatTime(value)}` : undefined
  648. };
  649. /**
  650. * Defines the shared type used by this module.
  651. * @typedef {Exclamation<KnownStatsChunkGroup, "chunkGroup.kind", "chunkGroupKind"> &
  652. * Exclamation<KnownStatsChunkGroup, "chunkGroup.separator", "chunkGroup"> &
  653. * Printers<KnownStatsChunkGroup, "chunkGroup"> &
  654. * Exclamation<KnownStatsChunkGroup, "chunkGroup.is", "chunkGroup"> &
  655. * Printers<Exclude<KnownStatsChunkGroup["assets"], undefined>[number], "chunkGroupAsset" | "chunkGroup"> &
  656. * { ['chunkGroupChildGroup.type']?: SimplePrinter<string, "chunkGroupAsset"> } &
  657. * { ['chunkGroupChild.assets[]']?: SimplePrinter<string, "chunkGroupAsset"> } &
  658. * { ['chunkGroupChild.chunks[]']?: SimplePrinter<ChunkId, "chunkGroupAsset"> } &
  659. * { ['chunkGroupChild.name']?: SimplePrinter<ChunkName, "chunkGroupAsset"> }} ChunkGroupPrinters
  660. */
  661. /** @type {ChunkGroupPrinters} */
  662. const CHUNK_GROUP_PRINTERS = {
  663. "chunkGroup.kind!": (_, { chunkGroupKind }) => chunkGroupKind,
  664. "chunkGroup.separator!": () => "\n",
  665. "chunkGroup.name": (name, { bold }) => (name ? bold(name) : undefined),
  666. "chunkGroup.isOverSizeLimit": (isOverSizeLimit, { formatFlag, yellow }) =>
  667. isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
  668. "chunkGroup.assetsSize": (size, { formatSize }) =>
  669. size ? formatSize(size) : undefined,
  670. "chunkGroup.auxiliaryAssetsSize": (size, { formatSize }) =>
  671. size ? `(${formatSize(size)})` : undefined,
  672. "chunkGroup.filteredAssets": (n, { chunkGroup: { assets } }) =>
  673. n > 0
  674. ? `${moreCount(assets, n)} ${plural(n, "asset", "assets")}`
  675. : undefined,
  676. "chunkGroup.filteredAuxiliaryAssets": (
  677. n,
  678. { chunkGroup: { auxiliaryAssets } }
  679. ) =>
  680. n > 0
  681. ? `${moreCount(auxiliaryAssets, n)} auxiliary ${plural(
  682. n,
  683. "asset",
  684. "assets"
  685. )}`
  686. : undefined,
  687. "chunkGroup.is!": () => "=",
  688. "chunkGroupAsset.name": (asset, { green }) => green(asset),
  689. "chunkGroupAsset.size": (size, { formatSize, chunkGroup }) =>
  690. chunkGroup.assets &&
  691. (chunkGroup.assets.length > 1 ||
  692. (chunkGroup.auxiliaryAssets && chunkGroup.auxiliaryAssets.length > 0)
  693. ? formatSize(size)
  694. : undefined),
  695. "chunkGroup.children": (children, context, printer) =>
  696. Array.isArray(children)
  697. ? undefined
  698. : printer.print(
  699. context.type,
  700. Object.keys(children).map((key) => ({
  701. type: key,
  702. children: children[key]
  703. })),
  704. context
  705. ),
  706. "chunkGroupChildGroup.type": (type) => `${type}:`,
  707. "chunkGroupChild.assets[]": (file, { formatFilename }) =>
  708. formatFilename(file),
  709. "chunkGroupChild.chunks[]": (id, { formatChunkId }) => formatChunkId(id),
  710. "chunkGroupChild.name": (name) => (name ? `(name: ${name})` : undefined)
  711. };
  712. /**
  713. * Defines the shared type used by this module.
  714. * @typedef {Printers<KnownStatsChunk, "chunk"> &
  715. * { ["chunk.childrenByOrder[].type"]: SimplePrinter<string, "chunk"> } &
  716. * { ["chunk.childrenByOrder[].children[]"]: SimplePrinter<ChunkId, "chunk"> } &
  717. * Exclamation<KnownStatsChunk, "chunk.separator", "chunk"> &
  718. * Printers<KnownStatsChunkOrigin, "chunkOrigin">} ChunkPrinters
  719. */
  720. /** @type {ChunkPrinters} */
  721. const CHUNK_PRINTERS = {
  722. "chunk.id": (id, { formatChunkId }) => formatChunkId(id),
  723. "chunk.files[]": (file, { formatFilename }) => formatFilename(file),
  724. "chunk.names[]": (name) => name,
  725. "chunk.idHints[]": (name) => name,
  726. "chunk.runtime[]": (name) => name,
  727. "chunk.sizes": (sizes, context) => printSizes(sizes, context),
  728. "chunk.parents[]": (parents, context) =>
  729. context.formatChunkId(parents, "parent"),
  730. "chunk.siblings[]": (siblings, context) =>
  731. context.formatChunkId(siblings, "sibling"),
  732. "chunk.children[]": (children, context) =>
  733. context.formatChunkId(children, "child"),
  734. "chunk.childrenByOrder": (childrenByOrder, context, printer) =>
  735. Array.isArray(childrenByOrder)
  736. ? undefined
  737. : printer.print(
  738. context.type,
  739. Object.keys(childrenByOrder).map((key) => ({
  740. type: key,
  741. children: childrenByOrder[key]
  742. })),
  743. context
  744. ),
  745. "chunk.childrenByOrder[].type": (type) => `${type}:`,
  746. "chunk.childrenByOrder[].children[]": (id, { formatChunkId }) =>
  747. isValidId(id) ? formatChunkId(id) : undefined,
  748. "chunk.entry": (entry, { formatFlag, yellow }) =>
  749. entry ? yellow(formatFlag("entry")) : undefined,
  750. "chunk.initial": (initial, { formatFlag, yellow }) =>
  751. initial ? yellow(formatFlag("initial")) : undefined,
  752. "chunk.rendered": (rendered, { formatFlag, green }) =>
  753. rendered ? green(formatFlag("rendered")) : undefined,
  754. "chunk.recorded": (recorded, { formatFlag, green }) =>
  755. recorded ? green(formatFlag("recorded")) : undefined,
  756. "chunk.reason": (reason, { yellow }) => (reason ? yellow(reason) : undefined),
  757. "chunk.filteredModules": (filteredModules, { chunk: { modules } }) =>
  758. filteredModules > 0
  759. ? `${moreCount(modules, filteredModules)} chunk ${plural(
  760. filteredModules,
  761. "module",
  762. "modules"
  763. )}`
  764. : undefined,
  765. "chunk.separator!": () => "\n",
  766. "chunkOrigin.request": (request) => request,
  767. "chunkOrigin.moduleId": (moduleId, { formatModuleId }) =>
  768. isValidId(moduleId) ? formatModuleId(moduleId) : undefined,
  769. "chunkOrigin.moduleName": (moduleName, { bold }) => bold(moduleName),
  770. "chunkOrigin.loc": (loc) => loc
  771. };
  772. /**
  773. * Defines the shared type used by this module.
  774. * @typedef {Printers<KnownStatsError, "error"> &
  775. * { ["error.filteredDetails"]?: SimplePrinter<number, "error"> } &
  776. * Exclamation<KnownStatsError, "error.separator", "error">} ErrorPrinters
  777. */
  778. /**
  779. * @type {ErrorPrinters}
  780. */
  781. const ERROR_PRINTERS = {
  782. "error.compilerPath": (compilerPath, { bold }) =>
  783. compilerPath ? bold(`(${compilerPath})`) : undefined,
  784. "error.chunkId": (chunkId, { formatChunkId }) =>
  785. isValidId(chunkId) ? formatChunkId(chunkId) : undefined,
  786. "error.chunkEntry": (chunkEntry, { formatFlag }) =>
  787. chunkEntry ? formatFlag("entry") : undefined,
  788. "error.chunkInitial": (chunkInitial, { formatFlag }) =>
  789. chunkInitial ? formatFlag("initial") : undefined,
  790. "error.file": (file, { bold }) => bold(file),
  791. "error.moduleName": (moduleName, { bold }) =>
  792. moduleName.includes("!")
  793. ? `${bold(moduleName.replace(/^([\s\S])*!/, ""))} (${moduleName})`
  794. : `${bold(moduleName)}`,
  795. "error.loc": (loc, { green }) => green(loc),
  796. "error.message": (message, { bold, formatError }) =>
  797. message.includes("\u001B[") ? message : bold(formatError(message)),
  798. "error.details": (details, { formatError }) => formatError(details),
  799. "error.filteredDetails": (filteredDetails) =>
  800. filteredDetails ? `+ ${filteredDetails} hidden lines` : undefined,
  801. "error.stack": (stack) => stack,
  802. "error.cause": (cause, context, printer) =>
  803. cause
  804. ? indent(
  805. `[cause]: ${
  806. /** @type {string} */
  807. (printer.print(`${context.type}.error`, cause, context))
  808. }`,
  809. " "
  810. )
  811. : undefined,
  812. "error.moduleTrace": (_moduleTrace) => undefined,
  813. "error.separator!": () => "\n"
  814. };
  815. /**
  816. * Defines the shared type used by this module.
  817. * @typedef {Printers<KnownStatsLoggingEntry, `loggingEntry(${LogTypeEnum}).loggingEntry`> &
  818. * { ["loggingEntry(clear).loggingEntry"]?: SimplePrinter<KnownStatsLoggingEntry, "logging"> } &
  819. * { ["loggingEntry.trace[]"]?: SimplePrinter<Exclude<KnownStatsLoggingEntry["trace"], undefined>[number], "logging"> } &
  820. * { loggingGroup?: SimplePrinter<KnownStatsLogging[], "logging"> } &
  821. * Printers<KnownStatsLogging & { name: string }, `loggingGroup`> &
  822. * Exclamation<KnownStatsLogging, "loggingGroup.separator", "loggingGroup">} LogEntryPrinters
  823. */
  824. /** @type {LogEntryPrinters} */
  825. const LOG_ENTRY_PRINTERS = {
  826. "loggingEntry(error).loggingEntry.message": (message, { red }) =>
  827. mapLines(message, (x) => `<e> ${red(x)}`),
  828. "loggingEntry(warn).loggingEntry.message": (message, { yellow }) =>
  829. mapLines(message, (x) => `<w> ${yellow(x)}`),
  830. "loggingEntry(info).loggingEntry.message": (message, { green }) =>
  831. mapLines(message, (x) => `<i> ${green(x)}`),
  832. "loggingEntry(log).loggingEntry.message": (message, { bold }) =>
  833. mapLines(message, (x) => ` ${bold(x)}`),
  834. "loggingEntry(debug).loggingEntry.message": (message) =>
  835. mapLines(message, (x) => ` ${x}`),
  836. "loggingEntry(trace).loggingEntry.message": (message) =>
  837. mapLines(message, (x) => ` ${x}`),
  838. "loggingEntry(status).loggingEntry.message": (message, { magenta }) =>
  839. mapLines(message, (x) => `<s> ${magenta(x)}`),
  840. "loggingEntry(profile).loggingEntry.message": (message, { magenta }) =>
  841. mapLines(message, (x) => `<p> ${magenta(x)}`),
  842. "loggingEntry(profileEnd).loggingEntry.message": (message, { magenta }) =>
  843. mapLines(message, (x) => `</p> ${magenta(x)}`),
  844. "loggingEntry(time).loggingEntry.message": (message, { magenta }) =>
  845. mapLines(message, (x) => `<t> ${magenta(x)}`),
  846. "loggingEntry(group).loggingEntry.message": (message, { cyan }) =>
  847. mapLines(message, (x) => `<-> ${cyan(x)}`),
  848. "loggingEntry(groupCollapsed).loggingEntry.message": (message, { cyan }) =>
  849. mapLines(message, (x) => `<+> ${cyan(x)}`),
  850. "loggingEntry(clear).loggingEntry": () => " -------",
  851. "loggingEntry(groupCollapsed).loggingEntry.children": () => "",
  852. "loggingEntry.trace[]": (trace) =>
  853. trace ? mapLines(trace, (x) => `| ${x}`) : undefined,
  854. loggingGroup: (loggingGroup) =>
  855. loggingGroup.entries.length === 0 ? "" : undefined,
  856. "loggingGroup.debug": (flag, { red }) => (flag ? red("DEBUG") : undefined),
  857. "loggingGroup.name": (name, { bold }) => bold(`LOG from ${name}`),
  858. "loggingGroup.separator!": () => "\n",
  859. "loggingGroup.filteredEntries": (filteredEntries) =>
  860. filteredEntries > 0 ? `+ ${filteredEntries} hidden lines` : undefined
  861. };
  862. /** @typedef {Printers<KnownStatsModuleTraceItem, "moduleTraceItem">} ModuleTraceItemPrinters */
  863. /** @type {ModuleTraceItemPrinters} */
  864. const MODULE_TRACE_ITEM_PRINTERS = {
  865. "moduleTraceItem.originName": (originName) => originName
  866. };
  867. /** @typedef {Printers<KnownStatsModuleTraceDependency, "moduleTraceDependency">} ModuleTraceDependencyPrinters */
  868. /** @type {ModuleTraceDependencyPrinters} */
  869. const MODULE_TRACE_DEPENDENCY_PRINTERS = {
  870. "moduleTraceDependency.loc": (loc) => loc
  871. };
  872. /**
  873. * @type {Record<string, string | ((item: KnownStatsLoggingEntry) => string)>}
  874. */
  875. const ITEM_NAMES = {
  876. "compilation.assets[]": "asset",
  877. "compilation.modules[]": "module",
  878. "compilation.chunks[]": "chunk",
  879. "compilation.entrypoints[]": "chunkGroup",
  880. "compilation.namedChunkGroups[]": "chunkGroup",
  881. "compilation.errors[]": "error",
  882. "compilation.warnings[]": "error",
  883. "compilation.hints[]": "error",
  884. "compilation.logging[]": "loggingGroup",
  885. "compilation.children[]": "compilation",
  886. "asset.related[]": "asset",
  887. "asset.children[]": "asset",
  888. "asset.chunks[]": "assetChunk",
  889. "asset.auxiliaryChunks[]": "assetChunk",
  890. "asset.chunkNames[]": "assetChunkName",
  891. "asset.chunkIdHints[]": "assetChunkIdHint",
  892. "asset.auxiliaryChunkNames[]": "assetChunkName",
  893. "asset.auxiliaryChunkIdHints[]": "assetChunkIdHint",
  894. "chunkGroup.assets[]": "chunkGroupAsset",
  895. "chunkGroup.auxiliaryAssets[]": "chunkGroupAsset",
  896. "chunkGroupChild.assets[]": "chunkGroupAsset",
  897. "chunkGroupChild.auxiliaryAssets[]": "chunkGroupAsset",
  898. "chunkGroup.children[]": "chunkGroupChildGroup",
  899. "chunkGroupChildGroup.children[]": "chunkGroupChild",
  900. "module.modules[]": "module",
  901. "module.children[]": "module",
  902. "module.reasons[]": "moduleReason",
  903. "moduleReason.children[]": "moduleReason",
  904. "module.issuerPath[]": "moduleIssuer",
  905. "chunk.origins[]": "chunkOrigin",
  906. "chunk.modules[]": "module",
  907. "loggingGroup.entries[]": (logEntry) =>
  908. `loggingEntry(${logEntry.type}).loggingEntry`,
  909. "loggingEntry.children[]": (logEntry) =>
  910. `loggingEntry(${logEntry.type}).loggingEntry`,
  911. "error.moduleTrace[]": "moduleTraceItem",
  912. "error.errors[]": "error",
  913. "moduleTraceItem.dependencies[]": "moduleTraceDependency"
  914. };
  915. const ERROR_PREFERRED_ORDER = [
  916. "compilerPath",
  917. "chunkId",
  918. "chunkEntry",
  919. "chunkInitial",
  920. "file",
  921. "separator!",
  922. "moduleName",
  923. "loc",
  924. "separator!",
  925. "message",
  926. "separator!",
  927. "details",
  928. "separator!",
  929. "filteredDetails",
  930. "separator!",
  931. "stack",
  932. "separator!",
  933. "cause",
  934. "separator!",
  935. "missing",
  936. "separator!",
  937. "moduleTrace"
  938. ];
  939. /** @type {Record<string, string[]>} */
  940. const PREFERRED_ORDERS = {
  941. compilation: [
  942. "name",
  943. "hash",
  944. "version",
  945. "time",
  946. "builtAt",
  947. "env",
  948. "publicPath",
  949. "assets",
  950. "filteredAssets",
  951. "entrypoints",
  952. "namedChunkGroups",
  953. "chunks",
  954. "modules",
  955. "filteredModules",
  956. "children",
  957. "logging",
  958. "hints",
  959. "warnings",
  960. "warningsInChildren!",
  961. "filteredWarningDetailsCount",
  962. "errors",
  963. "errorsInChildren!",
  964. "filteredErrorDetailsCount",
  965. "summary!",
  966. "needAdditionalPass"
  967. ],
  968. asset: [
  969. "type",
  970. "name",
  971. "size",
  972. "chunks",
  973. "auxiliaryChunks",
  974. "emitted",
  975. "comparedForEmit",
  976. "cached",
  977. "info",
  978. "isOverSizeLimit",
  979. "chunkNames",
  980. "auxiliaryChunkNames",
  981. "chunkIdHints",
  982. "auxiliaryChunkIdHints",
  983. "related",
  984. "filteredRelated",
  985. "children",
  986. "filteredChildren"
  987. ],
  988. "asset.info": [
  989. "immutable",
  990. "sourceFilename",
  991. "javascriptModule",
  992. "development",
  993. "hotModuleReplacement"
  994. ],
  995. chunkGroup: [
  996. "kind!",
  997. "name",
  998. "isOverSizeLimit",
  999. "assetsSize",
  1000. "auxiliaryAssetsSize",
  1001. "is!",
  1002. "assets",
  1003. "filteredAssets",
  1004. "auxiliaryAssets",
  1005. "filteredAuxiliaryAssets",
  1006. "separator!",
  1007. "children"
  1008. ],
  1009. chunkGroupAsset: ["name", "size"],
  1010. chunkGroupChildGroup: ["type", "children"],
  1011. chunkGroupChild: ["assets", "chunks", "name"],
  1012. module: [
  1013. "type",
  1014. "name",
  1015. "identifier",
  1016. "id",
  1017. "layer",
  1018. "sizes",
  1019. "chunks",
  1020. "depth",
  1021. "cacheable",
  1022. "orphan",
  1023. "runtime",
  1024. "optional",
  1025. "dependent",
  1026. "built",
  1027. "codeGenerated",
  1028. "cached",
  1029. "assets",
  1030. "failed",
  1031. "warnings",
  1032. "errors",
  1033. "children",
  1034. "filteredChildren",
  1035. "providedExports",
  1036. "usedExports",
  1037. "optimizationBailout",
  1038. "reasons",
  1039. "filteredReasons",
  1040. "issuerPath",
  1041. "profile",
  1042. "modules",
  1043. "filteredModules"
  1044. ],
  1045. moduleReason: [
  1046. "active",
  1047. "type",
  1048. "userRequest",
  1049. "moduleId",
  1050. "module",
  1051. "resolvedModule",
  1052. "loc",
  1053. "explanation",
  1054. "children",
  1055. "filteredChildren"
  1056. ],
  1057. "module.profile": [
  1058. "total",
  1059. "separator!",
  1060. "resolving",
  1061. "restoring",
  1062. "integration",
  1063. "building",
  1064. "storing",
  1065. "additionalResolving",
  1066. "additionalIntegration"
  1067. ],
  1068. chunk: [
  1069. "id",
  1070. "runtime",
  1071. "files",
  1072. "names",
  1073. "idHints",
  1074. "sizes",
  1075. "parents",
  1076. "siblings",
  1077. "children",
  1078. "childrenByOrder",
  1079. "entry",
  1080. "initial",
  1081. "rendered",
  1082. "recorded",
  1083. "reason",
  1084. "separator!",
  1085. "origins",
  1086. "separator!",
  1087. "modules",
  1088. "separator!",
  1089. "filteredModules"
  1090. ],
  1091. chunkOrigin: ["request", "moduleId", "moduleName", "loc"],
  1092. error: ERROR_PREFERRED_ORDER,
  1093. warning: ERROR_PREFERRED_ORDER,
  1094. "chunk.childrenByOrder[]": ["type", "children"],
  1095. loggingGroup: [
  1096. "debug",
  1097. "name",
  1098. "separator!",
  1099. "entries",
  1100. "separator!",
  1101. "filteredEntries"
  1102. ],
  1103. loggingEntry: ["message", "trace", "children"]
  1104. };
  1105. /** @typedef {(items: string[]) => string | undefined} SimpleItemsJoiner */
  1106. /** @type {SimpleItemsJoiner} */
  1107. const itemsJoinOneLine = (items) => items.filter(Boolean).join(" ");
  1108. /** @type {SimpleItemsJoiner} */
  1109. const itemsJoinOneLineBrackets = (items) =>
  1110. items.length > 0 ? `(${items.filter(Boolean).join(" ")})` : undefined;
  1111. /** @type {SimpleItemsJoiner} */
  1112. const itemsJoinMoreSpacing = (items) => items.filter(Boolean).join("\n\n");
  1113. /** @type {SimpleItemsJoiner} */
  1114. const itemsJoinComma = (items) => items.filter(Boolean).join(", ");
  1115. /** @type {SimpleItemsJoiner} */
  1116. const itemsJoinCommaBrackets = (items) =>
  1117. items.length > 0 ? `(${items.filter(Boolean).join(", ")})` : undefined;
  1118. /** @type {(item: string) => SimpleItemsJoiner} */
  1119. const itemsJoinCommaBracketsWithName = (name) => (items) =>
  1120. items.length > 0
  1121. ? `(${name}: ${items.filter(Boolean).join(", ")})`
  1122. : undefined;
  1123. /** @type {Record<string, SimpleItemsJoiner>} */
  1124. const SIMPLE_ITEMS_JOINER = {
  1125. "chunk.parents": itemsJoinOneLine,
  1126. "chunk.siblings": itemsJoinOneLine,
  1127. "chunk.children": itemsJoinOneLine,
  1128. "chunk.names": itemsJoinCommaBrackets,
  1129. "chunk.idHints": itemsJoinCommaBracketsWithName("id hint"),
  1130. "chunk.runtime": itemsJoinCommaBracketsWithName("runtime"),
  1131. "chunk.files": itemsJoinComma,
  1132. "chunk.childrenByOrder": itemsJoinOneLine,
  1133. "chunk.childrenByOrder[].children": itemsJoinOneLine,
  1134. "chunkGroup.assets": itemsJoinOneLine,
  1135. "chunkGroup.auxiliaryAssets": itemsJoinOneLineBrackets,
  1136. "chunkGroupChildGroup.children": itemsJoinComma,
  1137. "chunkGroupChild.assets": itemsJoinOneLine,
  1138. "chunkGroupChild.auxiliaryAssets": itemsJoinOneLineBrackets,
  1139. "asset.chunks": itemsJoinComma,
  1140. "asset.auxiliaryChunks": itemsJoinCommaBrackets,
  1141. "asset.chunkNames": itemsJoinCommaBracketsWithName("name"),
  1142. "asset.auxiliaryChunkNames": itemsJoinCommaBracketsWithName("auxiliary name"),
  1143. "asset.chunkIdHints": itemsJoinCommaBracketsWithName("id hint"),
  1144. "asset.auxiliaryChunkIdHints":
  1145. itemsJoinCommaBracketsWithName("auxiliary id hint"),
  1146. "module.chunks": itemsJoinOneLine,
  1147. "module.issuerPath": (items) =>
  1148. items
  1149. .filter(Boolean)
  1150. .map((item) => `${item} ->`)
  1151. .join(" "),
  1152. "compilation.errors": itemsJoinMoreSpacing,
  1153. "compilation.warnings": itemsJoinMoreSpacing,
  1154. "compilation.hints": itemsJoinMoreSpacing,
  1155. "compilation.logging": itemsJoinMoreSpacing,
  1156. "compilation.children": (items) =>
  1157. indent(/** @type {string} */ (itemsJoinMoreSpacing(items)), " "),
  1158. "moduleTraceItem.dependencies": itemsJoinOneLine,
  1159. "loggingEntry.children": (items) =>
  1160. indent(items.filter(Boolean).join("\n"), " ", false)
  1161. };
  1162. /**
  1163. * Returns result.
  1164. * @param {Item[]} items items
  1165. * @returns {string} result
  1166. */
  1167. const joinOneLine = (items) =>
  1168. items
  1169. .map((item) => item.content)
  1170. .filter(Boolean)
  1171. .join(" ");
  1172. /**
  1173. * Returns result.
  1174. * @param {Item[]} items items
  1175. * @returns {string} result
  1176. */
  1177. const joinInBrackets = (items) => {
  1178. /** @type {string[]} */
  1179. const res = [];
  1180. let mode = 0;
  1181. for (const item of items) {
  1182. if (item.element === "separator!") {
  1183. switch (mode) {
  1184. case 0:
  1185. case 1:
  1186. mode += 2;
  1187. break;
  1188. case 4:
  1189. res.push(")");
  1190. mode = 3;
  1191. break;
  1192. }
  1193. }
  1194. if (!item.content) continue;
  1195. switch (mode) {
  1196. case 0:
  1197. mode = 1;
  1198. break;
  1199. case 1:
  1200. res.push(" ");
  1201. break;
  1202. case 2:
  1203. res.push("(");
  1204. mode = 4;
  1205. break;
  1206. case 3:
  1207. res.push(" (");
  1208. mode = 4;
  1209. break;
  1210. case 4:
  1211. res.push(", ");
  1212. break;
  1213. }
  1214. res.push(item.content);
  1215. }
  1216. if (mode === 4) res.push(")");
  1217. return res.join("");
  1218. };
  1219. /**
  1220. * Returns result.
  1221. * @param {string} str a string
  1222. * @param {string} prefix prefix
  1223. * @param {boolean=} noPrefixInFirstLine need prefix in the first line?
  1224. * @returns {string} result
  1225. */
  1226. const indent = (str, prefix, noPrefixInFirstLine) => {
  1227. const rem = str.replace(/\n([^\n])/g, `\n${prefix}$1`);
  1228. if (noPrefixInFirstLine) return rem;
  1229. const ind = str[0] === "\n" ? "" : prefix;
  1230. return ind + rem;
  1231. };
  1232. /**
  1233. * Join explicit new line.
  1234. * @param {(false | Item)[]} items items
  1235. * @param {string} indenter indenter
  1236. * @returns {string} result
  1237. */
  1238. const joinExplicitNewLine = (items, indenter) => {
  1239. let firstInLine = true;
  1240. let first = true;
  1241. return items
  1242. .map((item) => {
  1243. if (!item || !item.content) return;
  1244. let content = indent(item.content, first ? "" : indenter, !firstInLine);
  1245. if (firstInLine) {
  1246. content = content.replace(/^\n+/, "");
  1247. }
  1248. if (!content) return;
  1249. first = false;
  1250. const noJoiner = firstInLine || content.startsWith("\n");
  1251. firstInLine = content.endsWith("\n");
  1252. return noJoiner ? content : ` ${content}`;
  1253. })
  1254. .filter(Boolean)
  1255. .join("")
  1256. .trim();
  1257. };
  1258. /**
  1259. * Returns joiner.
  1260. * @param {boolean} error is an error
  1261. * @returns {SimpleElementJoiner} joiner
  1262. */
  1263. const joinError =
  1264. (error) =>
  1265. /**
  1266. * Handles the callback logic for this hook.
  1267. * @param {Item[]} items items
  1268. * @param {StatsPrinterContextWithExtra} ctx context
  1269. * @returns {string} result
  1270. */
  1271. (items, { red, yellow }) =>
  1272. `${error ? red("ERROR") : yellow("WARNING")} in ${joinExplicitNewLine(
  1273. items,
  1274. ""
  1275. )}`;
  1276. /** @typedef {{ element: string, content: string | undefined }} Item */
  1277. /** @typedef {(items: Item[], context: StatsPrinterContextWithExtra & Required<KnownStatsPrinterContext>) => string} SimpleElementJoiner */
  1278. /** @type {Record<string, SimpleElementJoiner>} */
  1279. const SIMPLE_ELEMENT_JOINERS = {
  1280. compilation: (items) => {
  1281. /** @type {string[]} */
  1282. const result = [];
  1283. let lastNeedMore = false;
  1284. for (const item of items) {
  1285. if (!item.content) continue;
  1286. const needMoreSpace =
  1287. item.element === "warnings" ||
  1288. item.element === "filteredWarningDetailsCount" ||
  1289. item.element === "errors" ||
  1290. item.element === "filteredErrorDetailsCount" ||
  1291. item.element === "logging";
  1292. if (result.length !== 0) {
  1293. result.push(needMoreSpace || lastNeedMore ? "\n\n" : "\n");
  1294. }
  1295. result.push(item.content);
  1296. lastNeedMore = needMoreSpace;
  1297. }
  1298. if (lastNeedMore) result.push("\n");
  1299. return result.join("");
  1300. },
  1301. asset: (items) =>
  1302. joinExplicitNewLine(
  1303. items.map((item) => {
  1304. if (
  1305. (item.element === "related" || item.element === "children") &&
  1306. item.content
  1307. ) {
  1308. return {
  1309. ...item,
  1310. content: `\n${item.content}\n`
  1311. };
  1312. }
  1313. return item;
  1314. }),
  1315. " "
  1316. ),
  1317. "asset.info": joinOneLine,
  1318. module: (items, { module }) => {
  1319. let hasName = false;
  1320. return joinExplicitNewLine(
  1321. items.map((item) => {
  1322. switch (item.element) {
  1323. case "id":
  1324. if (module.id === module.name) {
  1325. if (hasName) return false;
  1326. if (item.content) hasName = true;
  1327. }
  1328. break;
  1329. case "name":
  1330. if (hasName) return false;
  1331. if (item.content) hasName = true;
  1332. break;
  1333. case "providedExports":
  1334. case "usedExports":
  1335. case "optimizationBailout":
  1336. case "reasons":
  1337. case "issuerPath":
  1338. case "profile":
  1339. case "children":
  1340. case "modules":
  1341. if (item.content) {
  1342. return {
  1343. ...item,
  1344. content: `\n${item.content}\n`
  1345. };
  1346. }
  1347. break;
  1348. }
  1349. return item;
  1350. }),
  1351. " "
  1352. );
  1353. },
  1354. chunk: (items) => {
  1355. let hasEntry = false;
  1356. return `chunk ${joinExplicitNewLine(
  1357. items.filter((item) => {
  1358. switch (item.element) {
  1359. case "entry":
  1360. if (item.content) hasEntry = true;
  1361. break;
  1362. case "initial":
  1363. if (hasEntry) return false;
  1364. break;
  1365. }
  1366. return true;
  1367. }),
  1368. " "
  1369. )}`;
  1370. },
  1371. "chunk.childrenByOrder[]": (items) => `(${joinOneLine(items)})`,
  1372. chunkGroup: (items) => joinExplicitNewLine(items, " "),
  1373. chunkGroupAsset: joinOneLine,
  1374. chunkGroupChildGroup: joinOneLine,
  1375. chunkGroupChild: joinOneLine,
  1376. moduleReason: (items, { moduleReason }) => {
  1377. let hasName = false;
  1378. return joinExplicitNewLine(
  1379. items.map((item) => {
  1380. switch (item.element) {
  1381. case "moduleId":
  1382. if (moduleReason.moduleId === moduleReason.module && item.content) {
  1383. hasName = true;
  1384. }
  1385. break;
  1386. case "module":
  1387. if (hasName) return false;
  1388. break;
  1389. case "resolvedModule":
  1390. if (moduleReason.module === moduleReason.resolvedModule) {
  1391. return false;
  1392. }
  1393. break;
  1394. case "children":
  1395. if (item.content) {
  1396. return {
  1397. ...item,
  1398. content: `\n${item.content}\n`
  1399. };
  1400. }
  1401. break;
  1402. }
  1403. return item;
  1404. }),
  1405. " "
  1406. );
  1407. },
  1408. "module.profile": joinInBrackets,
  1409. moduleIssuer: joinOneLine,
  1410. chunkOrigin: (items) => `> ${joinOneLine(items)}`,
  1411. "errors[].error": joinError(true),
  1412. "warnings[].error": joinError(false),
  1413. "hints[].error": (items, { cyan }) =>
  1414. `${cyan("HINT")} in ${joinExplicitNewLine(items, "")}`,
  1415. error: (items) => joinExplicitNewLine(items, ""),
  1416. "error.errors[].error": (items) =>
  1417. indent(`[errors]: ${joinExplicitNewLine(items, "")}`, " "),
  1418. loggingGroup: (items) => joinExplicitNewLine(items, "").trimEnd(),
  1419. moduleTraceItem: (items) => ` @ ${joinOneLine(items)}`,
  1420. moduleTraceDependency: joinOneLine
  1421. };
  1422. /** @type {Record<keyof KnownStatsPrinterColorFunctions, string>} */
  1423. const AVAILABLE_COLORS = {
  1424. bold: "\u001B[1m",
  1425. yellow: "\u001B[1m\u001B[33m",
  1426. red: "\u001B[1m\u001B[31m",
  1427. green: "\u001B[1m\u001B[32m",
  1428. cyan: "\u001B[1m\u001B[36m",
  1429. magenta: "\u001B[1m\u001B[35m"
  1430. };
  1431. /**
  1432. * Defines the tail type used by this module.
  1433. * @template T
  1434. * @typedef {T extends [infer Head, ...infer Tail] ? Tail : undefined} Tail
  1435. */
  1436. /**
  1437. * Defines the tail parameters type used by this module.
  1438. * @template {(...args: EXPECTED_ANY[]) => EXPECTED_ANY} T
  1439. * @typedef {T extends (firstArg: EXPECTED_ANY, ...rest: infer R) => EXPECTED_ANY ? R : never} TailParameters
  1440. */
  1441. /** @typedef {{ [Key in keyof KnownStatsPrinterFormatters]: (value: Parameters<NonNullable<KnownStatsPrinterFormatters[Key]>>[0], options: Required<KnownStatsPrinterColorFunctions> & StatsPrinterContextWithExtra, ...args: TailParameters<NonNullable<KnownStatsPrinterFormatters[Key]>>) => string }} AvailableFormats */
  1442. /** @type {AvailableFormats} */
  1443. const AVAILABLE_FORMATS = {
  1444. formatChunkId: (id, { yellow }, direction) => {
  1445. switch (direction) {
  1446. case "parent":
  1447. return `<{${yellow(id)}}>`;
  1448. case "sibling":
  1449. return `={${yellow(id)}}=`;
  1450. case "child":
  1451. return `>{${yellow(id)}}<`;
  1452. default:
  1453. return `{${yellow(id)}}`;
  1454. }
  1455. },
  1456. formatModuleId: (id) => `[${id}]`,
  1457. formatFilename: (filename, { green, yellow }, oversize) =>
  1458. (oversize ? yellow : green)(filename),
  1459. formatFlag: (flag) => `[${flag}]`,
  1460. formatLayer: (layer) => `(in ${layer})`,
  1461. formatSize: require("../util/formatSize"),
  1462. formatDateTime: (dateTime, { bold }) => {
  1463. const d = new Date(dateTime);
  1464. const x = twoDigit;
  1465. const date = `${d.getFullYear()}-${x(d.getMonth() + 1)}-${x(d.getDate())}`;
  1466. const time = `${x(d.getHours())}:${x(d.getMinutes())}:${x(d.getSeconds())}`;
  1467. return `${date} ${bold(time)}`;
  1468. },
  1469. formatTime: (
  1470. time,
  1471. { timeReference, bold, green, yellow, red },
  1472. boldQuantity
  1473. ) => {
  1474. const unit = " ms";
  1475. if (timeReference && time !== timeReference) {
  1476. const times = [
  1477. timeReference / 2,
  1478. timeReference / 4,
  1479. timeReference / 8,
  1480. timeReference / 16
  1481. ];
  1482. if (time < times[3]) return `${time}${unit}`;
  1483. else if (time < times[2]) return bold(`${time}${unit}`);
  1484. else if (time < times[1]) return green(`${time}${unit}`);
  1485. else if (time < times[0]) return yellow(`${time}${unit}`);
  1486. return red(`${time}${unit}`);
  1487. }
  1488. return `${boldQuantity ? bold(time) : time}${unit}`;
  1489. },
  1490. formatError: (message, { green, yellow, red }) => {
  1491. if (message.includes("\u001B[")) return message;
  1492. const highlights = [
  1493. { regExp: /(Did you mean .+)/g, format: green },
  1494. {
  1495. regExp: /(Set 'mode' option to 'development' or 'production')/g,
  1496. format: green
  1497. },
  1498. { regExp: /(\(module has no exports\))/g, format: red },
  1499. { regExp: /\(possible exports: (.+)\)/g, format: green },
  1500. { regExp: /(?:^|\n)(.* doesn't exist)/g, format: red },
  1501. { regExp: /('\w+' option has not been set)/g, format: red },
  1502. {
  1503. regExp: /(Emitted value instead of an instance of Error)/g,
  1504. format: yellow
  1505. },
  1506. { regExp: /(Used? .+ instead)/gi, format: yellow },
  1507. { regExp: /\b(deprecated|must|required)\b/g, format: yellow },
  1508. {
  1509. regExp: /\b(BREAKING CHANGE)\b/gi,
  1510. format: red
  1511. },
  1512. {
  1513. regExp:
  1514. /\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,
  1515. format: red
  1516. }
  1517. ];
  1518. for (const { regExp, format } of highlights) {
  1519. message = message.replace(
  1520. regExp,
  1521. /**
  1522. * Handles the format callback for this hook.
  1523. * @param {string} match match
  1524. * @param {string} content content
  1525. * @returns {string} result
  1526. */
  1527. (match, content) => match.replace(content, format(content))
  1528. );
  1529. }
  1530. return message;
  1531. }
  1532. };
  1533. /** @typedef {(result: string) => string} ResultModifierFn */
  1534. /** @type {Record<string, ResultModifierFn>} */
  1535. const RESULT_MODIFIER = {
  1536. "module.modules": (result) => indent(result, "| ")
  1537. };
  1538. /**
  1539. * Creates an order from the provided array.
  1540. * @param {string[]} array array
  1541. * @param {string[]} preferredOrder preferred order
  1542. * @returns {string[]} result
  1543. */
  1544. const createOrder = (array, preferredOrder) => {
  1545. const originalArray = [...array];
  1546. /** @type {Set<string>} */
  1547. const set = new Set(array);
  1548. /** @type {Set<string>} */
  1549. const usedSet = new Set();
  1550. array.length = 0;
  1551. for (const element of preferredOrder) {
  1552. if (element.endsWith("!") || set.has(element)) {
  1553. array.push(element);
  1554. usedSet.add(element);
  1555. }
  1556. }
  1557. for (const element of originalArray) {
  1558. if (!usedSet.has(element)) {
  1559. array.push(element);
  1560. }
  1561. }
  1562. return array;
  1563. };
  1564. const PLUGIN_NAME = "DefaultStatsPrinterPlugin";
  1565. class DefaultStatsPrinterPlugin {
  1566. /**
  1567. * Applies the plugin by registering its hooks on the compiler.
  1568. * @param {Compiler} compiler the compiler instance
  1569. * @returns {void}
  1570. */
  1571. apply(compiler) {
  1572. compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
  1573. compilation.hooks.statsPrinter.tap(PLUGIN_NAME, (stats, options) => {
  1574. // Put colors into context
  1575. stats.hooks.print
  1576. .for("compilation")
  1577. .tap(PLUGIN_NAME, (compilation, context) => {
  1578. for (const color of Object.keys(AVAILABLE_COLORS)) {
  1579. const name =
  1580. /** @type {keyof KnownStatsPrinterColorFunctions} */
  1581. (color);
  1582. /** @type {string | undefined} */
  1583. let start;
  1584. if (options.colors) {
  1585. if (
  1586. typeof options.colors === "object" &&
  1587. typeof options.colors[name] === "string"
  1588. ) {
  1589. start = options.colors[name];
  1590. } else {
  1591. start = AVAILABLE_COLORS[name];
  1592. }
  1593. }
  1594. if (start) {
  1595. /** @type {ColorFunction} */
  1596. context[color] = (str) =>
  1597. `${start}${
  1598. typeof str === "string"
  1599. ? str.replace(
  1600. // eslint-disable-next-line no-control-regex
  1601. /((\u001B\[39m|\u001B\[22m|\u001B\[0m)+)/g,
  1602. `$1${start}`
  1603. )
  1604. : str
  1605. }\u001B[39m\u001B[22m`;
  1606. } else {
  1607. /**
  1608. * Returns str string.
  1609. * @param {string} str string
  1610. * @returns {string} str string
  1611. */
  1612. context[color] = (str) => str;
  1613. }
  1614. }
  1615. for (const format of /** @type {(keyof KnownStatsPrinterFormatters)[]} */ (
  1616. Object.keys(AVAILABLE_FORMATS)
  1617. )) {
  1618. context[format] =
  1619. /** @type {(content: Parameters<NonNullable<KnownStatsPrinterFormatters[keyof KnownStatsPrinterFormatters]>>[0], ...args: Tail<Parameters<NonNullable<KnownStatsPrinterFormatters[keyof KnownStatsPrinterFormatters]>>>) => string} */
  1620. (content, ...args) =>
  1621. /** @type {EXPECTED_ANY} */
  1622. (AVAILABLE_FORMATS[format])(
  1623. content,
  1624. /** @type {StatsPrinterContext & Required<KnownStatsPrinterColorFunctions>} */
  1625. (context),
  1626. ...args
  1627. );
  1628. }
  1629. context.timeReference = compilation.time;
  1630. });
  1631. // Every `*_SIMPLE_PRINTERS` map registers identically: tap
  1632. // `print.for(key)` with a formatter called as `(obj, ctx, stats)`.
  1633. // One loop over all maps replaces a dozen near-identical blocks.
  1634. for (const simplePrinters of /** @type {Record<string, EXPECTED_ANY>[]} */ ([
  1635. COMPILATION_SIMPLE_PRINTERS,
  1636. ASSET_SIMPLE_PRINTERS,
  1637. MODULE_SIMPLE_PRINTERS,
  1638. MODULE_ISSUER_PRINTERS,
  1639. MODULE_REASON_PRINTERS,
  1640. MODULE_PROFILE_PRINTERS,
  1641. CHUNK_GROUP_PRINTERS,
  1642. CHUNK_PRINTERS,
  1643. ERROR_PRINTERS,
  1644. LOG_ENTRY_PRINTERS,
  1645. MODULE_TRACE_DEPENDENCY_PRINTERS,
  1646. MODULE_TRACE_ITEM_PRINTERS
  1647. ])) {
  1648. for (const key of Object.keys(simplePrinters)) {
  1649. stats.hooks.print
  1650. .for(key)
  1651. .tap(PLUGIN_NAME, (obj, ctx) =>
  1652. simplePrinters[key](obj, ctx, stats)
  1653. );
  1654. }
  1655. }
  1656. for (const key of Object.keys(PREFERRED_ORDERS)) {
  1657. const preferredOrder = PREFERRED_ORDERS[key];
  1658. stats.hooks.sortElements
  1659. .for(key)
  1660. .tap(PLUGIN_NAME, (elements, _context) => {
  1661. createOrder(elements, preferredOrder);
  1662. });
  1663. }
  1664. for (const key of Object.keys(ITEM_NAMES)) {
  1665. const itemName = ITEM_NAMES[key];
  1666. stats.hooks.getItemName
  1667. .for(key)
  1668. .tap(
  1669. PLUGIN_NAME,
  1670. typeof itemName === "string" ? () => itemName : itemName
  1671. );
  1672. }
  1673. for (const key of Object.keys(SIMPLE_ITEMS_JOINER)) {
  1674. const joiner = SIMPLE_ITEMS_JOINER[key];
  1675. stats.hooks.printItems.for(key).tap(PLUGIN_NAME, joiner);
  1676. }
  1677. for (const key of Object.keys(SIMPLE_ELEMENT_JOINERS)) {
  1678. const joiner =
  1679. /** @type {(items: Item[], context: StatsPrinterContext) => string} */
  1680. (SIMPLE_ELEMENT_JOINERS[key]);
  1681. stats.hooks.printElements.for(key).tap(PLUGIN_NAME, joiner);
  1682. }
  1683. for (const key of Object.keys(RESULT_MODIFIER)) {
  1684. const modifier = RESULT_MODIFIER[key];
  1685. stats.hooks.result.for(key).tap(PLUGIN_NAME, modifier);
  1686. }
  1687. });
  1688. });
  1689. }
  1690. }
  1691. module.exports = DefaultStatsPrinterPlugin;