diff --git a/packages/rspack/src/compilation.ts b/packages/rspack/src/compilation.ts index 7c0031aa94f..e1cc508ca50 100644 --- a/packages/rspack/src/compilation.ts +++ b/packages/rspack/src/compilation.ts @@ -37,6 +37,7 @@ import { LogType, Logger } from "./logging/Logger"; import { NormalModule } from "./normalModule"; import { NormalModuleFactory } from "./normalModuleFactory"; import { Stats, normalizeStatsPreset } from "./stats"; +import StatsPrinter from "./StatsPrinter"; import { concatErrorMsgAndStack, isJsStatsError, toJsAssetInfo } from "./util"; import { createRawFromSource, createSourceFromRaw } from "./util/createSource"; import { @@ -92,6 +93,7 @@ export class Compilation { succeedModule: tapable.SyncHook<[JsModule], undefined>; stillValidModule: tapable.SyncHook<[JsModule], undefined>; buildModule: tapable.SyncHook<[NormalizedJsModule]>; + statsPrinter: tapable.SyncHook<[StatsPrinter, StatsValue], undefined>; }; options: RspackOptionsNormalized; outputOptions: OutputNormalized; @@ -106,6 +108,7 @@ export class Compilation { normalModuleFactory?: NormalModuleFactory; children: Compilation[] = []; contextModuleFactory?: ContextModuleFactory; + statsPrinter?: StatsPrinter; constructor(compiler: Compiler, inner: JsCompilation) { this.name = undefined; @@ -128,7 +131,8 @@ export class Compilation { processWarnings: new tapable.SyncWaterfallHook(["warnings"]), succeedModule: new tapable.SyncHook(["module"]), stillValidModule: new tapable.SyncHook(["module"]), - buildModule: new tapable.SyncHook(["module"]) + buildModule: new tapable.SyncHook(["module"]), + statsPrinter: new tapable.SyncHook(["statsPrinter", "options"]) }; this.compiler = compiler; this.resolverFactory = compiler.resolverFactory; @@ -279,6 +283,12 @@ export class Compilation { return options; } + createStatsPrinter(options: StatsValue) { + const statsPrinter = new StatsPrinter(); + this.hooks.statsPrinter.call(statsPrinter, options); + return statsPrinter; + } + /** * Update an existing asset. Trying to update an asset that doesn't exist will throw an error. * diff --git a/packages/rspack/src/compiler.ts b/packages/rspack/src/compiler.ts index 857447aaea3..13c460b2a0d 100644 --- a/packages/rspack/src/compiler.ts +++ b/packages/rspack/src/compiler.ts @@ -13,6 +13,7 @@ import path from "path"; import * as tapable from "tapable"; import { Callback, SyncBailHook, SyncHook } from "tapable"; import type { WatchOptions } from "watchpack"; +import { ModuleFilenameHelpers } from "webpack"; import { EntryNormalized, OutputNormalized, @@ -191,7 +192,8 @@ class Compiler { // get LazySet() { // return require("./util/LazySet"); // } - } + }, + ModuleFilenameHelpers }; this.root = this; this.ruleSet = new RuleSetCompiler(); diff --git a/packages/rspack/src/lib/DefaultStatsPrinterPlugin.js b/packages/rspack/src/lib/DefaultStatsPrinterPlugin.js new file mode 100644 index 00000000000..9a67a7dbbe4 --- /dev/null +++ b/packages/rspack/src/lib/DefaultStatsPrinterPlugin.js @@ -0,0 +1,1462 @@ +/* + MIT License http://www.opensource.org/licenses/mit-license.php + Author Tobias Koppers @sokra +*/ + +"use strict"; + +const SizeFormatHelpers = require("./SizeFormatHelpers"); + +/** @typedef {import("./Compiler")} Compiler */ +/** @typedef {import("../StatsPrinter")} StatsPrinter */ +/** @typedef {import("../StatsPrinter").StatsPrinterContext} StatsPrinterContext */ + +const DATA_URI_CONTENT_LENGTH = 16; + +const plural = (n, singular, plural) => (n === 1 ? singular : plural); + +/** + * @param {Record} sizes sizes by source type + * @param {Object} options options + * @param {(number) => string=} options.formatSize size formatter + * @returns {string} text + */ +const printSizes = (sizes, { formatSize = n => `${n}` }) => { + const keys = Object.keys(sizes); + if (keys.length > 1) { + return keys.map(key => `${formatSize(sizes[key])} (${key})`).join(" "); + } else if (keys.length === 1) { + return formatSize(sizes[keys[0]]); + } +}; + +const getResourceName = resource => { + const dataUrl = /^data:[^,]+,/.exec(resource); + if (!dataUrl) return resource; + + const len = dataUrl[0].length + DATA_URI_CONTENT_LENGTH; + if (resource.length < len) return resource; + return `${resource.slice( + 0, + Math.min(resource.length - /* '..'.length */ 2, len) + )}..`; +}; + +const getModuleName = name => { + const [, prefix, resource] = /^(.*!)?([^!]*)$/.exec(name); + return [prefix, getResourceName(resource)]; +}; + +const mapLines = (str, fn) => str.split("\n").map(fn).join("\n"); + +/** + * @param {number} n a number + * @returns {string} number as two digit string, leading 0 + */ +const twoDigit = n => (n >= 10 ? `${n}` : `0${n}`); + +const isValidId = id => { + return typeof id === "number" || id; +}; + +const moreCount = (list, count) => { + return list && list.length > 0 ? `+ ${count}` : `${count}`; +}; + +/** @type {Record string | void>} */ +const SIMPLE_PRINTERS = { + // "compilation.summary!": ( + // _, + // { + // type, + // bold, + // green, + // red, + // yellow, + // formatDateTime, + // formatTime, + // compilation: { + // name, + // hash, + // version, + // time, + // builtAt, + // errorsCount, + // warningsCount + // } + // } + // ) => { + // const root = type === "compilation.summary!"; + // const warningsMessage = + // warningsCount > 0 + // ? yellow( + // `${warningsCount} ${plural(warningsCount, "warning", "warnings")}` + // ) + // : ""; + // const errorsMessage = + // errorsCount > 0 + // ? red(`${errorsCount} ${plural(errorsCount, "error", "errors")}`) + // : ""; + // const timeMessage = root && time ? ` in ${formatTime(time)}` : ""; + // const hashMessage = hash ? ` (${hash})` : ""; + // const builtAtMessage = + // root && builtAt ? `${formatDateTime(builtAt)}: ` : ""; + // const versionMessage = root && version ? `webpack ${version}` : ""; + // const nameMessage = + // root && name + // ? bold(name) + // : name + // ? `Child ${bold(name)}` + // : root + // ? "" + // : "Child"; + // const subjectMessage = + // nameMessage && versionMessage + // ? `${nameMessage} (${versionMessage})` + // : versionMessage || nameMessage || "webpack"; + // let statusMessage; + // if (errorsMessage && warningsMessage) { + // statusMessage = `compiled with ${errorsMessage} and ${warningsMessage}`; + // } else if (errorsMessage) { + // statusMessage = `compiled with ${errorsMessage}`; + // } else if (warningsMessage) { + // statusMessage = `compiled with ${warningsMessage}`; + // } else if (errorsCount === 0 && warningsCount === 0) { + // statusMessage = `compiled ${green("successfully")}`; + // } else { + // statusMessage = `compiled`; + // } + // if ( + // builtAtMessage || + // versionMessage || + // errorsMessage || + // warningsMessage || + // (errorsCount === 0 && warningsCount === 0) || + // timeMessage || + // hashMessage + // ) + // return `${builtAtMessage}${subjectMessage} ${statusMessage}${timeMessage}${hashMessage}`; + // }, + // "compilation.filteredWarningDetailsCount": count => + // count + // ? `${count} ${plural( + // count, + // "warning has", + // "warnings have" + // )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.` + // : undefined, + // "compilation.filteredErrorDetailsCount": (count, { yellow }) => + // count + // ? yellow( + // `${count} ${plural( + // count, + // "error has", + // "errors have" + // )} detailed information that is not shown.\nUse 'stats.errorDetails: true' resp. '--stats-error-details' to show it.` + // ) + // : undefined, + "compilation.hash": (hash, { bold }) => + hash ? `Hash: ${bold(hash)}` : undefined, + "compilation.version": (version, { bold }) => + version ? `Version: ${bold(version)}` : undefined, + "compilation.time": (time, { bold }) => + time ? `Time: ${bold(time)}ms` : undefined, + "compilation.builtAt": (builtAt, { bold, formatDateTime }) => + builtAt ? `Built at: ${bold(formatDateTime(builtAt))}` : undefined, + "compilation.env": (env, { bold }) => + env + ? `Environment (--env): ${bold(JSON.stringify(env, null, 2))}` + : undefined, + "compilation.publicPath": (publicPath, { bold }) => + `PublicPath: ${bold(publicPath)}`, + "compilation.entrypoints": (entrypoints, context, printer) => + Array.isArray(entrypoints) + ? undefined + : printer.print(context.type, Object.values(entrypoints), { + ...context, + chunkGroupKind: "Entrypoint" + }), + "compilation.namedChunkGroups": (namedChunkGroups, context, printer) => { + if (!Array.isArray(namedChunkGroups)) { + const { + compilation: { entrypoints } + } = context; + let chunkGroups = Object.values(namedChunkGroups); + if (entrypoints) { + chunkGroups = chunkGroups.filter( + group => + !Object.prototype.hasOwnProperty.call(entrypoints, group.name) + ); + } + return printer.print(context.type, chunkGroups, { + ...context, + chunkGroupKind: "Chunk Group" + }); + } + }, + // "compilation.assetsByChunkName": () => "", + + // "compilation.filteredModules": ( + // filteredModules, + // { compilation: { modules } } + // ) => + // filteredModules > 0 + // ? `${moreCount(modules, filteredModules)} ${plural( + // filteredModules, + // "module", + // "modules" + // )}` + // : undefined, + "compilation.filteredAssets": (filteredAssets, { compilation: { assets } }) => + filteredAssets > 0 + ? ` ${moreCount(assets, filteredAssets)}${ + filteredAssets.length > 0 ? " hidden" : "" + }${filteredAssets === 1 ? " asset" : " assets"}` + : undefined, + // "compilation.logging": (logging, context, printer) => + // Array.isArray(logging) + // ? undefined + // : printer.print( + // context.type, + // Object.entries(logging).map(([name, value]) => ({ ...value, name })), + // context + // ), + "compilation.warningsInChildren!": (_, { yellow, compilation }) => { + if ( + !compilation.children && + compilation.warningsCount > 0 && + compilation.warnings + ) { + const childWarnings = + compilation.warningsCount - compilation.warnings.length; + if (childWarnings > 0) { + return yellow( + `${childWarnings} ${plural( + childWarnings, + "WARNING", + "WARNINGS" + )} in child compilations${ + compilation.children + ? "" + : " (Use 'stats.children: true' resp. '--stats-children' for more details)" + }` + ); + } + } + }, + "compilation.errorsInChildren!": (_, { red, compilation }) => { + if ( + !compilation.children && + compilation.errorsCount > 0 && + compilation.errors + ) { + const childErrors = compilation.errorsCount - compilation.errors.length; + if (childErrors > 0) { + return red( + `${childErrors} ${plural( + childErrors, + "ERROR", + "ERRORS" + )} in child compilations${ + compilation.children + ? "" + : " (Use 'stats.children: true' resp. '--stats-children' for more details)" + }` + ); + } + } + }, + + "compilation.assetsTh!": (_, { bold, compilation }) => { + if (compilation.assets && compilation.assets.length > 0) { + return `${bold("Asset")}|${bold("Size")}|${bold("Chunks")}|${bold( + "Chunk Names" + )}`; + } + }, + // "asset.type": type => type, + "asset.name": (name, { yellow, green, isOverSizeLimit, assetsTable }) => { + if (name.length > assetsTable[0].colSize) { + assetsTable[0].colSize = name.length; + } + return isOverSizeLimit ? yellow(name) : green(name); + }, + "asset.size": ( + size, + { yellow, formatSize, assetsTable, isOverSizeLimit } + ) => { + const text = formatSize(size); + if (text.length > assetsTable[1].colSize) { + assetsTable[1].colSize = text.length; + } + return isOverSizeLimit ? yellow(text) : text; + }, + "asset.emitted": ( + emitted, + { green, formatFlag, _index, assetsStatusLen } + ) => { + if (!emitted) { + assetsStatusLen[_index] = 0; + } else { + } + emitted ? green(formatFlag("emitted")) : undefined; + }, + // "asset.comparedForEmit": (comparedForEmit, { yellow, formatFlag }) => + // comparedForEmit ? yellow(formatFlag("compared for emit")) : undefined, + // "asset.cached": (cached, { green, formatFlag }) => + // cached ? green(formatFlag("cached")) : undefined, + "asset.isOverSizeLimit": ( + isOverSizeLimit, + { assetsTable, yellow, formatFlag } + ) => { + const text = formatFlag("big"); + if (text.length > assetsTable[4].colSize) { + assetsTable[4].colSize = text.length; + } + isOverSizeLimit ? yellow(formatFlag("big")) : undefined; + }, + "asset.info.immutable": (immutable, { green, formatFlag }) => + immutable && inSummary ? green(formatFlag("immutable")) : undefined, + // "asset.info.javascriptModule": (javascriptModule, { formatFlag }) => + // javascriptModule ? formatFlag("javascript module") : undefined, + // "asset.info.sourceFilename": (sourceFilename, { formatFlag }) => + // sourceFilename + // ? formatFlag( + // sourceFilename === true + // ? "from source file" + // : `from: ${sourceFilename}` + // ) + // : undefined, + "asset.info.development": (development, { green, formatFlag }) => + development && inSummary ? green(formatFlag("dev")) : undefined, + "asset.info.hotModuleReplacement": ( + hotModuleReplacement, + { green, formatFlag } + ) => (hotModuleReplacement ? green(formatFlag("hmr")) : undefined), + "asset.summary!": (_, context, printer) => { + const assetsTable = context.assetsTable; + const asset = context.assets[context["_index"]]; + const emittedText = asset.emitted + ? printer.print("asset.emitted", asset.emitted, { + ...context, + inSummary: true + }) + : ""; + const infoText = printer.print("asset.info", asset.info, { + ...context, + inSummary: true + }); + const text = `${emittedText}${infoText}`; + if (text.length > assetsTable[3].colSize) { + assetsTable[3].colSize = text.length; + } + return `${emittedText}${infoText}`; + }, + "asset.separator!": () => "\n", + "asset.filteredRelated": (filteredRelated, { asset: { related } }) => + filteredRelated > 0 + ? `${moreCount(related, filteredRelated)} related ${plural( + filteredRelated, + "asset", + "assets" + )}` + : undefined, + "asset.filteredChildren": (filteredChildren, { asset: { children } }) => + filteredChildren > 0 + ? `${moreCount(children, filteredChildren)} ${plural( + filteredChildren, + "asset", + "assets" + )}` + : undefined, + + assetChunk: (id, { formatChunkId }) => formatChunkId(id), + + assetChunkName: name => name, + assetChunkIdHint: name => name, + + "module.type": type => (type !== "module" ? type : undefined), + "module.id": (id, { formatModuleId }) => + isValidId(id) ? formatModuleId(id) : undefined, + "module.name": (name, { bold }) => { + const [prefix, resource] = getModuleName(name); + return `[${prefix || ""}${bold(resource || "")}]`; + }, + "module.identifier": identifier => undefined, + "module.layer": (layer, { formatLayer }) => + layer ? formatLayer(layer) : undefined, + "module.size": size => SizeFormatHelpers.formatSize(size), + "module.chunks[]": (id, { formatChunkId }) => formatChunkId(id), + "module.depth": (depth, { formatFlag }) => + typeof depth === "number" ? formatFlag(`depth ${depth}`) : undefined, + "module.cacheable": (cacheable, { formatFlag, red }) => + cacheable === false ? red(formatFlag("not cacheable")) : undefined, + // "module.orphan": (orphan, { formatFlag, yellow }) => + // orphan ? yellow(formatFlag("orphan")) : undefined, + // "module.runtime": (runtime, { formatFlag, yellow }) => + // runtime ? yellow(formatFlag("runtime")) : undefined, + "module.optional": (optional, { formatFlag, yellow }) => + optional ? yellow(formatFlag("optional")) : undefined, + // "module.dependent": (dependent, { formatFlag, cyan }) => + // dependent ? cyan(formatFlag("dependent")) : undefined, + "module.built": (built, { formatFlag, green }) => + built ? green(formatFlag("built")) : undefined, + // "module.codeGenerated": (codeGenerated, { formatFlag, yellow }) => + // codeGenerated ? yellow(formatFlag("code generated")) : undefined, + // "module.buildTimeExecuted": (buildTimeExecuted, { formatFlag, green }) => + // buildTimeExecuted ? green(formatFlag("build time executed")) : undefined, + // "module.cached": (cached, { formatFlag, green }) => + // cached ? green(formatFlag("cached")) : undefined, + "module.assets": (assets, { formatFlag, magenta }) => + assets && assets.length + ? magenta( + formatFlag( + `${assets.length} ${plural(assets.length, "asset", "assets")}` + ) + ) + : undefined, + "module.prefetched": (prefetched, { magenta, formatFlag }) => + prefetched ? magenta(formatFlag("prefetched")) : undefined, + "module.warnings": (warnings, { formatFlag, yellow }) => + warnings + ? yellow( + formatFlag(`${warnings} ${plural(warnings, "warning", "warnings")}`) + ) + : undefined, + "module.errors": (errors, { formatFlag, red }) => + errors + ? red(formatFlag(`${errors} ${plural(errors, "error", "errors")}`)) + : undefined, + "module.providedExports": (providedExports, { formatFlag, cyan }) => { + if (Array.isArray(providedExports)) { + if (providedExports.length === 0) return cyan(formatFlag("no exports")); + return cyan(formatFlag(`exports: ${providedExports.join(", ")}`)); + } + }, + "module.usedExports": (usedExports, { formatFlag, cyan, module }) => { + if (usedExports !== true) { + if (usedExports === null) return cyan(formatFlag("used exports unknown")); + if (usedExports === false) return cyan(formatFlag("module unused")); + if (Array.isArray(usedExports)) { + if (usedExports.length === 0) + return cyan(formatFlag("no exports used")); + const providedExportsCount = Array.isArray(module.providedExports) + ? module.providedExports.length + : null; + if ( + providedExportsCount !== null && + providedExportsCount === usedExports.length + ) { + return cyan(formatFlag("all exports used")); + } else { + return cyan( + formatFlag(`only some exports used: ${usedExports.join(", ")}`) + ); + } + } + } + }, + "module.optimizationBailout[]": (optimizationBailout, { yellow }) => + yellow(optimizationBailout), + "module.issuerPath": (issuerPath, { module }) => + module.profile ? undefined : "", + "module.profile": profile => undefined, + "module.filteredModules": (filteredModules, { module: { modules } }) => + filteredModules > 0 + ? `${moreCount(modules, filteredModules)} nested ${plural( + filteredModules, + "module", + "modules" + )}` + : undefined, + "module.filteredReasons": (filteredReasons, { module: { reasons } }) => + filteredReasons > 0 + ? `${moreCount(reasons, filteredReasons)} ${plural( + filteredReasons, + "reason", + "reasons" + )}` + : undefined, + "module.filteredChildren": (filteredChildren, { module: { children } }) => + filteredChildren > 0 + ? `${moreCount(children, filteredChildren)} ${plural( + filteredChildren, + "module", + "modules" + )}` + : undefined, + "module.separator!": () => "\n", + + "moduleIssuer.id": (id, { formatFlag, compilation: { module } }) => + formatFlag(id), + "moduleIssuer.profile.total": (value, { formatTime }) => formatTime(value), + + "moduleReason.type": type => type, + "moduleReason.userRequest": (userRequest, { cyan }) => cyan(userRequest), + "moduleReason.moduleId": (moduleId, { formatFlag }) => + // isValidId(moduleId) ? formatModuleId(moduleId) : undefined, + formatFlag(moduleId), + "moduleReason.module": (module, { magenta }) => magenta(module), + "moduleReason.loc": loc => loc, + "moduleReason.explanation": (explanation, { cyan }) => cyan(explanation), + // "moduleReason.active": (active, { formatFlag }) => + // active ? undefined : formatFlag("inactive"), + // "moduleReason.resolvedModule": (module, { magenta }) => magenta(module), + // "moduleReason.filteredChildren": ( + // filteredChildren, + // { moduleReason: { children } } + // ) => + // filteredChildren > 0 + // ? `${moreCount(children, filteredChildren)} ${plural( + // filteredChildren, + // "reason", + // "reasons" + // )}` + // : undefined, + + "module.profile.total": (value, { formatTime }) => formatTime(value), + "module.profile.resolving": (value, { formatTime }) => + `resolving: ${formatTime(value)}`, + "module.profile.restoring": (value, { formatTime }) => + `restoring: ${formatTime(value)}`, + "module.profile.integration": (value, { formatTime }) => + `integration: ${formatTime(value)}`, + "module.profile.building": (value, { formatTime }) => + `building: ${formatTime(value)}`, + "module.profile.storing": (value, { formatTime }) => + `storing: ${formatTime(value)}`, + "module.profile.additionalResolving": (value, { formatTime }) => + value ? `additional resolving: ${formatTime(value)}` : undefined, + "module.profile.additionalIntegration": (value, { formatTime }) => + value ? `additional integration: ${formatTime(value)}` : undefined, + + "chunkGroup.kind!": (_, { chunkGroupKind }) => chunkGroupKind, + "chunkGroup.separator!": () => "\n", + "chunkGroup.name": (name, { bold }) => bold(name), + "chunkGroup.isOverSizeLimit": (isOverSizeLimit, { formatFlag, yellow }) => + isOverSizeLimit ? yellow(formatFlag("big")) : undefined, + // "chunkGroup.assetsSize": (size, { formatSize }) => + // size ? formatSize(size) : undefined, + // "chunkGroup.auxiliaryAssetsSize": (size, { formatSize }) => + // size ? `(${formatSize(size)})` : undefined, + // "chunkGroup.filteredAssets": (n, { chunkGroup: { assets } }) => + // n > 0 + // ? `${moreCount(assets, n)} ${plural(n, "asset", "assets")}` + // : undefined, + // "chunkGroup.filteredAuxiliaryAssets": ( + // n, + // { chunkGroup: { auxiliaryAssets } } + // ) => + // n > 0 + // ? `${moreCount(auxiliaryAssets, n)} auxiliary ${plural( + // n, + // "asset", + // "assets" + // )}` + // : undefined, + "chunkGroup.is!": () => "=", + "chunkGroupAsset.name": (asset, { green }) => green(asset), + // "chunkGroupAsset.size": (size, { formatSize, chunkGroup }) => + // chunkGroup.assets.length > 1 || + // (chunkGroup.auxiliaryAssets && chunkGroup.auxiliaryAssets.length > 0) + // ? formatSize(size) + // : undefined, + // "chunkGroup.children": (children, context, printer) => + // Array.isArray(children) + // ? undefined + // : printer.print( + // context.type, + // Object.keys(children).map(key => ({ + // type: key, + // children: children[key] + // })), + // context + // ), + // "chunkGroupChildGroup.type": type => `${type}:`, + // "chunkGroupChild.assets[]": (file, { formatFilename }) => + // formatFilename(file), + // "chunkGroupChild.chunks[]": (id, { formatChunkId }) => formatChunkId(id), + // "chunkGroupChild.name": name => (name ? `(name: ${name})` : undefined), + "chunk.id": id => { + let buf = ""; + if (chunk.id < 1000) buf += " "; + if (chunk.id < 100) buf += " "; + if (chunk.id < 10) buf += " "; + return (buf += `{${yellow(id)}}`); + }, + "chunk.files[]": (file, { green }) => green(file.join(", ")), + "chunk.names[]": names => + names.length ? `(${names.join(", ")})` : undefined, + "chunk.idHints[]": name => name, + "chunk.runtime[]": name => name, + "chunk.sizes": (sizes, context) => printSizes(sizes, context), + // "chunk.parents[]": (parents, context) => + // context.formatChunkId(parents, "parent"), + // "chunk.siblings[]": (siblings, context) => + // context.formatChunkId(siblings, "sibling"), + // "chunk.children[]": (children, context) => + // context.formatChunkId(children, "child"), + "chunk.childrenByOrder": (childrenByOrder, context, printer) => + Array.isArray(childrenByOrder) + ? undefined + : printer.print( + context.type, + Object.keys(childrenByOrder).map(key => ({ + type: key, + children: childrenByOrder[key] + })), + context + ), + "chunk.childrenByOrder[].type": type => `${type}:`, + "chunk.childrenByOrder[].children[]": (id, { formatChunkId }) => + isValidId(id) ? formatChunkId(id) : undefined, + "chunk.entry": (entry, { formatFlag, yellow }) => + entry ? yellow(formatFlag("entry")) : undefined, + "chunk.initial": (initial, { formatFlag, yellow }) => + initial ? yellow(formatFlag("initial")) : undefined, + "chunk.rendered": (rendered, { formatFlag, green }) => + rendered ? green(formatFlag("rendered")) : undefined, + "chunk.recorded": (recorded, { formatFlag, green }) => + recorded ? green(formatFlag("recorded")) : undefined, + "chunk.reason": (reason, { yellow }) => (reason ? yellow(reason) : undefined), + "chunk.filteredModules": (filteredModules, { chunk: { modules } }) => + filteredModules > 0 + ? `${moreCount(modules, filteredModules)} chunk ${plural( + filteredModules, + "module", + "modules" + )}` + : undefined, + "chunk.separator!": () => "\n", + + "chunkOrigin.reasons": reasons => + reasons.length ? yellow(reasons.join(" ")) : undefined, + "chunkOrigin.request": request => request, + "chunkOrigin.moduleId": (moduleId, { formatFlag }) => formatFlag(moduleId), + "chunkOrigin.moduleName": (moduleName, { bold }) => bold(moduleName), + "chunkOrigin.loc": loc => loc, + + "error.compilerPath": (compilerPath, { bold }) => + compilerPath ? bold(`(${compilerPath})`) : undefined, + "error.chunkId": (chunkId, { formatChunkId }) => + isValidId(chunkId) ? formatChunkId(chunkId) : undefined, + "error.chunkEntry": (chunkEntry, { formatFlag }) => + chunkEntry ? formatFlag("entry") : undefined, + "error.chunkInitial": (chunkInitial, { formatFlag }) => + chunkInitial ? formatFlag("initial") : undefined, + "error.file": (file, { bold }) => bold(file), + "error.moduleName": (moduleName, { bold }) => { + return moduleName.includes("!") + ? `${bold(moduleName.replace(/^(\s|\S)*!/, ""))} (${moduleName})` + : `${bold(moduleName)}`; + }, + "error.loc": (loc, { green }) => green(loc), + "error.message": (message, { bold, formatError }) => + message.includes("\u001b[") ? message : bold(formatError(message)), + "error.details": (details, { formatError }) => formatError(details), + "error.stack": stack => stack, + "error.moduleTrace": moduleTrace => undefined, + "error.separator!": () => "\n", + + "loggingEntry(error).loggingEntry.message": (message, { red }) => + mapLines(message, x => ` ${red(x)}`), + "loggingEntry(warn).loggingEntry.message": (message, { yellow }) => + mapLines(message, x => ` ${yellow(x)}`), + "loggingEntry(info).loggingEntry.message": (message, { green }) => + mapLines(message, x => ` ${green(x)}`), + "loggingEntry(log).loggingEntry.message": (message, { bold }) => + mapLines(message, x => ` ${bold(x)}`), + "loggingEntry(debug).loggingEntry.message": message => + mapLines(message, x => ` ${x}`), + "loggingEntry(trace).loggingEntry.message": message => + mapLines(message, x => ` ${x}`), + "loggingEntry(status).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => ` ${magenta(x)}`), + "loggingEntry(profile).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => `

${magenta(x)}`), + "loggingEntry(profileEnd).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => `

${magenta(x)}`), + "loggingEntry(time).loggingEntry.message": (message, { magenta }) => + mapLines(message, x => ` ${magenta(x)}`), + "loggingEntry(group).loggingEntry.message": (message, { cyan }) => + mapLines(message, x => `<-> ${cyan(x)}`), + "loggingEntry(groupCollapsed).loggingEntry.message": (message, { cyan }) => + mapLines(message, x => `<+> ${cyan(x)}`), + "loggingEntry(clear).loggingEntry": () => " -------", + "loggingEntry(groupCollapsed).loggingEntry.children": () => "", + "loggingEntry.trace[]": trace => + trace ? mapLines(trace, x => `| ${x}`) : undefined, + + "moduleTraceItem.originName": originName => originName, + + loggingGroup: loggingGroup => + loggingGroup.entries.length === 0 ? "" : undefined, + "loggingGroup.debug": (flag, { red }) => (flag ? red("DEBUG") : undefined), + "loggingGroup.name": (name, { bold }) => bold(`LOG from ${name}`), + "loggingGroup.separator!": () => "\n", + "loggingGroup.filteredEntries": filteredEntries => + filteredEntries > 0 ? `+ ${filteredEntries} hidden lines` : undefined, + + "moduleTraceDependency.loc": loc => loc +}; + +/** @type {Record} */ +const ITEM_NAMES = { + "compilation.assets[]": "asset", + "compilation.modules[]": "module", + "compilation.chunks[]": "chunk", + "compilation.entrypoints[]": "chunkGroup", + "compilation.namedChunkGroups[]": "chunkGroup", + "compilation.errors[]": "error", + "compilation.warnings[]": "error", + "compilation.logging[]": "loggingGroup", + "compilation.children[]": "compilation", + "asset.related[]": "asset", + "asset.children[]": "asset", + "asset.chunks[]": "assetChunk", + "asset.auxiliaryChunks[]": "assetChunk", + "asset.chunkNames[]": "assetChunkName", + "asset.chunkIdHints[]": "assetChunkIdHint", + "asset.auxiliaryChunkNames[]": "assetChunkName", + "asset.auxiliaryChunkIdHints[]": "assetChunkIdHint", + "chunkGroup.assets[]": "chunkGroupAsset", + "chunkGroup.auxiliaryAssets[]": "chunkGroupAsset", + "chunkGroupChild.assets[]": "chunkGroupAsset", + "chunkGroupChild.auxiliaryAssets[]": "chunkGroupAsset", + "chunkGroup.children[]": "chunkGroupChildGroup", + "chunkGroupChildGroup.children[]": "chunkGroupChild", + "module.modules[]": "module", + "module.children[]": "module", + "module.reasons[]": "moduleReason", + "moduleReason.children[]": "moduleReason", + "module.issuerPath[]": "moduleIssuer", + "chunk.origins[]": "chunkOrigin", + "chunk.modules[]": "module", + "loggingGroup.entries[]": logEntry => + `loggingEntry(${logEntry.type}).loggingEntry`, + "loggingEntry.children[]": logEntry => + `loggingEntry(${logEntry.type}).loggingEntry`, + "error.moduleTrace[]": "moduleTraceItem", + "moduleTraceItem.dependencies[]": "moduleTraceDependency" +}; + +const ERROR_PREFERRED_ORDER = [ + "compilerPath", + "chunkId", + "chunkEntry", + "chunkInitial", + "file", + "separator!", + "moduleName", + "loc", + "separator!", + "message", + "separator!", + "details", + "separator!", + "stack", + "separator!", + "missing", + "separator!", + "moduleTrace" +]; + +/** @type {Record} */ +const PREFERRED_ORDERS = { + compilation: [ + "name", + "hash", + "version", + "time", + "builtAt", + "env", + "publicPath", + "assetsTh!", + "assets", + "filteredAssets", + "entrypoints", + "namedChunkGroups", + "chunks", + "modules", + "filteredModules", + "children", + "logging", + "warnings", + "warningsInChildren!", + "filteredWarningDetailsCount", + "errors", + "errorsInChildren!", + "filteredErrorDetailsCount", + "summary!", + "needAdditionalPass" + ], + asset: [ + // "type", + "name", + "size", + // "chunks", + // "auxiliaryChunks", + // "emitted", + // "comparedForEmit", + // "cached", + "summary!", + // "info", + "isOverSizeLimit", + "chunkNames" + // "auxiliaryChunkNames", + // "chunkIdHints", + // "auxiliaryChunkIdHints", + // "related", + // "filteredRelated", + // "children", + // "filteredChildren" + ], + "asset.info": [ + "immutable", + "sourceFilename", + "javascriptModule", + "development", + "hotModuleReplacement" + ], + chunkGroup: [ + "kind!", + "name", + "isOverSizeLimit", + "assetsSize", + "auxiliaryAssetsSize", + "is!", + "assets", + "filteredAssets", + "auxiliaryAssets", + "filteredAuxiliaryAssets", + "separator!", + "children" + ], + chunkGroupAsset: ["name", "size"], + chunkGroupChildGroup: ["type", "children"], + chunkGroupChild: ["assets", "chunks", "name"], + module: [ + "type", + "name", + "identifier", + "id", + "layer", + "size", + "chunks", + "depth", + "cacheable", + "orphan", + "runtime", + "optional", + "dependent", + "built", + "codeGenerated", + "cached", + "assets", + "failed", + "warnings", + "errors", + "children", + "filteredChildren", + "providedExports", + "separator!", + "usedExports", + "separator!", + "optimizationBailout", + "separator!", + "reasons", + "separator!", + "filteredReasons", + "issuerPath", + "profile", + "separator!", + "modules", + "filteredModules" + ], + moduleReason: [ + "active", + "type", + "userRequest", + "moduleId", + "module", + "resolvedModule", + "loc", + "explanation", + "children", + "filteredChildren" + ], + "module.profile": [ + "total", + "separator!", + "resolving", + "restoring", + "integration", + "building", + "storing", + "additionalResolving", + "additionalIntegration" + ], + chunk: [ + "id", + "runtime", + "files", + "names", + "idHints", + "sizes", + "parents", + "siblings", + "children", + "childrenByOrder", + "entry", + "initial", + "rendered", + "recorded", + "reason", + "separator!", + "origins", + "separator!", + "modules", + "separator!", + "filteredModules" + ], + chunkOrigin: ["request", "moduleId", "moduleName", "loc"], + error: ERROR_PREFERRED_ORDER, + warning: ERROR_PREFERRED_ORDER, + "chunk.childrenByOrder[]": ["type", "children"], + loggingGroup: [ + "debug", + "name", + "separator!", + "entries", + "separator!", + "filteredEntries" + ], + loggingEntry: ["message", "trace", "children"] +}; + +const itemsJoinOneLine = items => items.filter(Boolean).join(" "); +const itemsJoinOneLineBrackets = items => + items.length > 0 ? `(${items.filter(Boolean).join(" ")})` : undefined; +const itemsJoinMoreSpacing = items => items.filter(Boolean).join("\n\n"); +const itemsJoinComma = items => items.filter(Boolean).join(", "); +const itemsJoinCommaBrackets = items => + items.length > 0 ? `(${items.filter(Boolean).join(", ")})` : undefined; +const itemsJoinCommaBracketsWithName = name => items => + items.length > 0 + ? `(${name}: ${items.filter(Boolean).join(", ")})` + : undefined; + +/** @type {Record string>} */ +const SIMPLE_ITEMS_JOINER = { + "chunk.parents": itemsJoinOneLine, + "chunk.siblings": itemsJoinOneLine, + "chunk.children": itemsJoinOneLine, + "chunk.names": itemsJoinCommaBrackets, + "chunk.idHints": itemsJoinCommaBracketsWithName("id hint"), + "chunk.runtime": itemsJoinCommaBracketsWithName("runtime"), + "chunk.files": itemsJoinComma, + "chunk.childrenByOrder": itemsJoinOneLine, + "chunk.childrenByOrder[].children": itemsJoinOneLine, + "chunkGroup.assets": itemsJoinOneLine, + "chunkGroup.auxiliaryAssets": itemsJoinOneLineBrackets, + "chunkGroupChildGroup.children": itemsJoinComma, + "chunkGroupChild.assets": itemsJoinOneLine, + "chunkGroupChild.auxiliaryAssets": itemsJoinOneLineBrackets, + "asset.chunks": itemsJoinComma, + "asset.auxiliaryChunks": itemsJoinCommaBrackets, + "asset.chunkNames": itemsJoinCommaBracketsWithName("name"), + "asset.auxiliaryChunkNames": itemsJoinCommaBracketsWithName("auxiliary name"), + "asset.chunkIdHints": itemsJoinCommaBracketsWithName("id hint"), + "asset.auxiliaryChunkIdHints": + itemsJoinCommaBracketsWithName("auxiliary id hint"), + "module.chunks": itemsJoinOneLine, + "module.issuerPath": items => + items + .filter(Boolean) + .map(item => `${item} ->`) + .join(" "), + "compilation.errors": itemsJoinMoreSpacing, + "compilation.warnings": itemsJoinMoreSpacing, + "compilation.logging": itemsJoinMoreSpacing, + "compilation.children": items => indent(itemsJoinMoreSpacing(items), " "), + "moduleTraceItem.dependencies": itemsJoinOneLine, + "loggingEntry.children": items => + indent(items.filter(Boolean).join("\n"), " ", false) +}; + +const joinOneLine = items => + items + .map(item => item.content) + .filter(Boolean) + .join(" "); + +const joinInBrackets = items => { + const res = []; + let mode = 0; + for (const item of items) { + if (item.element === "separator!") { + switch (mode) { + case 0: + case 1: + mode += 2; + break; + case 4: + res.push(")"); + mode = 3; + break; + } + } + if (!item.content) continue; + switch (mode) { + case 0: + mode = 1; + break; + case 1: + res.push(" "); + break; + case 2: + res.push("("); + mode = 4; + break; + case 3: + res.push(" ("); + mode = 4; + break; + case 4: + res.push(", "); + break; + } + res.push(item.content); + } + if (mode === 4) res.push(")"); + return res.join(""); +}; + +const indent = (str, prefix, noPrefixInFirstLine) => { + const rem = str.replace(/\n([^\n])/g, "\n" + prefix + "$1"); + if (noPrefixInFirstLine) return rem; + const ind = str[0] === "\n" ? "" : prefix; + return ind + rem; +}; + +const joinExplicitNewLine = (items, indenter) => { + let firstInLine = true; + let first = true; + return items + .map(item => { + if (!item || !item.content) return; + let content = indent(item.content, first ? "" : indenter, !firstInLine); + if (firstInLine) { + content = content.replace(/^\n+/, ""); + } + if (!content) return; + first = false; + const noJoiner = firstInLine || content.startsWith("\n"); + firstInLine = content.endsWith("\n"); + return noJoiner ? content : " " + content; + }) + .filter(Boolean) + .join("") + .trim(); +}; + +const joinError = + error => + (items, { red, yellow }) => + `${error ? red("ERROR") : yellow("WARNING")} in ${joinExplicitNewLine( + items, + "" + )}`; + +/** @type {Record string>} */ +const SIMPLE_ELEMENT_JOINERS = { + compilation: items => { + const result = []; + let lastNeedMore = false; + for (const item of items) { + if (!item.content) continue; + const needMoreSpace = + item.element === "warnings" || + item.element === "filteredWarningDetailsCount" || + item.element === "errors" || + item.element === "filteredErrorDetailsCount" || + item.element === "logging"; + if (result.length !== 0) { + result.push(needMoreSpace || lastNeedMore ? "\n\n" : "\n"); + } + result.push(item.content); + lastNeedMore = needMoreSpace; + } + if (lastNeedMore) result.push("\n"); + return result.join(""); + }, + asset: items => + joinExplicitNewLine( + items.map(item => { + if ( + (item.element === "related" || item.element === "children") && + item.content + ) { + return { + ...item, + content: `\n${item.content}\n` + }; + } + return item; + }), + " " + ), + "asset.info": joinOneLine, + module: (items, { module }) => { + let hasName = false; + return joinExplicitNewLine( + items.map(item => { + switch (item.element) { + case "id": + if (module.id === module.name) { + if (hasName) return false; + if (item.content) hasName = true; + } + break; + case "name": + if (hasName) return false; + if (item.content) hasName = true; + break; + case "providedExports": + case "usedExports": + case "optimizationBailout": + case "reasons": + case "issuerPath": + case "profile": + case "children": + case "modules": + if (item.content) { + return { + ...item, + content: `\n${item.content}\n` + }; + } + break; + } + return item; + }), + " " + ); + }, + chunk: items => { + let hasEntry = false; + return ( + "chunk " + + joinExplicitNewLine( + items.filter(item => { + switch (item.element) { + case "entry": + if (item.content) hasEntry = true; + break; + case "initial": + if (hasEntry) return false; + break; + } + return true; + }), + " " + ) + ); + }, + "chunk.childrenByOrder[]": items => `(${joinOneLine(items)})`, + chunkGroup: items => joinExplicitNewLine(items, " "), + chunkGroupAsset: joinOneLine, + chunkGroupChildGroup: joinOneLine, + chunkGroupChild: joinOneLine, + // moduleReason: (items, { moduleReason }) => { + // let hasName = false; + // return joinOneLine( + // items.filter(item => { + // switch (item.element) { + // case "moduleId": + // if (moduleReason.moduleId === moduleReason.module && item.content) + // hasName = true; + // break; + // case "module": + // if (hasName) return false; + // break; + // case "resolvedModule": + // return ( + // moduleReason.module !== moduleReason.resolvedModule && + // item.content + // ); + // } + // return true; + // }) + // ); + // }, + moduleReason: (items, { moduleReason }) => { + let hasName = false; + return joinExplicitNewLine( + items.map(item => { + switch (item.element) { + case "moduleId": + if (moduleReason.moduleId === moduleReason.module && item.content) + hasName = true; + break; + case "module": + if (hasName) return false; + break; + case "resolvedModule": + if (moduleReason.module === moduleReason.resolvedModule) + return false; + break; + case "children": + if (item.content) { + return { + ...item, + content: `\n${item.content}\n` + }; + } + break; + } + return item; + }), + " " + ); + }, + "module.profile": joinInBrackets, + moduleIssuer: joinOneLine, + chunkOrigin: items => "> " + joinOneLine(items), + "errors[].error": joinError(true), + "warnings[].error": joinError(false), + loggingGroup: items => joinExplicitNewLine(items, "").trimEnd(), + moduleTraceItem: items => " @ " + joinOneLine(items), + moduleTraceDependency: joinOneLine +}; + +const AVAILABLE_COLORS = { + bold: "\u001b[1m", + yellow: "\u001b[1m\u001b[33m", + red: "\u001b[1m\u001b[31m", + green: "\u001b[1m\u001b[32m", + cyan: "\u001b[1m\u001b[36m", + magenta: "\u001b[1m\u001b[35m" +}; + +const AVAILABLE_FORMATS = { + formatChunkId: (id, { yellow }, direction) => { + switch (direction) { + case "parent": + return `<{${yellow(id)}}>`; + case "sibling": + return `={${yellow(id)}}=`; + case "child": + return `>{${yellow(id)}}<`; + default: + return `{${yellow(id)}}`; + } + }, + formatModuleId: id => `[${id}]`, + formatFilename: (filename, { green, yellow }, oversize) => + (oversize ? yellow : green)(filename), + formatFlag: flag => `[${flag}]`, + formatLayer: layer => `(in ${layer})`, + formatSize: require("./SizeFormatHelpers").formatSize, + formatDateTime: (dateTime, { bold }) => { + const builtAtDate = new Date(dateTime); + let timeZone = undefined; + try { + builtAtDate.toLocaleTimeString(); + } catch (err) { + // Force UTC if runtime timezone is unsupported + timeZone = "UTC"; + } + const date = builtAtDate.toLocaleDateString(undefined, { + day: "2-digit", + month: "2-digit", + year: "numeric", + timeZone + }); + const time = builtAtDate.toLocaleTimeString(undefined, { timeZone }); + return `${date} ${bold(time)}}`; + }, + formatTime: (time, { bold, green, yellow, red }) => { + let times = [800, 400, 200, 100]; + if (time) { + times = [time / 2, time / 4, time / 8, time / 16]; + } + if (time < times[3]) return `${time}ms`; + else if (time < times[2]) return `${bold(time)}ms`; + else if (time < times[1]) return `${green(time)}ms`; + else if (time < times[0]) return `${yellow(time)}ms`; + else return `${red(time)}ms`; + }, + formatError: e => { + // if (message.includes("\u001b[")) return message; + // const highlights = [ + // { regExp: /(Did you mean .+)/g, format: green }, + // { + // regExp: /(Set 'mode' option to 'development' or 'production')/g, + // format: green + // }, + // { regExp: /(\(module has no exports\))/g, format: red }, + // { regExp: /\(possible exports: (.+)\)/g, format: green }, + // { regExp: /(?:^|\n)(.* doesn't exist)/g, format: red }, + // { regExp: /('\w+' option has not been set)/g, format: red }, + // { + // regExp: /(Emitted value instead of an instance of Error)/g, + // format: yellow + // }, + // { regExp: /(Used? .+ instead)/gi, format: yellow }, + // { regExp: /\b(deprecated|must|required)\b/g, format: yellow }, + // { + // regExp: /\b(BREAKING CHANGE)\b/gi, + // format: red + // }, + // { + // regExp: + // /\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, + // format: red + // } + // ]; + // for (const { regExp, format } of highlights) { + // message = message.replace(regExp, (match, content) => { + // return match.replace(content, format(content)); + // }); + // } + // return message; + return e.formatted; + } +}; + +const RESULT_MODIFIER = { + "module.modules": result => { + return indent(result, "| "); + } +}; + +const createOrder = (array, preferredOrder) => { + const originalArray = array.slice(); + const set = new Set(array); + const usedSet = new Set(); + array.length = 0; + for (const element of preferredOrder) { + if (element.endsWith("!") || set.has(element)) { + array.push(element); + usedSet.add(element); + } + } + for (const element of originalArray) { + if (!usedSet.has(element)) { + array.push(element); + } + } + return array; +}; + +class DefaultStatsPrinterPlugin { + /** + * Apply the plugin + * @param {Compiler} compiler the compiler instance + * @returns {void} + */ + apply(compiler) { + compiler.hooks.compilation.tap("DefaultStatsPrinterPlugin", compilation => { + compilation.hooks.statsPrinter.tap( + "DefaultStatsPrinterPlugin", + (stats, options, context) => { + // Put colors into context + stats.hooks.print + .for("compilation") + .tap("DefaultStatsPrinterPlugin", (compilation, context) => { + for (const color of Object.keys(AVAILABLE_COLORS)) { + let start; + if (options.colors) { + if ( + typeof options.colors === "object" && + typeof options.colors[color] === "string" + ) { + start = options.colors[color]; + } else { + start = AVAILABLE_COLORS[color]; + } + } + if (start) { + context[color] = str => + `${start}${ + typeof str === "string" + ? str.replace( + /((\u001b\[39m|\u001b\[22m|\u001b\[0m)+)/g, + `$1${start}` + ) + : str + }\u001b[39m\u001b[22m`; + } else { + context[color] = str => str; + } + } + for (const format of Object.keys(AVAILABLE_FORMATS)) { + context[format] = (content, ...args) => + AVAILABLE_FORMATS[format](content, context, ...args); + } + context.timeReference = compilation.time; + const assetsTable = [ + { value: "Asset", colSize: 0, align: "right" }, + { value: "Size", colSize: 0, align: "right" }, + { value: "Chunks", colSize: 0, align: "right" }, + { value: " ", colSize: 0, align: "left", indexedSize: [] }, + { value: " ", colSize: 0, align: "left" }, + { value: "Chunk Names", colSize: 0, align: "left" } + ]; + context.assetsTable = assetsTable; + }); + + for (const key of Object.keys(SIMPLE_PRINTERS)) { + stats.hooks.print + .for(key) + .tap("DefaultStatsPrinterPlugin", (obj, ctx) => + SIMPLE_PRINTERS[key](obj, ctx, stats) + ); + } + + for (const key of Object.keys(PREFERRED_ORDERS)) { + const preferredOrder = PREFERRED_ORDERS[key]; + stats.hooks.sortElements + .for(key) + .tap("DefaultStatsPrinterPlugin", (elements, context) => { + createOrder(elements, preferredOrder); + }); + } + + for (const key of Object.keys(ITEM_NAMES)) { + const itemName = ITEM_NAMES[key]; + stats.hooks.getItemName + .for(key) + .tap( + "DefaultStatsPrinterPlugin", + typeof itemName === "string" ? () => itemName : itemName + ); + } + + for (const key of Object.keys(SIMPLE_ITEMS_JOINER)) { + const joiner = SIMPLE_ITEMS_JOINER[key]; + stats.hooks.printItems + .for(key) + .tap("DefaultStatsPrinterPlugin", joiner); + } + + for (const key of Object.keys(SIMPLE_ELEMENT_JOINERS)) { + const joiner = SIMPLE_ELEMENT_JOINERS[key]; + stats.hooks.printElements + .for(key) + .tap("DefaultStatsPrinterPlugin", joiner); + } + + for (const key of Object.keys(RESULT_MODIFIER)) { + const modifier = RESULT_MODIFIER[key]; + stats.hooks.result + .for(key) + .tap("DefaultStatsPrinterPlugin", modifier); + } + } + ); + }); + } +} +module.exports = DefaultStatsPrinterPlugin; diff --git a/packages/rspack/src/lib/SizeFormatHelpers.js b/packages/rspack/src/lib/SizeFormatHelpers.js new file mode 100644 index 00000000000..c8518cd019d --- /dev/null +++ b/packages/rspack/src/lib/SizeFormatHelpers.js @@ -0,0 +1,18 @@ +module.exports = { + formatSize: size => { + if (typeof size !== "number" || Number.isNaN(size) === true) { + return "unknown size"; + } + + if (size <= 0) { + return "0 bytes"; + } + + const abbreviations = ["bytes", "KiB", "MiB", "GiB"]; + const index = Math.floor(Math.log(size) / Math.log(1024)); + + return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${ + abbreviations[index] + }`; + } +}; diff --git a/packages/rspack/src/rspack.ts b/packages/rspack/src/rspack.ts index 10582b31fa5..8f7a2c449c7 100644 --- a/packages/rspack/src/rspack.ts +++ b/packages/rspack/src/rspack.ts @@ -31,6 +31,7 @@ import MultiStats from "./multiStats"; import assert from "assert"; import { asArray, isNil } from "./util"; import IgnoreWarningsPlugin from "./lib/ignoreWarningsPlugin"; +import DefaultStatsPrinterPlugin from "./lib/DefaultStatsPrinterPlugin"; function createMultiCompiler(options: MultiRspackOptions): MultiCompiler { const compilers = options.map(createCompiler); @@ -60,6 +61,8 @@ function createCompiler(userOptions: RspackOptions): Compiler { infrastructureLogging: options.infrastructureLogging }).apply(compiler); + new DefaultStatsPrinterPlugin().apply(compiler); + const logger = compiler.getInfrastructureLogger("config"); logger.debug( "RawOptions:", diff --git a/packages/rspack/src/stats.ts b/packages/rspack/src/stats.ts index 357e9dbdb45..ab56015ef7c 100644 --- a/packages/rspack/src/stats.ts +++ b/packages/rspack/src/stats.ts @@ -35,6 +35,28 @@ export type StatsCompilation = { children?: StatsCompilation[]; }; +const SizeFormatHelpers = { + formatSize: (size: number) => { + if (typeof size !== "number" || Number.isNaN(size) === true) { + return "unknown size"; + } + + if (size <= 0) { + return "0 bytes"; + } + + const abbreviations = ["bytes", "KiB", "MiB", "GiB"]; + const index = Math.floor(Math.log(size) / Math.log(1024)); + + return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${ + abbreviations[index] + }`; + } +}; + +const formatError = (e: binding.JsStatsError) => { + return e.formatted; +}; export class Stats { #inner: binding.JsStats; compilation: Compilation; @@ -144,10 +166,12 @@ export class Stats { forToString: true }); const useColors = optionsOrFallback(options.colors, false); - const obj: any = this.toJson(options, true); - return Stats.jsonToString(obj, useColors); - } + const data: any = this.toJson(options, true); + const statsPrinter = this.compilation.createStatsPrinter(options); + const result = statsPrinter.print("compilation", data, {}); + return result === undefined ? "" : result; + } static jsonToString(obj: any, useColors: boolean): any { const buf = []; @@ -900,7 +924,7 @@ export class Stats { } if (obj.children) { for (const child of obj.children) { - const childString = Stats.jsonToString(child, useColors); + const childString = this.jsonToString(child, useColors); if (childString) { if (child.name) { colors.normal("Child "); @@ -929,29 +953,6 @@ export class Stats { } } -const SizeFormatHelpers = { - formatSize: (size: unknown) => { - if (typeof size !== "number" || Number.isNaN(size) === true) { - return "unknown size"; - } - - if (size <= 0) { - return "0 bytes"; - } - - const abbreviations = ["bytes", "KiB", "MiB", "GiB"]; - const index = Math.floor(Math.log(size) / Math.log(1024)); - - return `${+(size / Math.pow(1024, index)).toPrecision(3)} ${ - abbreviations[index] - }`; - } -}; - -const formatError = (e: binding.JsStatsError) => { - return e.formatted; -}; - export const optionsOrFallback = ( options: boolean | undefined, fallback: boolean diff --git a/packages/rspack/src/statsPrinter.ts b/packages/rspack/src/statsPrinter.ts new file mode 100644 index 00000000000..c0c7787d70e --- /dev/null +++ b/packages/rspack/src/statsPrinter.ts @@ -0,0 +1,1015 @@ +import { + JsStatsAsset, + JsStatsChunk, + JsStatsChunkGroup, + JsStatsModule, + JsStatsModuleReason +} from "@rspack/binding"; +import { HookMap, SyncBailHook, SyncWaterfallHook, AsArray } from "tapable"; +import { StatsCompilation } from "./stats"; + +interface StatsPrinterContext { + type: string; + compilation: StatsCompilation; + chunkGroup: JsStatsChunkGroup; + asset: JsStatsAsset; + module: JsStatsModule; + chunk: JsStatsChunk; + moduleReason: JsStatsModuleReason; + bold: (str: string) => string; + yellow: (str: string) => string; + red: (str: string) => string; + green: (str: string) => string; + magenta: (str: string) => string; + cyan: (str: string) => string; + formatFilename: (file: string, oversize?: boolean) => string; + formatModuleId: (id: string) => string; + formatChunkId: ( + id: string, + direction?: "parent" | "child" | "sibling" + ) => string; + formatSize: (size: number) => string; + formatDateTime: (dateTime: number) => string; + formatFlag: (flag: string) => string; + formatTime: (time: number, boldQuantity?: boolean) => string; + chunkGroupKind: string; +} + +interface PrintedElement { + element: string; + content: string; +} + +export default class StatsPrinter { + hooks; + private _levelHookCache: Map, Map>; + private _inPrint: boolean; + constructor() { + this.hooks = Object.freeze({ + sortElements: new HookMap( + () => + new SyncBailHook<[string[], StatsPrinterContext], undefined>([ + "elements", + "context" + ]) + ), + printElements: new HookMap( + () => + new SyncBailHook<[PrintedElement[], StatsPrinterContext], undefined>([ + "printedElements", + "context" + ]) + ), + sortItems: new HookMap( + () => + new SyncBailHook<[any[], StatsPrinterContext], true>([ + "items", + "context" + ]) + ), + getItemName: new HookMap( + () => + new SyncBailHook<[any, StatsPrinterContext], string>([ + "item", + "context" + ]) + ), + printItems: new HookMap( + () => + new SyncBailHook<[string[], StatsPrinterContext], string>([ + "printedItems", + "context" + ]) + ), + print: new HookMap( + () => + new SyncBailHook<[{}, StatsPrinterContext], string>([ + "object", + "context" + ]) + ), + result: new HookMap( + () => + new SyncWaterfallHook<[string, StatsPrinterContext]>([ + "result", + "context" + ]) + ) + }); + this._levelHookCache = new Map(); + this._inPrint = false; + } + + _getAllLevelHooks(hookMap: HookMap, type: string): T[] { + let cache: Map | undefined = this._levelHookCache.get(hookMap); + if (cache === undefined) { + cache = new Map(); + this._levelHookCache.set(hookMap, cache); + } + const cacheEntry = cache.get(type); + if (cacheEntry !== undefined) { + return cacheEntry; + } + const hooks: T[] = []; + const typeParts = type.split("."); + for (let i = 0; i < typeParts.length; i++) { + const hook = hookMap.get(typeParts.slice(i).join(".")); + if (hook) { + hooks.push(hook); + } + } + cache.set(type, hooks); + return hooks; + } + + _forEachLevel( + hookMap: HookMap>, + type: string, + fn: (hook: SyncBailHook) => R + ): R | undefined { + for (const hook of this._getAllLevelHooks(hookMap, type)) { + const result = fn(hook); + if (result !== undefined) return result; + } + } + + _forEachLevelWaterfall( + hookMap: HookMap>, + type: string, + data: AsArray[0], + fn: (hook: SyncWaterfallHook, data: AsArray[0]) => AsArray[0] + ): AsArray[0] { + for (const hook of this._getAllLevelHooks(hookMap, type)) { + data = fn(hook, data); + } + return data; + } + + print(type: string, object: Object, baseContext: Object): string { + if (this._inPrint) { + return this._print(type, object, baseContext); + } else { + try { + this._inPrint = true; + return this._print(type, object, baseContext); + } finally { + this._levelHookCache.clear(); + this._inPrint = false; + } + } + } + + _print(type: string, object: any, baseContext: any) { + const context = { + ...baseContext, + type, + [type]: object + }; + + let printResult = this._forEachLevel(this.hooks.print, type, hook => + hook.call(object, context) + ); + if (printResult === undefined) { + if (Array.isArray(object)) { + const sortedItems = object.slice(); + this._forEachLevel(this.hooks.sortItems, type, h => + h.call(sortedItems, context) + ); + const printedItems = sortedItems.map((item, i) => { + const itemContext = { + ...context, + _index: i + }; + const itemName = this._forEachLevel( + this.hooks.getItemName, + `${type}[]`, + h => h.call(item, itemContext) + ); + if (itemName) itemContext[itemName] = item; + return this.print( + itemName ? `${type}[].${itemName}` : `${type}[]`, + item, + itemContext + ); + }); + printResult = this._forEachLevel(this.hooks.printItems, type, h => + h.call(printedItems, context) + ); + if (printResult === undefined) { + const result = printedItems.filter(Boolean); + if (result.length > 0) printResult = result.join("\n"); + } + } else if (object !== null && typeof object === "object") { + const elements = Object.keys(object).filter( + key => object[key] !== undefined + ); + this._forEachLevel(this.hooks.sortElements, type, h => + h.call(elements, context) + ); + const printedElements = elements.map(element => { + const content = this.print(`${type}.${element}`, object[element], { + ...context, + _parent: object, + _element: element, + [element]: object[element] + }); + return { element, content }; + }); + printResult = this._forEachLevel(this.hooks.printElements, type, h => + h.call(printedElements, context) + ); + if (printResult === undefined) { + const result = printedElements.map(e => e.content).filter(Boolean); + if (result.length > 0) printResult = result.join("\n"); + } + } + } + + return this._forEachLevelWaterfall( + this.hooks.result, + type, + printResult!, + (h, r) => h.call(r, context) + ); + } + // jsonToString(obj: any, useColors: boolean): any { + // const buf = []; + + // const defaultColors = { + // bold: "\u001b[1m", + // yellow: "\u001b[1m\u001b[33m", + // red: "\u001b[1m\u001b[31m", + // green: "\u001b[1m\u001b[32m", + // cyan: "\u001b[1m\u001b[36m", + // magenta: "\u001b[1m\u001b[35m" + // }; + + // const colors: any = Object.keys(defaultColors).reduce( + // (obj, color) => { + // // @ts-expect-error + // obj[color] = str => { + // if (useColors) { + // buf.push( + // useColors === true || useColors[color] === undefined + // ? // @ts-expect-error + // defaultColors[color] + // : useColors[color] + // ); + // } + // buf.push(str); + // if (useColors) { + // buf.push("\u001b[39m\u001b[22m"); + // } + // }; + // return obj; + // }, + // { + // // @ts-expect-error + // normal: str => buf.push(str) + // } + // ); + + // const coloredTime = (time: number) => { + // let times = [800, 400, 200, 100]; + // if (obj.time) { + // times = [obj.time / 2, obj.time / 4, obj.time / 8, obj.time / 16]; + // } + // if (time < times[3]) colors.normal(`${time}ms`); + // else if (time < times[2]) colors.bold(`${time}ms`); + // else if (time < times[1]) colors.green(`${time}ms`); + // else if (time < times[0]) colors.yellow(`${time}ms`); + // else colors.red(`${time}ms`); + // }; + + // const newline = () => buf.push("\n"); + + // const getText = ( + // arr: { [x: string]: { [x: string]: { value: any } } }, + // row: number, + // col: number + // ) => { + // return arr[row][col].value; + // }; + + // const table = ( + // array: string | any[], + // align: string | string[], + // splitter?: string + // ) => { + // const rows = array.length; + // const cols = array[0].length; + // const colSizes = new Array(cols); + // for (let col = 0; col < cols; col++) { + // colSizes[col] = 0; + // } + // for (let row = 0; row < rows; row++) { + // for (let col = 0; col < cols; col++) { + // // @ts-expect-error + // const value = `${getText(array, row, col)}`; + // if (value.length > colSizes[col]) { + // colSizes[col] = value.length; + // } + // } + // } + // for (let row = 0; row < rows; row++) { + // for (let col = 0; col < cols; col++) { + // const format = array[row][col].color; + // // @ts-expect-error + // const value = `${getText(array, row, col)}`; + // let l = value.length; + // if (align[col] === "l") { + // format(value); + // } + // for (; l < colSizes[col] && col !== cols - 1; l++) { + // colors.normal(" "); + // } + // if (align[col] === "r") { + // format(value); + // } + // if (col + 1 < cols && colSizes[col] !== 0) { + // colors.normal(splitter || " "); + // } + // } + // newline(); + // } + // }; + + // const getAssetColor = ( + // asset: { isOverSizeLimit: any }, + // defaultColor: any + // ) => { + // if (asset.isOverSizeLimit) { + // return colors.yellow; + // } + + // return defaultColor; + // }; + + // if (obj.hash) { + // colors.normal("Hash: "); + // colors.bold(obj.hash); + // newline(); + // } + // if (obj.version) { + // colors.normal("Version: rspack "); + // colors.bold(obj.version); + // newline(); + // } + // if (typeof obj.time === "number") { + // colors.normal("Time: "); + // colors.bold(obj.time); + // colors.normal("ms"); + // newline(); + // } + // if (typeof obj.builtAt === "number") { + // const builtAtDate = new Date(obj.builtAt); + // let timeZone = undefined; + + // try { + // builtAtDate.toLocaleTimeString(); + // } catch (err) { + // // Force UTC if runtime timezone is unsupported + // timeZone = "UTC"; + // } + + // colors.normal("Built at: "); + // colors.normal( + // builtAtDate.toLocaleDateString(undefined, { + // day: "2-digit", + // month: "2-digit", + // year: "numeric", + // timeZone + // }) + // ); + // colors.normal(" "); + // colors.bold(builtAtDate.toLocaleTimeString(undefined, { timeZone })); + // newline(); + // } + // if (obj.env) { + // colors.normal("Environment (--env): "); + // colors.bold(JSON.stringify(obj.env, null, 2)); + // newline(); + // } + // if (obj.publicPath) { + // colors.normal("PublicPath: "); + // colors.bold(obj.publicPath); + // newline(); + // } + + // if (obj.assets && obj.assets.length > 0) { + // const t = [ + // [ + // { + // value: "Asset", + // color: colors.bold + // }, + // { + // value: "Size", + // color: colors.bold + // }, + // { + // value: "Chunks", + // color: colors.bold + // }, + // { + // value: "", + // color: colors.bold + // }, + // { + // value: "", + // color: colors.bold + // }, + // { + // value: "Chunk Names", + // color: colors.bold + // } + // ] + // ]; + // for (const asset of obj.assets) { + // t.push([ + // { + // value: asset.name, + // color: getAssetColor(asset, colors.green) + // }, + // { + // value: SizeFormatHelpers.formatSize(asset.size), + // color: getAssetColor(asset, colors.normal) + // }, + // { + // value: asset.chunks.join(", "), + // color: colors.bold + // }, + // { + // value: [ + // asset.emitted && "[emitted]", + // asset.info?.immutable && "[immutable]", + // asset.info?.development && "[dev]", + // asset.info?.hotModuleReplacement && "[hmr]" + // ] + // .filter(Boolean) + // .join(" "), + // color: colors.green + // }, + // { + // value: asset.isOverSizeLimit ? "[big]" : "", + // color: getAssetColor(asset, colors.normal) + // }, + // { + // value: asset.chunkNames.join(", "), + // color: colors.normal + // } + // ]); + // } + // table(t, "rrrlll"); + // } + // if (obj.filteredAssets > 0) { + // colors.normal(" "); + // if (obj.assets.length > 0) colors.normal("+ "); + // colors.normal(obj.filteredAssets); + // if (obj.assets.length > 0) colors.normal(" hidden"); + // colors.normal(obj.filteredAssets !== 1 ? " assets" : " asset"); + // newline(); + // } + + // const processChunkGroups = ( + // namedGroups: { [x: string]: any }, + // prefix: string + // ) => { + // for (const name of Object.keys(namedGroups)) { + // const cg = namedGroups[name]; + // colors.normal(`${prefix} `); + // colors.bold(name); + // if (cg.isOverSizeLimit) { + // colors.normal(" "); + // colors.yellow("[big]"); + // } + // colors.normal(" ="); + // for (const asset of cg.assets) { + // colors.normal(" "); + // colors.green(asset.name); + // } + // // for (const name of Object.keys(cg.childAssets)) { + // // const assets = cg.childAssets[name]; + // // if (assets && assets.length > 0) { + // // colors.normal(" "); + // // colors.magenta(`(${name}:`); + // // for (const asset of assets) { + // // colors.normal(" "); + // // colors.green(asset); + // // } + // // colors.magenta(")"); + // // } + // // } + // newline(); + // } + // }; + + // if (obj.entrypoints) { + // processChunkGroups(obj.entrypoints, "Entrypoint"); + // } + + // if (obj.namedChunkGroups) { + // let outputChunkGroups = obj.namedChunkGroups; + // if (obj.entrypoints) { + // outputChunkGroups = Object.keys(outputChunkGroups) + // .filter(name => !obj.entrypoints[name]) + // .reduce((result, name) => { + // // @ts-expect-error + // result[name] = obj.namedChunkGroups[name]; + // return result; + // }, {}); + // } + // processChunkGroups(outputChunkGroups, "Chunk Group"); + // } + + // const modulesByIdentifier = {}; + // if (obj.modules) { + // for (const module of obj.modules) { + // // @ts-expect-error + // modulesByIdentifier[`$${module.identifier}`] = module; + // } + // } else if (obj.chunks) { + // for (const chunk of obj.chunks) { + // if (chunk.modules) { + // for (const module of chunk.modules) { + // // @ts-expect-error + // modulesByIdentifier[`$${module.identifier}`] = module; + // } + // } + // } + // } + + // const processModuleAttributes = (module: { + // size: any; + // chunks: any; + // depth: any; + // cacheable: boolean; + // optional: any; + // built: any; + // assets: string | any[]; + // prefetched: any; + // failed: any; + // warnings: number; + // errors: number; + // }) => { + // colors.normal(" "); + // colors.normal(SizeFormatHelpers.formatSize(module.size)); + // if (module.chunks) { + // for (const chunk of module.chunks) { + // colors.normal(" {"); + // colors.yellow(chunk); + // colors.normal("}"); + // } + // } + // if (typeof module.depth === "number") { + // colors.normal(` [depth ${module.depth}]`); + // } + // if (module.cacheable === false) { + // colors.red(" [not cacheable]"); + // } + // if (module.optional) { + // colors.yellow(" [optional]"); + // } + // if (module.built) { + // colors.green(" [built]"); + // } + // if (module.assets && module.assets.length) { + // colors.magenta( + // ` [${module.assets.length} asset${ + // module.assets.length === 1 ? "" : "s" + // }]` + // ); + // } + // if (module.prefetched) { + // colors.magenta(" [prefetched]"); + // } + // if (module.failed) colors.red(" [failed]"); + // if (module.warnings) { + // colors.yellow( + // ` [${module.warnings} warning${module.warnings === 1 ? "" : "s"}]` + // ); + // } + // if (module.errors) { + // colors.red( + // ` [${module.errors} error${module.errors === 1 ? "" : "s"}]` + // ); + // } + // }; + + // const processModuleContent = ( + // module: { + // providedExports: any[]; + // usedExports: boolean | any[] | null | undefined; + // optimizationBailout: any; + // reasons: any; + // profile: { [x: string]: any }; + // issuerPath: any; + // modules: any; + // }, + // prefix: string + // ) => { + // if (Array.isArray(module.providedExports)) { + // colors.normal(prefix); + // if (module.providedExports.length === 0) { + // colors.cyan("[no exports]"); + // } else { + // colors.cyan(`[exports: ${module.providedExports.join(", ")}]`); + // } + // newline(); + // } + // if (module.usedExports !== undefined) { + // if (module.usedExports !== true) { + // colors.normal(prefix); + // if (module.usedExports === null) { + // colors.cyan("[used exports unknown]"); + // } else if (module.usedExports === false) { + // colors.cyan("[no exports used]"); + // } else if ( + // Array.isArray(module.usedExports) && + // module.usedExports.length === 0 + // ) { + // colors.cyan("[no exports used]"); + // } else if (Array.isArray(module.usedExports)) { + // const providedExportsCount = Array.isArray(module.providedExports) + // ? module.providedExports.length + // : null; + // if ( + // providedExportsCount !== null && + // providedExportsCount === module.usedExports.length + // ) { + // colors.cyan("[all exports used]"); + // } else { + // colors.cyan( + // `[only some exports used: ${module.usedExports.join(", ")}]` + // ); + // } + // } + // newline(); + // } + // } + // if (Array.isArray(module.optimizationBailout)) { + // for (const item of module.optimizationBailout) { + // colors.normal(prefix); + // colors.yellow(item); + // newline(); + // } + // } + // if (module.reasons) { + // for (const reason of module.reasons) { + // colors.normal(prefix); + // if (reason.type) { + // colors.normal(reason.type); + // colors.normal(" "); + // } + // if (reason.userRequest) { + // colors.cyan(reason.userRequest); + // colors.normal(" "); + // } + // if (reason.moduleId) { + // colors.normal("["); + // colors.normal(reason.moduleId); + // colors.normal("]"); + // } + // if (reason.module && reason.module !== reason.moduleId) { + // colors.normal(" "); + // colors.magenta(reason.module); + // } + // if (reason.loc) { + // colors.normal(" "); + // colors.normal(reason.loc); + // } + // if (reason.explanation) { + // colors.normal(" "); + // colors.cyan(reason.explanation); + // } + // newline(); + // } + // } + // if (module.profile) { + // colors.normal(prefix); + // let sum = 0; + // if (module.issuerPath) { + // for (const m of module.issuerPath) { + // colors.normal("["); + // colors.normal(m.id); + // colors.normal("] "); + // if (m.profile) { + // const time = (m.profile.factory || 0) + (m.profile.building || 0); + // coloredTime(time); + // sum += time; + // colors.normal(" "); + // } + // colors.normal("-> "); + // } + // } + // for (const key of Object.keys(module.profile)) { + // colors.normal(`${key}:`); + // const time = module.profile[key]; + // coloredTime(time); + // colors.normal(" "); + // sum += time; + // } + // colors.normal("= "); + // coloredTime(sum); + // newline(); + // } + // if (module.modules) { + // // @ts-expect-error + // processModulesList(module, prefix + "| "); + // } + // }; + + // const processModulesList = ( + // obj: { modules: string | any[]; filteredModules: number }, + // prefix: string + // ) => { + // if (obj.modules) { + // let maxModuleId = 0; + // for (const module of obj.modules) { + // if (typeof module.id === "number") { + // if (maxModuleId < module.id) maxModuleId = module.id; + // } + // } + // let contentPrefix = prefix + " "; + // if (maxModuleId >= 10) contentPrefix += " "; + // if (maxModuleId >= 100) contentPrefix += " "; + // if (maxModuleId >= 1000) contentPrefix += " "; + // for (const module of obj.modules) { + // colors.normal(prefix); + // const name = module.name || module.identifier; + // if (typeof module.id === "string" || typeof module.id === "number") { + // if (typeof module.id === "number") { + // if (module.id < 1000 && maxModuleId >= 1000) colors.normal(" "); + // if (module.id < 100 && maxModuleId >= 100) colors.normal(" "); + // if (module.id < 10 && maxModuleId >= 10) colors.normal(" "); + // } else { + // if (maxModuleId >= 1000) colors.normal(" "); + // if (maxModuleId >= 100) colors.normal(" "); + // if (maxModuleId >= 10) colors.normal(" "); + // } + // if (name !== module.id) { + // colors.normal("["); + // colors.normal(module.id); + // colors.normal("]"); + // colors.normal(" "); + // } else { + // colors.normal("["); + // colors.bold(module.id); + // colors.normal("]"); + // } + // } + // if (name !== module.id) { + // colors.bold(name); + // } + // processModuleAttributes(module); + // newline(); + // processModuleContent(module, contentPrefix); + // } + // if (obj.filteredModules > 0) { + // colors.normal(prefix); + // colors.normal(" "); + // if (obj.modules.length > 0) colors.normal(" + "); + // colors.normal(obj.filteredModules); + // if (obj.modules.length > 0) colors.normal(" hidden"); + // colors.normal(obj.filteredModules !== 1 ? " modules" : " module"); + // newline(); + // } + // } + // }; + + // if (obj.chunks) { + // for (const chunk of obj.chunks) { + // colors.normal("chunk "); + // if (chunk.id < 1000) colors.normal(" "); + // if (chunk.id < 100) colors.normal(" "); + // if (chunk.id < 10) colors.normal(" "); + // colors.normal("{"); + // colors.yellow(chunk.id); + // colors.normal("} "); + // colors.green(chunk.files.join(", ")); + // if (chunk.names && chunk.names.length > 0) { + // colors.normal(" ("); + // colors.normal(chunk.names.join(", ")); + // colors.normal(")"); + // } + // colors.normal(" "); + // colors.normal(SizeFormatHelpers.formatSize(chunk.size)); + // // TODO: stats chunk relation + // // for (const id of chunk.parents) { + // // colors.normal(" <{"); + // // colors.yellow(id); + // // colors.normal("}>"); + // // } + // // for (const id of chunk.siblings) { + // // colors.normal(" ={"); + // // colors.yellow(id); + // // colors.normal("}="); + // // } + // // for (const id of chunk.children) { + // // colors.normal(" >{"); + // // colors.yellow(id); + // // colors.normal("}<"); + // // } + // if (chunk.childrenByOrder) { + // for (const name of Object.keys(chunk.childrenByOrder)) { + // const children = chunk.childrenByOrder[name]; + // colors.normal(" "); + // colors.magenta(`(${name}:`); + // for (const id of children) { + // colors.normal(" {"); + // colors.yellow(id); + // colors.normal("}"); + // } + // colors.magenta(")"); + // } + // } + // if (chunk.entry) { + // colors.yellow(" [entry]"); + // } else if (chunk.initial) { + // colors.yellow(" [initial]"); + // } + // if (chunk.rendered) { + // colors.green(" [rendered]"); + // } + // if (chunk.recorded) { + // colors.green(" [recorded]"); + // } + // if (chunk.reason) { + // colors.yellow(` ${chunk.reason}`); + // } + // newline(); + // if (chunk.origins) { + // for (const origin of chunk.origins) { + // colors.normal(" > "); + // if (origin.reasons && origin.reasons.length) { + // colors.yellow(origin.reasons.join(" ")); + // colors.normal(" "); + // } + // if (origin.request) { + // colors.normal(origin.request); + // colors.normal(" "); + // } + // if (origin.module) { + // colors.normal("["); + // colors.normal(origin.moduleId); + // colors.normal("] "); + // // @ts-expect-error + // const module = modulesByIdentifier[`$${origin.module}`]; + // if (module) { + // colors.bold(module.name); + // colors.normal(" "); + // } + // } + // if (origin.loc) { + // colors.normal(origin.loc); + // } + // newline(); + // } + // } + // processModulesList(chunk, " "); + // } + // } + + // processModulesList(obj, ""); + + // if (obj.logging) { + // for (const origin of Object.keys(obj.logging)) { + // const logData = obj.logging[origin]; + // if (logData.entries.length > 0) { + // newline(); + // if (logData.debug) { + // colors.red("DEBUG "); + // } + // colors.bold("LOG from " + origin); + // newline(); + // let indent = ""; + // for (const entry of logData.entries) { + // let color = colors.normal; + // let prefix = " "; + // switch (entry.type) { + // case LogType.clear: + // colors.normal(`${indent}-------`); + // newline(); + // continue; + // case LogType.error: + // color = colors.red; + // prefix = " "; + // break; + // case LogType.warn: + // color = colors.yellow; + // prefix = " "; + // break; + // case LogType.info: + // color = colors.green; + // prefix = " "; + // break; + // case LogType.log: + // color = colors.bold; + // break; + // case LogType.trace: + // case LogType.debug: + // color = colors.normal; + // break; + // case LogType.status: + // color = colors.magenta; + // prefix = " "; + // break; + // case LogType.profile: + // color = colors.magenta; + // prefix = "

"; + // break; + // case LogType.profileEnd: + // color = colors.magenta; + // prefix = "

"; + // break; + // case LogType.time: + // color = colors.magenta; + // prefix = " "; + // break; + // case LogType.group: + // color = colors.cyan; + // prefix = "<-> "; + // break; + // case LogType.groupCollapsed: + // color = colors.cyan; + // prefix = "<+> "; + // break; + // case LogType.groupEnd: + // if (indent.length >= 2) + // indent = indent.slice(0, indent.length - 2); + // continue; + // } + // if (entry.message) { + // for (const line of entry.message.split("\n")) { + // colors.normal(`${indent}${prefix}`); + // color(line); + // newline(); + // } + // } + // if (entry.trace) { + // for (const line of entry.trace) { + // colors.normal(`${indent}| ${line}`); + // newline(); + // } + // } + // switch (entry.type) { + // case LogType.group: + // indent += " "; + // break; + // } + // } + // if (logData.filteredEntries) { + // colors.normal(`+ ${logData.filteredEntries} hidden lines`); + // newline(); + // } + // } + // } + // } + + // if (obj.warnings) { + // for (const warning of obj.warnings) { + // newline(); + // // formatted warning already have color. + // colors.normal(formatError(warning)); + // newline(); + // } + // } + // if (obj.errors) { + // for (const error of obj.errors) { + // newline(); + // // formatted error already have color. + // colors.normal(formatError(error)); + // newline(); + // } + // } + // if (obj.children) { + // for (const child of obj.children) { + // const childString = this.jsonToString(child, useColors); + // if (childString) { + // if (child.name) { + // colors.normal("Child "); + // colors.bold(child.name); + // colors.normal(":"); + // } else { + // colors.normal("Child"); + // } + // newline(); + // buf.push(" "); + // buf.push(childString.replace(/\n/g, "\n ")); + // newline(); + // } + // } + // } + // if (obj.needAdditionalPass) { + // colors.yellow( + // "Compilation needs an additional pass and will compile again." + // ); + // } + + // while (buf[buf.length - 1] === "\n") { + // buf.pop(); + // } + // return buf.join(""); + // } +} +module.exports = StatsPrinter;