mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			995 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			995 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| /*
 | |
| 	MIT License http://www.opensource.org/licenses/mit-license.php
 | |
| 	Author Tobias Koppers @sokra
 | |
| */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| /** @typedef {import("../Compiler")} Compiler */
 | |
| /** @typedef {import("./StatsPrinter")} StatsPrinter */
 | |
| 
 | |
| /**
 | |
|  * @typedef {Object} UsualContext
 | |
|  * @property {string} type
 | |
|  * @property {Object} compilation
 | |
|  * @property {Object} asset
 | |
|  * @property {Object} module
 | |
|  * @property {Object} chunk
 | |
|  * @property {Object} moduleReason
 | |
|  * @property {(str: string) => string} bold
 | |
|  * @property {(str: string) => string} yellow
 | |
|  * @property {(str: string) => string} red
 | |
|  * @property {(str: string) => string} green
 | |
|  * @property {(str: string) => string} magenta
 | |
|  * @property {(str: string) => string} cyan
 | |
|  * @property {(file: string, oversize?: boolean) => string} formatFilename
 | |
|  * @property {(id: string) => string} formatModuleId
 | |
|  * @property {(id: string, direction?: "parent"|"child"|"sibling") => string} formatChunkId
 | |
|  * @property {(size: number) => string} formatSize
 | |
|  * @property {(flag: string) => string} formatFlag
 | |
|  * @property {(time: number) => string} formatTime
 | |
|  * @property {number} maxModuleId
 | |
|  * @property {string} chunkGroupKind
 | |
|  */
 | |
| 
 | |
| const plural = (n, singular, plural) => (n === 1 ? singular : plural);
 | |
| 
 | |
| const printSizes = (sizes, { formatSize }) => {
 | |
| 	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 isValidId = id => {
 | |
| 	return typeof id === "number" || id;
 | |
| };
 | |
| 
 | |
| /** @type {Record<string, (thing: any, context: UsualContext, printer: StatsPrinter) => string | void>} */
 | |
| const SIMPLE_PRINTERS = {
 | |
| 	"compilation.hash": (hash, { bold, type }) =>
 | |
| 		type === "compilation.hash" ? `Hash: ${bold(hash)}` : undefined,
 | |
| 	"compilation.version": (version, { bold, type }) =>
 | |
| 		type === "compilation.version"
 | |
| 			? `Version: webpack ${bold(version)}`
 | |
| 			: undefined,
 | |
| 	"compilation.time": (time, { bold }) => `Time: ${bold(time)}ms`,
 | |
| 	"compilation.builtAt": (builtAt, { bold }) => {
 | |
| 		const builtAtDate = new Date(builtAt);
 | |
| 		return `Built at: ${builtAtDate.toLocaleDateString(undefined, {
 | |
| 			day: "2-digit",
 | |
| 			month: "2-digit",
 | |
| 			year: "numeric"
 | |
| 		})} ${bold(builtAtDate.toLocaleTimeString())}`;
 | |
| 	},
 | |
| 	"compilation.env": (env, { bold }) =>
 | |
| 		env
 | |
| 			? `Environment (--env): ${bold(JSON.stringify(env, null, 2))}`
 | |
| 			: undefined,
 | |
| 	"compilation.publicPath": (publicPath, { bold }) =>
 | |
| 		`PublicPath: ${bold(publicPath || "(none)")}`,
 | |
| 	"compilation.entrypoints": (entrypoints, context, printer) =>
 | |
| 		Array.isArray(entrypoints)
 | |
| 			? undefined
 | |
| 			: printer.print(
 | |
| 					context.type,
 | |
| 					Object.values(entrypoints),
 | |
| 					Object.assign({}, context, { chunkGroupKind: "Entrypoint" })
 | |
| 			  ),
 | |
| 	"compilation.namedChunkGroups": (namedChunkGroups, context, printer) =>
 | |
| 		Array.isArray(namedChunkGroups)
 | |
| 			? undefined
 | |
| 			: printer.print(
 | |
| 					context.type,
 | |
| 					Object.values(namedChunkGroups),
 | |
| 					Object.assign({}, context, { chunkGroupKind: "Chunk Group" })
 | |
| 			  ),
 | |
| 	"compilation.assetsByChunkName": () => "",
 | |
| 
 | |
| 	"compilation.modules": (modules, context) => {
 | |
| 		let maxModuleId = 0;
 | |
| 		for (const module of modules) {
 | |
| 			if (typeof module.id === "number") {
 | |
| 				if (maxModuleId < module.id) maxModuleId = module.id;
 | |
| 			}
 | |
| 		}
 | |
| 		context.maxModuleId = maxModuleId;
 | |
| 	},
 | |
| 	"compilation.filteredModules": (
 | |
| 		filteredModules,
 | |
| 		{ compilation: { modules } }
 | |
| 	) =>
 | |
| 		filteredModules > 0
 | |
| 			? `   ${
 | |
| 					modules && modules.length > 0
 | |
| 						? ` + ${filteredModules} hidden`
 | |
| 						: filteredModules
 | |
| 			  } ${plural(filteredModules, "module", "modules")}`
 | |
| 			: undefined,
 | |
| 	"compilation.filteredAssets": (filteredAssets, { compilation: { assets } }) =>
 | |
| 		filteredAssets > 0
 | |
| 			? `${
 | |
| 					assets && assets.length > 0
 | |
| 						? ` + ${filteredAssets} hidden`
 | |
| 						: filteredAssets
 | |
| 			  } ${plural(filteredAssets, "asset", "assets")}`
 | |
| 			: undefined,
 | |
| 	"compilation.children[].compilation.name": name =>
 | |
| 		name ? `Child ${name}:` : "Child",
 | |
| 
 | |
| 	"asset.name": (name, { formatFilename, asset: { isOverSizeLimit } }) =>
 | |
| 		formatFilename(name, isOverSizeLimit),
 | |
| 	"asset.size": (
 | |
| 		size,
 | |
| 		{ asset: { isOverSizeLimit }, yellow, green, formatSize }
 | |
| 	) => (isOverSizeLimit ? yellow(formatSize(size)) : formatSize(size)),
 | |
| 	"asset.emitted": (emitted, { green, formatFlag }) =>
 | |
| 		emitted ? green(formatFlag("emitted")) : undefined,
 | |
| 	"asset.isOverSizeLimit": (isOverSizeLimit, { yellow, formatFlag }) =>
 | |
| 		isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
 | |
| 
 | |
| 	assetChunk: (id, { formatChunkId }) => formatChunkId(id),
 | |
| 
 | |
| 	assetChunkName: name => name,
 | |
| 
 | |
| 	"module.id": (id, { formatModuleId }) =>
 | |
| 		isValidId(id) ? formatModuleId(id) : undefined,
 | |
| 	"module.name": (name, { bold }) => bold(name),
 | |
| 	"module.identifier": identifier => identifier,
 | |
| 	"module.sizes": printSizes,
 | |
| 	"module.chunks[]": (id, { formatChunkId }) => formatChunkId(id),
 | |
| 	"module.depth": (depth, { formatFlag }) =>
 | |
| 		depth !== null ? 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.built": (built, { formatFlag, green }) =>
 | |
| 		built ? green(formatFlag("built")) : undefined,
 | |
| 	"module.assets": (assets, { formatFlag, magenta }) =>
 | |
| 		assets && assets.length
 | |
| 			? magenta(
 | |
| 					formatFlag(
 | |
| 						`${assets.length} ${plural(assets.length, "asset", "assets")}`
 | |
| 					)
 | |
| 			  )
 | |
| 			: undefined,
 | |
| 	"module.failed": (failed, { formatFlag, red }) =>
 | |
| 		failed ? red(formatFlag("failed")) : 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 === module.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.modules": (modules, context) => {
 | |
| 		let maxModuleId = 0;
 | |
| 		for (const module of modules) {
 | |
| 			if (typeof module.id === "number") {
 | |
| 				if (maxModuleId < module.id) maxModuleId = module.id;
 | |
| 			}
 | |
| 		}
 | |
| 		context.maxModuleId = maxModuleId;
 | |
| 	},
 | |
| 	"module.filteredModules": (filteredModules, { compilation: { modules } }) =>
 | |
| 		filteredModules > 0
 | |
| 			? `   ${
 | |
| 					modules && modules.length > 0
 | |
| 						? ` + ${filteredModules} hidden`
 | |
| 						: filteredModules
 | |
| 			  } nested ${plural(filteredModules, "module", "modules")}`
 | |
| 			: undefined,
 | |
| 	"module.separator!": () => "\n",
 | |
| 
 | |
| 	"moduleIssuer.id": (id, { formatModuleId }) => formatModuleId(id),
 | |
| 	"moduleIssuer.profile.total": (value, { formatTime }) => formatTime(value),
 | |
| 
 | |
| 	"moduleReason.type": type => type,
 | |
| 	"moduleReason.userRequest": (userRequest, { cyan }) => cyan(userRequest),
 | |
| 	"moduleReason.moduleId": (moduleId, { formatModuleId }) =>
 | |
| 		moduleId !== null ? formatModuleId(moduleId) : undefined,
 | |
| 	"moduleReason.module": (module, { magenta }) => magenta(module),
 | |
| 	"moduleReason.loc": loc => loc,
 | |
| 	"moduleReason.explanation": (explanation, { cyan }) => cyan(explanation),
 | |
| 
 | |
| 	"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.name": (name, { bold }) => bold(name),
 | |
| 	"chunkGroup.isOverSizeLimit": (isOverSizeLimit, { formatFlag, yellow }) =>
 | |
| 		isOverSizeLimit ? yellow(formatFlag("big")) : undefined,
 | |
| 	"chunkGroup.separator!": () => "=",
 | |
| 	"chunkGroup.assets[]": (asset, { green }) => green(asset),
 | |
| 	"chunkGroup.childAssets": (childAssets, context, printer) =>
 | |
| 		Array.isArray(childAssets)
 | |
| 			? undefined
 | |
| 			: printer.print(
 | |
| 					context.type,
 | |
| 					Object.keys(childAssets).map(key => ({
 | |
| 						type: key,
 | |
| 						children: childAssets[key]
 | |
| 					})),
 | |
| 					context
 | |
| 			  ),
 | |
| 	"chunkGroup.childAssets[].type": type => `${type}:`,
 | |
| 	"chunkGroup.childAssets[].children[]": (file, { formatFilename }) =>
 | |
| 		formatFilename(file),
 | |
| 
 | |
| 	"chunk.id": (id, { formatChunkId }) => formatChunkId(id),
 | |
| 	"chunk.files[]": (file, { formatFilename }) => formatFilename(file),
 | |
| 	"chunk.names[]": 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 }) => yellow(reason),
 | |
| 	"chunk.rootModules": (modules, context) => {
 | |
| 		let maxModuleId = 0;
 | |
| 		for (const module of modules) {
 | |
| 			if (typeof module.id === "number") {
 | |
| 				if (maxModuleId < module.id) maxModuleId = module.id;
 | |
| 			}
 | |
| 		}
 | |
| 		context.maxModuleId = maxModuleId;
 | |
| 	},
 | |
| 	"chunk.filteredRootModules": (
 | |
| 		filteredRootModules,
 | |
| 		{ chunk: { rootModules } }
 | |
| 	) =>
 | |
| 		filteredRootModules > 0
 | |
| 			? `   ${
 | |
| 					rootModules && rootModules.length > 0
 | |
| 						? ` + ${filteredRootModules} hidden`
 | |
| 						: filteredRootModules
 | |
| 			  } root ${plural(filteredRootModules, "module", "modules")}`
 | |
| 			: undefined,
 | |
| 	"chunk.nonRootModules": (
 | |
| 		nonRootModules,
 | |
| 		{ chunk: { filteredRootModules, rootModules } }
 | |
| 	) =>
 | |
| 		nonRootModules > 0
 | |
| 			? `   ${
 | |
| 					(rootModules && rootModules.length > 0) || filteredRootModules > 0
 | |
| 						? ` + ${nonRootModules} hidden`
 | |
| 						: nonRootModules
 | |
| 			  } dependent ${plural(nonRootModules, "module", "modules")}`
 | |
| 			: undefined,
 | |
| 	"chunk.modules": (modules, context) => {
 | |
| 		let maxModuleId = 0;
 | |
| 		for (const module of modules) {
 | |
| 			if (typeof module.id === "number") {
 | |
| 				if (maxModuleId < module.id) maxModuleId = module.id;
 | |
| 			}
 | |
| 		}
 | |
| 		context.maxModuleId = maxModuleId;
 | |
| 	},
 | |
| 	"chunk.filteredModules": (filteredModules, { chunk: { modules } }) =>
 | |
| 		filteredModules > 0
 | |
| 			? `   ${
 | |
| 					modules && modules.length > 0
 | |
| 						? ` + ${filteredModules} hidden`
 | |
| 						: filteredModules
 | |
| 			  } chunk ${plural(filteredModules, "module", "modules")}`
 | |
| 			: undefined,
 | |
| 	"chunk.separator!": () => "\n",
 | |
| 
 | |
| 	"chunkOrigin.request": request => request,
 | |
| 	"chunkOrigin.moduleId": (moduleId, { formatModuleId }) =>
 | |
| 		moduleId !== null && moduleId !== undefined
 | |
| 			? formatModuleId(moduleId)
 | |
| 			: undefined,
 | |
| 	"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 }) => bold(message),
 | |
| 	"error.details": details => details,
 | |
| 	"error.stack": stack => stack,
 | |
| 	"error.missing[]": missing => `[${missing}]`,
 | |
| 	"error.moduleTrace": moduleTrace => undefined,
 | |
| 	"error.separator!": () => "\n",
 | |
| 
 | |
| 	"moduleTraceItem.originName": originName => originName,
 | |
| 
 | |
| 	"moduleTraceDependency.loc": loc => loc
 | |
| };
 | |
| 
 | |
| /** @type {Record<string, string>} */
 | |
| 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.children[]": "compilation",
 | |
| 	"asset.chunks[]": "assetChunk",
 | |
| 	"asset.chunkNames[]": "assetChunkName",
 | |
| 	"module.modules[]": "module",
 | |
| 	"module.reasons[]": "moduleReason",
 | |
| 	"module.issuerPath[]": "moduleIssuer",
 | |
| 	"chunk.origins[]": "chunkOrigin",
 | |
| 	"chunk.rootModules[]": "module",
 | |
| 	"chunk.modules[]": "module",
 | |
| 	"error.moduleTrace[]": "moduleTraceItem",
 | |
| 	"moduleTraceItem.dependencies[]": "moduleTraceDependency"
 | |
| };
 | |
| 
 | |
| const ERROR_PREFERED_ORDER = [
 | |
| 	"compilerPath",
 | |
| 	"chunkId",
 | |
| 	"chunkEntry",
 | |
| 	"chunkInitial",
 | |
| 	"file",
 | |
| 	"separator!",
 | |
| 	"moduleName",
 | |
| 	"loc",
 | |
| 	"separator!",
 | |
| 	"message",
 | |
| 	"details",
 | |
| 	"stack",
 | |
| 	"separator!",
 | |
| 	"missing",
 | |
| 	"separator!",
 | |
| 	"moduleTrace"
 | |
| ];
 | |
| 
 | |
| /** @type {Record<string, string[]>} */
 | |
| const PREFERED_ORDERS = {
 | |
| 	compilation: [
 | |
| 		"name",
 | |
| 		"hash",
 | |
| 		"version",
 | |
| 		"time",
 | |
| 		"builtAt",
 | |
| 		"env",
 | |
| 		"publicPath",
 | |
| 		"assets",
 | |
| 		"filteredAssets",
 | |
| 		"entrypoints",
 | |
| 		"namedChunkGroups",
 | |
| 		"chunks",
 | |
| 		"modules",
 | |
| 		"filteredModules",
 | |
| 		"warnings",
 | |
| 		"errors",
 | |
| 		"children",
 | |
| 		"needAdditionalPass"
 | |
| 	],
 | |
| 	asset: ["name", "size", "chunks", "emitted", "isOverSizeLimit", "chunkNames"],
 | |
| 	chunkGroup: [
 | |
| 		"kind!",
 | |
| 		"name",
 | |
| 		"isOverSizeLimit",
 | |
| 		"separator!",
 | |
| 		"assets",
 | |
| 		"childAssets"
 | |
| 	],
 | |
| 	module: [
 | |
| 		"id",
 | |
| 		"name",
 | |
| 		"identifier",
 | |
| 		"sizes",
 | |
| 		"chunks",
 | |
| 		"depth",
 | |
| 		"cacheable",
 | |
| 		"orphan",
 | |
| 		"runtime",
 | |
| 		"optional",
 | |
| 		"built",
 | |
| 		"assets",
 | |
| 		"failed",
 | |
| 		"warnings",
 | |
| 		"errors",
 | |
| 		"separator!",
 | |
| 		"providedExports",
 | |
| 		"separator!",
 | |
| 		"usedExports",
 | |
| 		"separator!",
 | |
| 		"optimizationBailout",
 | |
| 		"separator!",
 | |
| 		"reasons",
 | |
| 		"separator!",
 | |
| 		"issuerPath",
 | |
| 		"profile",
 | |
| 		"separator!",
 | |
| 		"modules"
 | |
| 	],
 | |
| 	moduleReason: [
 | |
| 		"type",
 | |
| 		"userRequest",
 | |
| 		"moduleId",
 | |
| 		"module",
 | |
| 		"loc",
 | |
| 		"explanation"
 | |
| 	],
 | |
| 	"module.profile": [
 | |
| 		"total",
 | |
| 		"separator!",
 | |
| 		"resolving",
 | |
| 		"restoring",
 | |
| 		"integration",
 | |
| 		"building",
 | |
| 		"storing",
 | |
| 		"additionalResolving",
 | |
| 		"additionalIntegration"
 | |
| 	],
 | |
| 	chunk: [
 | |
| 		"id",
 | |
| 		"files",
 | |
| 		"names",
 | |
| 		"sizes",
 | |
| 		"parents",
 | |
| 		"siblings",
 | |
| 		"children",
 | |
| 		"childrenByOrder",
 | |
| 		"entry",
 | |
| 		"initial",
 | |
| 		"rendered",
 | |
| 		"recorded",
 | |
| 		"reason",
 | |
| 		"separator!",
 | |
| 		"origins",
 | |
| 		"separator!",
 | |
| 		"rootModules",
 | |
| 		"separator!",
 | |
| 		"filteredRootModules",
 | |
| 		"separator!",
 | |
| 		"nonRootModules",
 | |
| 		"separator!",
 | |
| 		"modules",
 | |
| 		"separator!",
 | |
| 		"filteredModules"
 | |
| 	],
 | |
| 	chunkOrigin: ["request", "moduleId", "moduleName", "loc"],
 | |
| 	error: ERROR_PREFERED_ORDER,
 | |
| 	warning: ERROR_PREFERED_ORDER,
 | |
| 	"chunk.childrenByOrder[]": ["type", "children"],
 | |
| 	"chunkGroup.childAssets[]": ["type", "children"]
 | |
| };
 | |
| 
 | |
| const itemsJoinOneLine = items => items.filter(Boolean).join(" ");
 | |
| 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;
 | |
| 
 | |
| /** @type {Record<string, (items: string[]) => string>} */
 | |
| const SIMPLE_ITEMS_JOINER = {
 | |
| 	"chunk.parents": itemsJoinOneLine,
 | |
| 	"chunk.siblings": itemsJoinOneLine,
 | |
| 	"chunk.children": itemsJoinOneLine,
 | |
| 	"chunk.names": itemsJoinCommaBrackets,
 | |
| 	"chunk.files": itemsJoinComma,
 | |
| 	"chunk.childrenByOrder": itemsJoinOneLine,
 | |
| 	"chunk.childrenByOrder[].children": itemsJoinOneLine,
 | |
| 	"chunkGroup.assets": itemsJoinOneLine,
 | |
| 	"chunkGroup.childAssets": itemsJoinOneLine,
 | |
| 	"chunkGroup.childAssets[].children": itemsJoinOneLine,
 | |
| 	"asset.chunks": itemsJoinComma,
 | |
| 	"asset.chunkNames": itemsJoinComma,
 | |
| 	"module.chunks": itemsJoinOneLine,
 | |
| 	"module.issuerPath": items =>
 | |
| 		items
 | |
| 			.filter(Boolean)
 | |
| 			.map(item => `${item} ->`)
 | |
| 			.join(" "),
 | |
| 	"compilation.errors": itemsJoinMoreSpacing,
 | |
| 	"compilation.warnings": itemsJoinMoreSpacing,
 | |
| 	"moduleTraceItem.dependencies": itemsJoinOneLine,
 | |
| 	"compilation.children": items =>
 | |
| 		items.map(item => indent(item, "    ", true)).join("\n")
 | |
| };
 | |
| 
 | |
| 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;
 | |
| 	return items
 | |
| 		.map(item => {
 | |
| 			if (!item.content) return;
 | |
| 			let content = indent(item.content, indenter, !firstInLine);
 | |
| 			if (firstInLine) {
 | |
| 				content = content.replace(/^\n+/, "");
 | |
| 			}
 | |
| 			if (!content) return;
 | |
| 			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, (items: ({ element: string, content: string })[], context: UsualContext) => 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 === "errors";
 | |
| 			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("");
 | |
| 	},
 | |
| 	module: (items, { module, maxModuleId }) => {
 | |
| 		let hasName = false;
 | |
| 		let indenter = "    ";
 | |
| 		if (maxModuleId >= 10) indenter += " ";
 | |
| 		if (maxModuleId >= 100) indenter += " ";
 | |
| 		if (maxModuleId >= 1000) indenter += " ";
 | |
| 		let prefix = "";
 | |
| 		if (typeof module.id === "number") {
 | |
| 			if (module.id < 1000 && maxModuleId >= 1000) prefix += " ";
 | |
| 			if (module.id < 100 && maxModuleId >= 100) prefix += " ";
 | |
| 			if (module.id < 10 && maxModuleId >= 10) prefix += " ";
 | |
| 		} else if (typeof module.id === "string" && module.id !== "") {
 | |
| 			if (maxModuleId >= 1000) prefix += " ";
 | |
| 			if (maxModuleId >= 100) prefix += " ";
 | |
| 			if (maxModuleId >= 10) prefix += " ";
 | |
| 		}
 | |
| 		return (
 | |
| 			prefix +
 | |
| 			joinExplicitNewLine(
 | |
| 				items.filter(item => {
 | |
| 					switch (item.element) {
 | |
| 						case "id":
 | |
| 							if (module.id === module.name && item.content) hasName = true;
 | |
| 							break;
 | |
| 						case "name":
 | |
| 							if (hasName) return false;
 | |
| 							if (item.content) hasName = true;
 | |
| 							break;
 | |
| 						case "identifier":
 | |
| 							if (hasName) return false;
 | |
| 							if (item.content) hasName = true;
 | |
| 							break;
 | |
| 					}
 | |
| 					return true;
 | |
| 				}),
 | |
| 				indenter
 | |
| 			)
 | |
| 		);
 | |
| 	},
 | |
| 	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: joinOneLine,
 | |
| 	"chunkGroup.childAssets[]": items => `(${joinOneLine(items)})`,
 | |
| 	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;
 | |
| 				}
 | |
| 				return true;
 | |
| 			})
 | |
| 		);
 | |
| 	},
 | |
| 	"module.profile": joinInBrackets,
 | |
| 	moduleIssuer: joinOneLine,
 | |
| 	chunkOrigin: items => "   > " + joinOneLine(items),
 | |
| 	"errors[].error": joinError(true),
 | |
| 	"warnings[].error": joinError(false),
 | |
| 	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}]`,
 | |
| 	formatSize: require("../SizeFormatHelpers").formatSize,
 | |
| 	formatTime: (time, { timeReference, bold, green, yellow, red }) => {
 | |
| 		if (timeReference && time !== timeReference) {
 | |
| 			const times = [
 | |
| 				timeReference / 2,
 | |
| 				timeReference / 4,
 | |
| 				timeReference / 8,
 | |
| 				timeReference / 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`);
 | |
| 		} else {
 | |
| 			return `${time}ms`;
 | |
| 		}
 | |
| 	}
 | |
| };
 | |
| 
 | |
| const RESULT_MODIFIER = {
 | |
| 	"module.modules": result => {
 | |
| 		return indent(result, "| ");
 | |
| 	}
 | |
| };
 | |
| 
 | |
| const createOrder = (array, preferedOrder) => {
 | |
| 	const originalArray = array.slice();
 | |
| 	const set = new Set(array);
 | |
| 	const usedSet = new Set();
 | |
| 	array.length = 0;
 | |
| 	for (const element of preferedOrder) {
 | |
| 		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;
 | |
| };
 | |
| 
 | |
| const filterColors = value => value.replace(/\u001b\[\d+m/g, "");
 | |
| 
 | |
| const table = (array, align, splitter) => {
 | |
| 	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++) {
 | |
| 			const value = filterColors(`${array[row][col]}`);
 | |
| 			if (value.length > colSizes[col]) {
 | |
| 				colSizes[col] = value.length;
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	const lines = [];
 | |
| 	for (let row = 0; row < rows; row++) {
 | |
| 		let str = "";
 | |
| 		for (let col = 0; col < cols; col++) {
 | |
| 			const value = `${array[row][col]}`;
 | |
| 			let l = filterColors(value).length;
 | |
| 			if (align[col] === "l") {
 | |
| 				str += value;
 | |
| 			}
 | |
| 			for (; l < colSizes[col] && col !== cols - 1; l++) {
 | |
| 				str += " ";
 | |
| 			}
 | |
| 			if (align[col] === "r") {
 | |
| 				str += value;
 | |
| 			}
 | |
| 			if (col + 1 < cols && colSizes[col] !== 0) {
 | |
| 				str += splitter || "  ";
 | |
| 			}
 | |
| 		}
 | |
| 		lines.push(str.trimRight());
 | |
| 	}
 | |
| 	return lines.join("\n");
 | |
| };
 | |
| 
 | |
| class DefaultStatsPrinterPlugin {
 | |
| 	/**
 | |
| 	 * @param {Compiler} compiler webpack compiler
 | |
| 	 * @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}${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;
 | |
| 						});
 | |
| 
 | |
| 					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(PREFERED_ORDERS)) {
 | |
| 						const preferedOrder = PREFERED_ORDERS[key];
 | |
| 						stats.hooks.sortElements
 | |
| 							.for(key)
 | |
| 							.tap("DefaultStatsPrinterPlugin", (elements, context) => {
 | |
| 								createOrder(elements, preferedOrder);
 | |
| 							});
 | |
| 					}
 | |
| 
 | |
| 					for (const key of Object.keys(ITEM_NAMES)) {
 | |
| 						const itemName = ITEM_NAMES[key];
 | |
| 						stats.hooks.getItemName
 | |
| 							.for(key)
 | |
| 							.tap("DefaultStatsPrinterPlugin", () => 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);
 | |
| 					}
 | |
| 
 | |
| 					// Print assets as table
 | |
| 					stats.hooks.printElements
 | |
| 						.for("compilation.assets[].asset")
 | |
| 						.tap("DefaultStatsPrinterPlugin", (elements, { bold }) => {
 | |
| 							const elementsMap = elements.reduce(
 | |
| 								(obj, e) => ((obj[e.element] = e.content), obj),
 | |
| 								Object.create(null)
 | |
| 							);
 | |
| 							return [
 | |
| 								elementsMap.name || "",
 | |
| 								elementsMap.size || "",
 | |
| 								elementsMap.chunks || "",
 | |
| 								elementsMap.emitted || "",
 | |
| 								elementsMap.isOverSizeLimit || "",
 | |
| 								elementsMap.chunkNames || ""
 | |
| 							];
 | |
| 						});
 | |
| 					stats.hooks.printItems
 | |
| 						.for("compilation.assets")
 | |
| 						.tap("DefaultStatsPrinterPlugin", (items, { bold }) => {
 | |
| 							if (items.length === 0) return undefined;
 | |
| 							let header = ["Asset", "Size", "Chunks", "", "", "Chunk Names"];
 | |
| 							header = header.map(h => (h ? bold(h) : h));
 | |
| 							return table([header].concat(items), "rrrlll");
 | |
| 						});
 | |
| 				}
 | |
| 			);
 | |
| 		});
 | |
| 	}
 | |
| }
 | |
| module.exports = DefaultStatsPrinterPlugin;
 |