| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 | /*	MIT License http://www.opensource.org/licenses/mit-license.php	Author Tobias Koppers @sokra*/"use strict";const { HookMap, SyncWaterfallHook, SyncBailHook } = require("tapable");/** @template T @typedef {import("tapable").AsArray<T>} AsArray<T> *//** @typedef {import("tapable").Hook} Hook *//** @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 *//** * @typedef {Object} PrintedElement * @property {string} element * @property {string} content *//** * @typedef {Object} KnownStatsPrinterContext * @property {string=} type * @property {StatsCompilation=} compilation * @property {StatsChunkGroup=} chunkGroup * @property {StatsAsset=} asset * @property {StatsModule=} module * @property {StatsChunk=} chunk * @property {StatsModuleReason=} 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 {(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 */class StatsPrinter {	constructor() {		this.hooks = Object.freeze({			/** @type {HookMap<SyncBailHook<[string[], StatsPrinterContext], true>>} */			sortElements: new HookMap(				() => new SyncBailHook(["elements", "context"])			),			/** @type {HookMap<SyncBailHook<[PrintedElement[], StatsPrinterContext], string>>} */			printElements: new HookMap(				() => new SyncBailHook(["printedElements", "context"])			),			/** @type {HookMap<SyncBailHook<[any[], StatsPrinterContext], true>>} */			sortItems: new HookMap(() => new SyncBailHook(["items", "context"])),			/** @type {HookMap<SyncBailHook<[any, StatsPrinterContext], string>>} */			getItemName: new HookMap(() => new SyncBailHook(["item", "context"])),			/** @type {HookMap<SyncBailHook<[string[], StatsPrinterContext], string>>} */			printItems: new HookMap(				() => new SyncBailHook(["printedItems", "context"])			),			/** @type {HookMap<SyncBailHook<[{}, StatsPrinterContext], string>>} */			print: new HookMap(() => new SyncBailHook(["object", "context"])),			/** @type {HookMap<SyncWaterfallHook<[string, StatsPrinterContext]>>} */			result: new HookMap(() => new SyncWaterfallHook(["result", "context"]))		});		/** @type {Map<HookMap<Hook>, Map<string, Hook[]>>} */		this._levelHookCache = new Map();		this._inPrint = false;	}	/**	 * get all level hooks	 * @private	 * @template {Hook} T	 * @param {HookMap<T>} hookMap HookMap	 * @param {string} type type	 * @returns {T[]} hooks	 */	_getAllLevelHooks(hookMap, type) {		let cache = /** @type {Map<string, T[]>} */ (			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;		}		/** @type {T[]} */		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;	}	/**	 * Run `fn` for each level	 * @private	 * @template T	 * @template R	 * @param {HookMap<SyncBailHook<T, R>>} hookMap HookMap	 * @param {string} type type	 * @param {(hook: SyncBailHook<T, R>) => R} fn function	 * @returns {R} result of `fn`	 */	_forEachLevel(hookMap, type, fn) {		for (const hook of this._getAllLevelHooks(hookMap, type)) {			const result = fn(hook);			if (result !== undefined) return result;		}	}	/**	 * Run `fn` for each level	 * @private	 * @template T	 * @param {HookMap<SyncWaterfallHook<T>>} hookMap HookMap	 * @param {string} type type	 * @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`	 */	_forEachLevelWaterfall(hookMap, type, data, fn) {		for (const hook of this._getAllLevelHooks(hookMap, type)) {			data = fn(hook, data);		}		return data;	}	/**	 * @param {string} type The type	 * @param {Object} object Object to print	 * @param {Object=} baseContext The base context	 * @returns {string} printed result	 */	print(type, object, baseContext) {		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;			}		}	}	/**	 * @private	 * @param {string} type type	 * @param {Object} object object	 * @param {Object=} baseContext context	 * @returns {string} printed result	 */	_print(type, object, baseContext) {		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)		);	}}module.exports = StatsPrinter;
 |