DefaultStatsPrinterPlugin.js 52 KB

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