You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							292 lines
						
					
					
						
							8.4 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							292 lines
						
					
					
						
							8.4 KiB
						
					
					
				
								/*
							 | 
						|
									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");
							 | 
						|
								const smartGrouping = require("../util/smartGrouping");
							 | 
						|
								
							 | 
						|
								/** @typedef {import("../Chunk")} Chunk */
							 | 
						|
								/** @typedef {import("../Compilation")} Compilation */
							 | 
						|
								/** @typedef {import("../Module")} Module */
							 | 
						|
								/** @typedef {import("../WebpackError")} WebpackError */
							 | 
						|
								/** @typedef {import("../util/runtime").RuntimeSpec} RuntimeSpec */
							 | 
						|
								
							 | 
						|
								/** @typedef {import("../util/smartGrouping").GroupConfig<any, object>} GroupConfig */
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * @typedef {Object} KnownStatsFactoryContext
							 | 
						|
								 * @property {string} type
							 | 
						|
								 * @property {function(string): string=} makePathsRelative
							 | 
						|
								 * @property {Compilation=} compilation
							 | 
						|
								 * @property {Set<Module>=} rootModules
							 | 
						|
								 * @property {Map<string,Chunk[]>=} compilationFileToChunks
							 | 
						|
								 * @property {Map<string,Chunk[]>=} compilationAuxiliaryFileToChunks
							 | 
						|
								 * @property {RuntimeSpec=} runtime
							 | 
						|
								 * @property {function(Compilation): WebpackError[]=} cachedGetErrors
							 | 
						|
								 * @property {function(Compilation): WebpackError[]=} cachedGetWarnings
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								/** @typedef {KnownStatsFactoryContext & Record<string, any>} StatsFactoryContext */
							 | 
						|
								
							 | 
						|
								class StatsFactory {
							 | 
						|
									constructor() {
							 | 
						|
										this.hooks = Object.freeze({
							 | 
						|
											/** @type {HookMap<SyncBailHook<[Object, any, StatsFactoryContext]>>} */
							 | 
						|
											extract: new HookMap(
							 | 
						|
												() => new SyncBailHook(["object", "data", "context"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any, StatsFactoryContext, number, number]>>} */
							 | 
						|
											filter: new HookMap(
							 | 
						|
												() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[(function(any, any): number)[], StatsFactoryContext]>>} */
							 | 
						|
											sort: new HookMap(() => new SyncBailHook(["comparators", "context"])),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any, StatsFactoryContext, number, number]>>} */
							 | 
						|
											filterSorted: new HookMap(
							 | 
						|
												() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[GroupConfig[], StatsFactoryContext]>>} */
							 | 
						|
											groupResults: new HookMap(
							 | 
						|
												() => new SyncBailHook(["groupConfigs", "context"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[(function(any, any): number)[], StatsFactoryContext]>>} */
							 | 
						|
											sortResults: new HookMap(
							 | 
						|
												() => new SyncBailHook(["comparators", "context"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any, StatsFactoryContext, number, number]>>} */
							 | 
						|
											filterResults: new HookMap(
							 | 
						|
												() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
							 | 
						|
											),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any[], StatsFactoryContext]>>} */
							 | 
						|
											merge: new HookMap(() => new SyncBailHook(["items", "context"])),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any[], StatsFactoryContext]>>} */
							 | 
						|
											result: new HookMap(() => new SyncWaterfallHook(["result", "context"])),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any, StatsFactoryContext]>>} */
							 | 
						|
											getItemName: new HookMap(() => new SyncBailHook(["item", "context"])),
							 | 
						|
											/** @type {HookMap<SyncBailHook<[any, StatsFactoryContext]>>} */
							 | 
						|
											getItemFactory: new HookMap(() => new SyncBailHook(["item", "context"]))
							 | 
						|
										});
							 | 
						|
										const hooks = this.hooks;
							 | 
						|
										this._caches =
							 | 
						|
											/** @type {Record<keyof typeof hooks, Map<string, SyncBailHook<[any[], StatsFactoryContext]>[]>>} */ ({});
							 | 
						|
										for (const key of Object.keys(hooks)) {
							 | 
						|
											this._caches[key] = new Map();
							 | 
						|
										}
							 | 
						|
										this._inCreate = false;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_getAllLevelHooks(hookMap, cache, type) {
							 | 
						|
										const cacheEntry = cache.get(type);
							 | 
						|
										if (cacheEntry !== undefined) {
							 | 
						|
											return cacheEntry;
							 | 
						|
										}
							 | 
						|
										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;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_forEachLevel(hookMap, cache, type, fn) {
							 | 
						|
										for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
							 | 
						|
											const result = fn(hook);
							 | 
						|
											if (result !== undefined) return result;
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_forEachLevelWaterfall(hookMap, cache, type, data, fn) {
							 | 
						|
										for (const hook of this._getAllLevelHooks(hookMap, cache, type)) {
							 | 
						|
											data = fn(hook, data);
							 | 
						|
										}
							 | 
						|
										return data;
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_forEachLevelFilter(hookMap, cache, type, items, fn, forceClone) {
							 | 
						|
										const hooks = this._getAllLevelHooks(hookMap, cache, type);
							 | 
						|
										if (hooks.length === 0) return forceClone ? items.slice() : items;
							 | 
						|
										let i = 0;
							 | 
						|
										return items.filter((item, idx) => {
							 | 
						|
											for (const hook of hooks) {
							 | 
						|
												const r = fn(hook, item, idx, i);
							 | 
						|
												if (r !== undefined) {
							 | 
						|
													if (r) i++;
							 | 
						|
													return r;
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
											i++;
							 | 
						|
											return true;
							 | 
						|
										});
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									/**
							 | 
						|
									 * @param {string} type type
							 | 
						|
									 * @param {any} data factory data
							 | 
						|
									 * @param {Omit<StatsFactoryContext, "type">} baseContext context used as base
							 | 
						|
									 * @returns {any} created object
							 | 
						|
									 */
							 | 
						|
									create(type, data, baseContext) {
							 | 
						|
										if (this._inCreate) {
							 | 
						|
											return this._create(type, data, baseContext);
							 | 
						|
										} else {
							 | 
						|
											try {
							 | 
						|
												this._inCreate = true;
							 | 
						|
												return this._create(type, data, baseContext);
							 | 
						|
											} finally {
							 | 
						|
												for (const key of Object.keys(this._caches)) this._caches[key].clear();
							 | 
						|
												this._inCreate = false;
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									_create(type, data, baseContext) {
							 | 
						|
										const context = {
							 | 
						|
											...baseContext,
							 | 
						|
											type,
							 | 
						|
											[type]: data
							 | 
						|
										};
							 | 
						|
										if (Array.isArray(data)) {
							 | 
						|
											// run filter on unsorted items
							 | 
						|
											const items = this._forEachLevelFilter(
							 | 
						|
												this.hooks.filter,
							 | 
						|
												this._caches.filter,
							 | 
						|
												type,
							 | 
						|
												data,
							 | 
						|
												(h, r, idx, i) => h.call(r, context, idx, i),
							 | 
						|
												true
							 | 
						|
											);
							 | 
						|
								
							 | 
						|
											// sort items
							 | 
						|
											const comparators = [];
							 | 
						|
											this._forEachLevel(this.hooks.sort, this._caches.sort, type, h =>
							 | 
						|
												h.call(comparators, context)
							 | 
						|
											);
							 | 
						|
											if (comparators.length > 0) {
							 | 
						|
												items.sort(
							 | 
						|
													// @ts-expect-error number of arguments is correct
							 | 
						|
													concatComparators(...comparators, keepOriginalOrder(items))
							 | 
						|
												);
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// run filter on sorted items
							 | 
						|
											const items2 = this._forEachLevelFilter(
							 | 
						|
												this.hooks.filterSorted,
							 | 
						|
												this._caches.filterSorted,
							 | 
						|
												type,
							 | 
						|
												items,
							 | 
						|
												(h, r, idx, i) => h.call(r, context, idx, i),
							 | 
						|
												false
							 | 
						|
											);
							 | 
						|
								
							 | 
						|
											// for each item
							 | 
						|
											let resultItems = items2.map((item, i) => {
							 | 
						|
												const itemContext = {
							 | 
						|
													...context,
							 | 
						|
													_index: i
							 | 
						|
												};
							 | 
						|
								
							 | 
						|
												// run getItemName
							 | 
						|
												const itemName = this._forEachLevel(
							 | 
						|
													this.hooks.getItemName,
							 | 
						|
													this._caches.getItemName,
							 | 
						|
													`${type}[]`,
							 | 
						|
													h => h.call(item, itemContext)
							 | 
						|
												);
							 | 
						|
												if (itemName) itemContext[itemName] = item;
							 | 
						|
												const innerType = itemName ? `${type}[].${itemName}` : `${type}[]`;
							 | 
						|
								
							 | 
						|
												// run getItemFactory
							 | 
						|
												const itemFactory =
							 | 
						|
													this._forEachLevel(
							 | 
						|
														this.hooks.getItemFactory,
							 | 
						|
														this._caches.getItemFactory,
							 | 
						|
														innerType,
							 | 
						|
														h => h.call(item, itemContext)
							 | 
						|
													) || this;
							 | 
						|
								
							 | 
						|
												// run item factory
							 | 
						|
												return itemFactory.create(innerType, item, itemContext);
							 | 
						|
											});
							 | 
						|
								
							 | 
						|
											// sort result items
							 | 
						|
											const comparators2 = [];
							 | 
						|
											this._forEachLevel(
							 | 
						|
												this.hooks.sortResults,
							 | 
						|
												this._caches.sortResults,
							 | 
						|
												type,
							 | 
						|
												h => h.call(comparators2, context)
							 | 
						|
											);
							 | 
						|
											if (comparators2.length > 0) {
							 | 
						|
												resultItems.sort(
							 | 
						|
													// @ts-expect-error number of arguments is correct
							 | 
						|
													concatComparators(...comparators2, keepOriginalOrder(resultItems))
							 | 
						|
												);
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// group result items
							 | 
						|
											const groupConfigs = [];
							 | 
						|
											this._forEachLevel(
							 | 
						|
												this.hooks.groupResults,
							 | 
						|
												this._caches.groupResults,
							 | 
						|
												type,
							 | 
						|
												h => h.call(groupConfigs, context)
							 | 
						|
											);
							 | 
						|
											if (groupConfigs.length > 0) {
							 | 
						|
												resultItems = smartGrouping(resultItems, groupConfigs);
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// run filter on sorted result items
							 | 
						|
											const finalResultItems = this._forEachLevelFilter(
							 | 
						|
												this.hooks.filterResults,
							 | 
						|
												this._caches.filterResults,
							 | 
						|
												type,
							 | 
						|
												resultItems,
							 | 
						|
												(h, r, idx, i) => h.call(r, context, idx, i),
							 | 
						|
												false
							 | 
						|
											);
							 | 
						|
								
							 | 
						|
											// run merge on mapped items
							 | 
						|
											let result = this._forEachLevel(
							 | 
						|
												this.hooks.merge,
							 | 
						|
												this._caches.merge,
							 | 
						|
												type,
							 | 
						|
												h => h.call(finalResultItems, context)
							 | 
						|
											);
							 | 
						|
											if (result === undefined) result = finalResultItems;
							 | 
						|
								
							 | 
						|
											// run result on merged items
							 | 
						|
											return this._forEachLevelWaterfall(
							 | 
						|
												this.hooks.result,
							 | 
						|
												this._caches.result,
							 | 
						|
												type,
							 | 
						|
												result,
							 | 
						|
												(h, r) => h.call(r, context)
							 | 
						|
											);
							 | 
						|
										} else {
							 | 
						|
											const object = {};
							 | 
						|
								
							 | 
						|
											// run extract on value
							 | 
						|
											this._forEachLevel(this.hooks.extract, this._caches.extract, type, h =>
							 | 
						|
												h.call(object, data, context)
							 | 
						|
											);
							 | 
						|
								
							 | 
						|
											// run result on extracted object
							 | 
						|
											return this._forEachLevelWaterfall(
							 | 
						|
												this.hooks.result,
							 | 
						|
												this._caches.result,
							 | 
						|
												type,
							 | 
						|
												object,
							 | 
						|
												(h, r) => h.call(r, context)
							 | 
						|
											);
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								module.exports = StatsFactory;
							 |