| 
									
										
										
										
											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, SyncBailHook, SyncWaterfallHook } = require("tapable"); | 
					
						
							|  |  |  | const { concatComparators, keepOriginalOrder } = require("../util/comparators"); | 
					
						
							| 
									
										
										
										
											2020-08-27 15:59:12 +08:00
										 |  |  | const smartGrouping = require("../util/smartGrouping"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** @typedef {import("../Chunk")} Chunk */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("../ChunkGroup").OriginRecord} OriginRecord */ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** @typedef {import("../Compilation")} Compilation */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("../Compilation").Asset} Asset */ | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | /** @typedef {import("../Compilation").NormalizedStatsOptions} NormalizedStatsOptions */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("../Dependency")} Dependency */ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** @typedef {import("../Module")} Module */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("../ModuleGraph").ModuleProfile} ModuleProfile */ | 
					
						
							|  |  |  | /** @typedef {import("../ModuleGraphConnection")} ModuleGraphConnection */ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** @typedef {import("../WebpackError")} WebpackError */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("../util/comparators").Comparator<EXPECTED_ANY>} Comparator */ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */ | 
					
						
							| 
									
										
										
										
											2025-08-21 21:34:51 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T, R | 
					
						
							|  |  |  |  * @typedef {import("../util/smartGrouping").GroupConfig<T, R>} GroupConfig | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").ChunkGroupInfoWithName} ChunkGroupInfoWithName */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").ModuleIssuerPath} ModuleIssuerPath */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").ModuleTrace} ModuleTrace */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsAsset} StatsAsset */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsChunk} StatsChunk */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsChunkGroup} StatsChunkGroup */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsChunkOrigin} StatsChunkOrigin */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsCompilation} StatsCompilation */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsError} StatsError */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModule} StatsModule */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModuleIssuer} StatsModuleIssuer */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModuleReason} StatsModuleReason */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModuleTraceDependency} StatsModuleTraceDependency */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsModuleTraceItem} StatsModuleTraceItem */ | 
					
						
							|  |  |  | /** @typedef {import("./DefaultStatsFactoryPlugin").StatsProfile} StatsProfile */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2024-06-11 21:09:50 +08:00
										 |  |  |  * @typedef {object} KnownStatsFactoryContext | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  |  * @property {string} type | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  |  * @property {Compilation} compilation | 
					
						
							| 
									
										
										
										
											2025-09-02 22:12:40 +08:00
										 |  |  |  * @property {(path: string) => string} makePathsRelative | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  |  * @property {Set<Module>} rootModules | 
					
						
							| 
									
										
										
										
											2025-03-12 09:56:14 +08:00
										 |  |  |  * @property {Map<string, Chunk[]>} compilationFileToChunks | 
					
						
							|  |  |  |  * @property {Map<string, Chunk[]>} compilationAuxiliaryFileToChunks | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  |  * @property {RuntimeSpec} runtime | 
					
						
							| 
									
										
										
										
											2025-09-11 08:10:10 +08:00
										 |  |  |  * @property {(compilation: Compilation) => Error[]} cachedGetErrors | 
					
						
							|  |  |  |  * @property {(compilation: Compilation) => Error[]} cachedGetWarnings | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-04 21:38:51 +08:00
										 |  |  | /** @typedef {KnownStatsFactoryContext & Record<string, EXPECTED_ANY>} StatsFactoryContext */ | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | // StatsLogging StatsLoggingEntry
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @template F | 
					
						
							|  |  |  |  * @typedef {T extends Compilation ? StatsCompilation : T extends ChunkGroupInfoWithName ? StatsChunkGroup : T extends Chunk ? StatsChunk : T extends OriginRecord ? StatsChunkOrigin : T extends Module ? StatsModule : T extends ModuleGraphConnection ? StatsModuleReason : T extends Asset ? StatsAsset : T extends ModuleTrace ? StatsModuleTraceItem : T extends Dependency ? StatsModuleTraceDependency : T extends Error ? StatsError : T extends ModuleProfile ? StatsProfile : F} StatsObject | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @template F | 
					
						
							|  |  |  |  * @typedef {T extends ChunkGroupInfoWithName[] ? Record<string, StatsObject<ChunkGroupInfoWithName, F>> : T extends (infer V)[] ? StatsObject<V, F>[] : StatsObject<T, F>} CreatedObject | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-09 23:41:52 +08:00
										 |  |  | /** @typedef {EXPECTED_ANY} ObjectForExtract */ | 
					
						
							|  |  |  | /** @typedef {EXPECTED_ANY} FactoryData */ | 
					
						
							|  |  |  | /** @typedef {EXPECTED_ANY} FactoryDataItem */ | 
					
						
							|  |  |  | /** @typedef {EXPECTED_ANY} Result */ | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {object} StatsFactoryHooks | 
					
						
							| 
									
										
										
										
											2024-10-24 22:09:39 +08:00
										 |  |  |  * @property {HookMap<SyncBailHook<[ObjectForExtract, FactoryData, StatsFactoryContext], void>>} extract | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem, StatsFactoryContext, number, number], boolean | void>>} filter | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[Comparator[], StatsFactoryContext], void>>} sort | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem, StatsFactoryContext, number, number], boolean | void>>} filterSorted | 
					
						
							| 
									
										
										
										
											2025-08-21 21:34:51 +08:00
										 |  |  |  * @property {HookMap<SyncBailHook<[GroupConfig<EXPECTED_ANY, EXPECTED_ANY>[], StatsFactoryContext], void>>} groupResults | 
					
						
							| 
									
										
										
										
											2024-10-24 22:09:39 +08:00
										 |  |  |  * @property {HookMap<SyncBailHook<[Comparator[], StatsFactoryContext], void>>} sortResults | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem, StatsFactoryContext, number, number], boolean | void>>} filterResults | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem[], StatsFactoryContext], Result | void>>} merge | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  |  * @property {HookMap<SyncBailHook<[Result, StatsFactoryContext], Result>>} result | 
					
						
							| 
									
										
										
										
											2024-10-24 22:09:39 +08:00
										 |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem, StatsFactoryContext], string | void>>} getItemName | 
					
						
							|  |  |  |  * @property {HookMap<SyncBailHook<[FactoryDataItem, StatsFactoryContext], StatsFactory | void>>} getItemFactory | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @typedef {Map<string, T[]>} Caches | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | class StatsFactory { | 
					
						
							|  |  |  | 	constructor() { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 		/** @type {StatsFactoryHooks} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 		this.hooks = Object.freeze({ | 
					
						
							|  |  |  | 			extract: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["object", "data", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			filter: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"]) | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			sort: new HookMap(() => new SyncBailHook(["comparators", "context"])), | 
					
						
							|  |  |  | 			filterSorted: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2020-08-27 15:59:12 +08:00
										 |  |  | 			groupResults: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["groupConfigs", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			sortResults: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["comparators", "context"]) | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			filterResults: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"]) | 
					
						
							|  |  |  | 			), | 
					
						
							|  |  |  | 			merge: new HookMap(() => new SyncBailHook(["items", "context"])), | 
					
						
							|  |  |  | 			result: new HookMap(() => new SyncWaterfallHook(["result", "context"])), | 
					
						
							|  |  |  | 			getItemName: new HookMap(() => new SyncBailHook(["item", "context"])), | 
					
						
							|  |  |  | 			getItemFactory: new HookMap(() => new SyncBailHook(["item", "context"])) | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 		const hooks = this.hooks; | 
					
						
							| 
									
										
										
										
											2025-07-10 21:30:16 +08:00
										 |  |  | 		this._caches = | 
					
						
							|  |  |  | 			/** @type {{ [Key in keyof StatsFactoryHooks]: Map<string, SyncBailHook<EXPECTED_ANY, EXPECTED_ANY>[]> }} */ ({}); | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 		for (const key of Object.keys(hooks)) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			this._caches[/** @type {keyof StatsFactoryHooks} */ (key)] = new Map(); | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		this._inCreate = false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @template {StatsFactoryHooks[keyof StatsFactoryHooks]} HM | 
					
						
							|  |  |  | 	 * @template {HM extends HookMap<infer H> ? H : never} H | 
					
						
							|  |  |  | 	 * @param {HM} hookMap hook map | 
					
						
							|  |  |  | 	 * @param {Caches<H>} cache cache | 
					
						
							|  |  |  | 	 * @param {string} type type | 
					
						
							|  |  |  | 	 * @returns {H[]} hooks | 
					
						
							|  |  |  | 	 * @private | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 	_getAllLevelHooks(hookMap, cache, type) { | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		const cacheEntry = cache.get(type); | 
					
						
							|  |  |  | 		if (cacheEntry !== undefined) { | 
					
						
							|  |  |  | 			return cacheEntry; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 		const hooks = /** @type {H[]} */ ([]); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		const typeParts = type.split("."); | 
					
						
							|  |  |  | 		for (let i = 0; i < typeParts.length; i++) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			const hook = /** @type {H} */ (hookMap.get(typeParts.slice(i).join("."))); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			if (hook) { | 
					
						
							|  |  |  | 				hooks.push(hook); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cache.set(type, hooks); | 
					
						
							|  |  |  | 		return hooks; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @template {StatsFactoryHooks[keyof StatsFactoryHooks]} HM | 
					
						
							|  |  |  | 	 * @template {HM extends HookMap<infer H> ? H : never} H | 
					
						
							|  |  |  | 	 * @template {H extends import("tapable").Hook<any, infer R> ? R : never} R | 
					
						
							|  |  |  | 	 * @param {HM} hookMap hook map | 
					
						
							|  |  |  | 	 * @param {Caches<H>} cache cache | 
					
						
							|  |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2025-03-12 09:56:14 +08:00
										 |  |  | 	 * @param {(hook: H) => R | void} fn fn | 
					
						
							| 
									
										
										
										
											2024-10-25 02:13:59 +08:00
										 |  |  | 	 * @returns {R | void} hook | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 * @private | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 	_forEachLevel(hookMap, cache, type, fn) { | 
					
						
							|  |  |  | 		for (const hook of this._getAllLevelHooks(hookMap, cache, type)) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			const result = fn(/** @type {H} */ (hook)); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			if (result !== undefined) return result; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @template {StatsFactoryHooks[keyof StatsFactoryHooks]} HM | 
					
						
							|  |  |  | 	 * @template {HM extends HookMap<infer H> ? H : never} H | 
					
						
							|  |  |  | 	 * @param {HM} hookMap hook map | 
					
						
							|  |  |  | 	 * @param {Caches<H>} cache cache | 
					
						
							|  |  |  | 	 * @param {string} type type | 
					
						
							|  |  |  | 	 * @param {FactoryData} data data | 
					
						
							| 
									
										
										
										
											2025-03-12 09:56:14 +08:00
										 |  |  | 	 * @param {(hook: H, factoryData: FactoryData) => FactoryData} fn fn | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 * @returns {FactoryData} data | 
					
						
							|  |  |  | 	 * @private | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 	_forEachLevelWaterfall(hookMap, cache, type, data, fn) { | 
					
						
							|  |  |  | 		for (const hook of this._getAllLevelHooks(hookMap, cache, type)) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			data = fn(/** @type {H} */ (hook), data); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return data; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @template {StatsFactoryHooks[keyof StatsFactoryHooks]} T | 
					
						
							|  |  |  | 	 * @template {T extends HookMap<infer H> ? H : never} H | 
					
						
							|  |  |  | 	 * @template {H extends import("tapable").Hook<any, infer R> ? R : never} R | 
					
						
							|  |  |  | 	 * @param {T} hookMap hook map | 
					
						
							|  |  |  | 	 * @param {Caches<H>} cache cache | 
					
						
							|  |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2025-08-28 18:34:30 +08:00
										 |  |  | 	 * @param {FactoryData[]} items items | 
					
						
							| 
									
										
										
										
											2025-03-12 09:56:14 +08:00
										 |  |  | 	 * @param {(hook: H, item: R, idx: number, i: number) => R | undefined} fn fn | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 * @param {boolean} forceClone force clone | 
					
						
							|  |  |  | 	 * @returns {R[]} result for each level | 
					
						
							|  |  |  | 	 * @private | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 	_forEachLevelFilter(hookMap, cache, type, items, fn, forceClone) { | 
					
						
							|  |  |  | 		const hooks = this._getAllLevelHooks(hookMap, cache, type); | 
					
						
							| 
									
										
										
										
											2025-07-03 17:06:45 +08:00
										 |  |  | 		if (hooks.length === 0) return forceClone ? [...items] : items; | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		let i = 0; | 
					
						
							|  |  |  | 		return items.filter((item, idx) => { | 
					
						
							|  |  |  | 			for (const hook of hooks) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 				const r = fn(/** @type {H} */ (hook), item, idx, i); | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				if (r !== undefined) { | 
					
						
							|  |  |  | 					if (r) i++; | 
					
						
							|  |  |  | 					return r; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			i++; | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | 	 * @template FactoryData | 
					
						
							|  |  |  | 	 * @template FallbackCreatedObject | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 	 * @param {string} type type | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 * @param {FactoryData} data factory data | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 	 * @param {Omit<StatsFactoryContext, "type">} baseContext context used as base | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | 	 * @returns {CreatedObject<FactoryData, FallbackCreatedObject>} created object | 
					
						
							| 
									
										
										
										
											2020-12-18 01:51:55 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 	create(type, data, baseContext) { | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		if (this._inCreate) { | 
					
						
							|  |  |  | 			return this._create(type, data, baseContext); | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		try { | 
					
						
							|  |  |  | 			this._inCreate = true; | 
					
						
							|  |  |  | 			return this._create(type, data, baseContext); | 
					
						
							|  |  |  | 		} finally { | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 			for (const key of Object.keys(this._caches)) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 				this._caches[/** @type {keyof StatsFactoryHooks} */ (key)].clear(); | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 			this._inCreate = false; | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | 	 * @private | 
					
						
							|  |  |  | 	 * @template FactoryData | 
					
						
							|  |  |  | 	 * @template FallbackCreatedObject | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 * @param {string} type type | 
					
						
							|  |  |  | 	 * @param {FactoryData} data factory data | 
					
						
							|  |  |  | 	 * @param {Omit<StatsFactoryContext, "type">} baseContext context used as base | 
					
						
							| 
									
										
										
										
											2025-04-16 22:04:11 +08:00
										 |  |  | 	 * @returns {CreatedObject<FactoryData, FallbackCreatedObject>} created object | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 	_create(type, data, baseContext) { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 		const context = /** @type {StatsFactoryContext} */ ({ | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 			...baseContext, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			type, | 
					
						
							|  |  |  | 			[type]: data | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 		if (Array.isArray(data)) { | 
					
						
							|  |  |  | 			// run filter on unsorted items
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			const items = this._forEachLevelFilter( | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				this.hooks.filter, | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 				this._caches.filter, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				type, | 
					
						
							|  |  |  | 				data, | 
					
						
							|  |  |  | 				(h, r, idx, i) => h.call(r, context, idx, i), | 
					
						
							|  |  |  | 				true | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// sort items
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			/** @type {Comparator[]} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			const comparators = []; | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 			this._forEachLevel(this.hooks.sort, this._caches.sort, type, (h) => | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				h.call(comparators, context) | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			if (comparators.length > 0) { | 
					
						
							|  |  |  | 				items.sort( | 
					
						
							| 
									
										
										
										
											2020-03-30 23:56:37 +08:00
										 |  |  | 					// @ts-expect-error number of arguments is correct
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					concatComparators(...comparators, keepOriginalOrder(items)) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// run filter on sorted items
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			const items2 = this._forEachLevelFilter( | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				this.hooks.filterSorted, | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 				this._caches.filterSorted, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				type, | 
					
						
							|  |  |  | 				items, | 
					
						
							|  |  |  | 				(h, r, idx, i) => h.call(r, context, idx, i), | 
					
						
							|  |  |  | 				false | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// for each item
 | 
					
						
							| 
									
										
										
										
											2020-08-27 15:59:12 +08:00
										 |  |  | 			let resultItems = items2.map((item, i) => { | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 				/** @type {StatsFactoryContext} */ | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// run getItemName
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				const itemName = this._forEachLevel( | 
					
						
							|  |  |  | 					this.hooks.getItemName, | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 					this._caches.getItemName, | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 					`${type}[]`, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 					(h) => h.call(item, itemContext) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				); | 
					
						
							|  |  |  | 				if (itemName) itemContext[itemName] = item; | 
					
						
							|  |  |  | 				const innerType = itemName ? `${type}[].${itemName}` : `${type}[]`; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// run getItemFactory
 | 
					
						
							|  |  |  | 				const itemFactory = | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 					this._forEachLevel( | 
					
						
							|  |  |  | 						this.hooks.getItemFactory, | 
					
						
							|  |  |  | 						this._caches.getItemFactory, | 
					
						
							|  |  |  | 						innerType, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 						(h) => h.call(item, itemContext) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					) || this; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// run item factory
 | 
					
						
							|  |  |  | 				return itemFactory.create(innerType, item, itemContext); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// sort result items
 | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 			/** @type {Comparator[]} */ | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			const comparators2 = []; | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 			this._forEachLevel( | 
					
						
							|  |  |  | 				this.hooks.sortResults, | 
					
						
							|  |  |  | 				this._caches.sortResults, | 
					
						
							|  |  |  | 				type, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 				(h) => h.call(comparators2, context) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 			if (comparators2.length > 0) { | 
					
						
							|  |  |  | 				resultItems.sort( | 
					
						
							| 
									
										
										
										
											2020-03-30 23:56:37 +08:00
										 |  |  | 					// @ts-expect-error number of arguments is correct
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 					concatComparators(...comparators2, keepOriginalOrder(resultItems)) | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-02 00:08:09 +08:00
										 |  |  | 			// group result items
 | 
					
						
							| 
									
										
										
										
											2025-08-21 21:34:51 +08:00
										 |  |  | 			/** @type {GroupConfig<EXPECTED_ANY, EXPECTED_ANY>[]} */ | 
					
						
							| 
									
										
										
										
											2020-09-02 00:08:09 +08:00
										 |  |  | 			const groupConfigs = []; | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 			this._forEachLevel( | 
					
						
							|  |  |  | 				this.hooks.groupResults, | 
					
						
							|  |  |  | 				this._caches.groupResults, | 
					
						
							|  |  |  | 				type, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 				(h) => h.call(groupConfigs, context) | 
					
						
							| 
									
										
										
										
											2020-09-02 00:08:09 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 			if (groupConfigs.length > 0) { | 
					
						
							|  |  |  | 				resultItems = smartGrouping(resultItems, groupConfigs); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			// run filter on sorted result items
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			const finalResultItems = this._forEachLevelFilter( | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				this.hooks.filterResults, | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 				this._caches.filterResults, | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 				type, | 
					
						
							|  |  |  | 				resultItems, | 
					
						
							|  |  |  | 				(h, r, idx, i) => h.call(r, context, idx, i), | 
					
						
							|  |  |  | 				false | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// run merge on mapped items
 | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 			let result = this._forEachLevel( | 
					
						
							|  |  |  | 				this.hooks.merge, | 
					
						
							|  |  |  | 				this._caches.merge, | 
					
						
							|  |  |  | 				type, | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 				(h) => h.call(finalResultItems, context) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			); | 
					
						
							|  |  |  | 			if (result === undefined) result = finalResultItems; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// run result on merged items
 | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 			return this._forEachLevelWaterfall( | 
					
						
							|  |  |  | 				this.hooks.result, | 
					
						
							| 
									
										
										
										
											2020-12-04 22:02:06 +08:00
										 |  |  | 				this._caches.result, | 
					
						
							| 
									
										
										
										
											2019-07-25 04:42:05 +08:00
										 |  |  | 				type, | 
					
						
							|  |  |  | 				result, | 
					
						
							|  |  |  | 				(h, r) => h.call(r, context) | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 			); | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-08-06 11:08:48 +08:00
										 |  |  | 		/** @type {ObjectForExtract} */ | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 		const object = {}; | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 		// run extract on value
 | 
					
						
							| 
									
										
										
										
											2025-07-17 00:13:14 +08:00
										 |  |  | 		this._forEachLevel(this.hooks.extract, this._caches.extract, type, (h) => | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 			h.call(object, data, context) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-31 04:21:27 +08:00
										 |  |  | 		// run result on extracted object
 | 
					
						
							|  |  |  | 		return this._forEachLevelWaterfall( | 
					
						
							|  |  |  | 			this.hooks.result, | 
					
						
							|  |  |  | 			this._caches.result, | 
					
						
							|  |  |  | 			type, | 
					
						
							|  |  |  | 			object, | 
					
						
							|  |  |  | 			(h, r) => h.call(r, context) | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-07-02 20:10:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 01:29:12 +08:00
										 |  |  | module.exports = StatsFactory; |