| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const { HookMap, SyncWaterfallHook, SyncBailHook } = require("tapable"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | /** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> */ | 
					
						
							|  |  |  | /** @typedef {import("tapable").Hook} Hook */ | 
					
						
							| 
									
										
										
										
											2021-01-10 07:46:29 +08:00
										 |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsChunk} StatsChunk */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsChunkGroup} StatsChunkGroup */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsCompilation} StatsCompilation */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModule} StatsModule */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModuleReason} StatsModuleReason */ | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} PrintedElement | 
					
						
							|  |  |  |  * @property {string} element | 
					
						
							|  |  |  |  * @property {string} content | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} KnownStatsPrinterContext | 
					
						
							|  |  |  |  * @property {string=} type | 
					
						
							| 
									
										
										
										
											2021-01-10 07:46:29 +08:00
										 |  |  |  * @property {StatsCompilation=} compilation | 
					
						
							|  |  |  |  * @property {StatsChunkGroup=} chunkGroup | 
					
						
							|  |  |  |  * @property {StatsAsset=} asset | 
					
						
							|  |  |  |  * @property {StatsModule=} module | 
					
						
							|  |  |  |  * @property {StatsChunk=} chunk | 
					
						
							|  |  |  |  * @property {StatsModuleReason=} moduleReason | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  |  * @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 {(dateTime: number) => string=} formatDateTime | 
					
						
							|  |  |  |  * @property {(flag: string) => string=} formatFlag | 
					
						
							|  |  |  |  * @property {(time: number, boldQuantity?: boolean) => string=} formatTime | 
					
						
							|  |  |  |  * @property {string=} chunkGroupKind | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** @typedef {KnownStatsPrinterContext & Record<string, any>} StatsPrinterContext */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | class StatsPrinter { | 
					
						
							|  |  |  | 	constructor() { | 
					
						
							|  |  |  | 		this.hooks = Object.freeze({ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[string[], StatsPrinterContext], true>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			sortElements: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["elements", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[PrintedElement[], StatsPrinterContext], string>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			printElements: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["printedElements", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[any[], StatsPrinterContext], true>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			sortItems: new HookMap(() => new SyncBailHook(["items", "context"])), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[any, StatsPrinterContext], string>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			getItemName: new HookMap(() => new SyncBailHook(["item", "context"])), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[string[], StatsPrinterContext], string>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			printItems: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["printedItems", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncBailHook<[{}, StatsPrinterContext], string>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			print: new HookMap(() => new SyncBailHook(["object", "context"])), | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 			/** @type {HookMap<SyncWaterfallHook<[string, StatsPrinterContext]>>} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			result: new HookMap(() => new SyncWaterfallHook(["result", "context"])) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 		/** @type {Map<HookMap<Hook>, Map<string, Hook[]>>} */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		this._levelHookCache = new Map(); | 
					
						
							|  |  |  | 		this._inPrint = false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2020-10-13 02:16:58 +08:00
										 |  |  | 	 * get all level hooks | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @private | 
					
						
							|  |  |  | 	 * @template {Hook} T | 
					
						
							|  |  |  | 	 * @param {HookMap<T>} hookMap HookMap | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @returns {T[]} hooks | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 	_getAllLevelHooks(hookMap, type) { | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 		let cache = /** @type {Map<string, T[]>} */ ( | 
					
						
							|  |  |  | 			this._levelHookCache.get(hookMap) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		if (cache === undefined) { | 
					
						
							|  |  |  | 			cache = new Map(); | 
					
						
							|  |  |  | 			this._levelHookCache.set(hookMap, cache); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const cacheEntry = cache.get(type); | 
					
						
							|  |  |  | 		if (cacheEntry !== undefined) { | 
					
						
							|  |  |  | 			return cacheEntry; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 		/** @type {T[]} */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		const hooks = []; | 
					
						
							|  |  |  | 		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; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2020-10-13 02:16:58 +08:00
										 |  |  | 	 * Run `fn` for each level | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @private | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 * @template T | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @template R | 
					
						
							|  |  |  | 	 * @param {HookMap<SyncBailHook<T, R>>} hookMap HookMap | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @param {(hook: SyncBailHook<T, R>) => R} fn function | 
					
						
							|  |  |  | 	 * @returns {R} result of `fn` | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 	_forEachLevel(hookMap, type, fn) { | 
					
						
							|  |  |  | 		for (const hook of this._getAllLevelHooks(hookMap, type)) { | 
					
						
							|  |  |  | 			const result = fn(hook); | 
					
						
							|  |  |  | 			if (result !== undefined) return result; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * Run `fn` for each level | 
					
						
							|  |  |  | 	 * @private | 
					
						
							|  |  |  | 	 * @template T | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @param {HookMap<SyncWaterfallHook<T>>} hookMap HookMap | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2020-05-12 20:05:07 +08:00
										 |  |  | 	 * @param {AsArray<T>[0]} data data | 
					
						
							|  |  |  | 	 * @param {(hook: SyncWaterfallHook<T>, data: AsArray<T>[0]) => AsArray<T>[0]} fn function | 
					
						
							|  |  |  | 	 * @returns {AsArray<T>[0]} result of `fn` | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 	_forEachLevelWaterfall(hookMap, type, data, fn) { | 
					
						
							|  |  |  | 		for (const hook of this._getAllLevelHooks(hookMap, type)) { | 
					
						
							|  |  |  | 			data = fn(hook, data); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return data; | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2020-05-08 05:29:42 +08:00
										 |  |  | 	 * @param {string} type The type | 
					
						
							|  |  |  | 	 * @param {Object} object Object to print | 
					
						
							|  |  |  | 	 * @param {Object=} baseContext The base context | 
					
						
							|  |  |  | 	 * @returns {string} printed result | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 	print(type, object, baseContext) { | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		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; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @private | 
					
						
							|  |  |  | 	 * @param {string} type type | 
					
						
							|  |  |  | 	 * @param {Object} object object | 
					
						
							|  |  |  | 	 * @param {Object=} baseContext context | 
					
						
							| 
									
										
										
										
											2020-05-08 05:29:42 +08:00
										 |  |  | 	 * @returns {string} printed result | 
					
						
							| 
									
										
										
										
											2020-03-29 05:55:41 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 	_print(type, object, baseContext) { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 		const context = { | 
					
						
							|  |  |  | 			...baseContext, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			type, | 
					
						
							|  |  |  | 			[type]: object | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		let printResult = this._forEachLevel(this.hooks.print, type, hook => | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			hook.call(object, context) | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		if (printResult === undefined) { | 
					
						
							|  |  |  | 			if (Array.isArray(object)) { | 
					
						
							|  |  |  | 				const sortedItems = object.slice(); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				this._forEachLevel(this.hooks.sortItems, type, h => | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					h.call(sortedItems, context) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				const printedItems = sortedItems.map((item, i) => { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 					const itemContext = { | 
					
						
							|  |  |  | 						...context, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 						_index: i | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 					}; | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 					const itemName = this._forEachLevel( | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 						this.hooks.getItemName, | 
					
						
							|  |  |  | 						`${type}[]`, | 
					
						
							|  |  |  | 						h => h.call(item, itemContext) | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 					if (itemName) itemContext[itemName] = item; | 
					
						
							|  |  |  | 					return this.print( | 
					
						
							|  |  |  | 						itemName ? `${type}[].${itemName}` : `${type}[]`, | 
					
						
							|  |  |  | 						item, | 
					
						
							|  |  |  | 						itemContext | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				printResult = this._forEachLevel(this.hooks.printItems, type, h => | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					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") { | 
					
						
							| 
									
										
										
										
											2020-09-02 00:08:09 +08:00
										 |  |  | 				const elements = Object.keys(object).filter( | 
					
						
							|  |  |  | 					key => object[key] !== undefined | 
					
						
							|  |  |  | 				); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				this._forEachLevel(this.hooks.sortElements, type, h => | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					h.call(elements, context) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				const printedElements = elements.map(element => { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 					const content = this.print(`${type}.${element}`, object[element], { | 
					
						
							|  |  |  | 						...context, | 
					
						
							|  |  |  | 						_parent: object, | 
					
						
							|  |  |  | 						_element: element, | 
					
						
							|  |  |  | 						[element]: object[element] | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					return { element, content }; | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				printResult = this._forEachLevel(this.hooks.printElements, type, h => | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					h.call(printedElements, context) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				if (printResult === undefined) { | 
					
						
							|  |  |  | 					const result = printedElements.map(e => e.content).filter(Boolean); | 
					
						
							|  |  |  | 					if (result.length > 0) printResult = result.join("\n"); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		return this._forEachLevelWaterfall( | 
					
						
							|  |  |  | 			this.hooks.result, | 
					
						
							|  |  |  | 			type, | 
					
						
							|  |  |  | 			printResult, | 
					
						
							|  |  |  | 			(h, r) => h.call(r, context) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 		); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | module.exports = StatsPrinter; |