DefaultStatsPrinterPlugin.js 41 KB

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