DefaultStatsPrinterPlugin.js 53 KB

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