All Downloads are FREE. Search and download functionalities are using the official Maven repository.

package.lib.stats.StatsFactory.js Maven / Gradle / Ivy

Go to download

Packs ECMAScript/CommonJs/AMD modules for the browser. Allows you to split your codebase into multiple bundles, which can be loaded on demand. Supports loaders to preprocess files, i.e. json, jsx, es7, css, less, ... and your custom stuff.

The newest version!
/*
	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} GroupConfig */

/**
 * @typedef {object} KnownStatsFactoryContext
 * @property {string} type
 * @property {function(string): string=} makePathsRelative
 * @property {Compilation=} compilation
 * @property {Set=} rootModules
 * @property {Map=} compilationFileToChunks
 * @property {Map=} compilationAuxiliaryFileToChunks
 * @property {RuntimeSpec=} runtime
 * @property {function(Compilation): WebpackError[]=} cachedGetErrors
 * @property {function(Compilation): WebpackError[]=} cachedGetWarnings
 */

/** @typedef {KnownStatsFactoryContext & Record} StatsFactoryContext */

class StatsFactory {
	constructor() {
		this.hooks = Object.freeze({
			/** @type {HookMap>} */
			extract: new HookMap(
				() => new SyncBailHook(["object", "data", "context"])
			),
			/** @type {HookMap>} */
			filter: new HookMap(
				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
			),
			/** @type {HookMap>} */
			sort: new HookMap(() => new SyncBailHook(["comparators", "context"])),
			/** @type {HookMap>} */
			filterSorted: new HookMap(
				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
			),
			/** @type {HookMap>} */
			groupResults: new HookMap(
				() => new SyncBailHook(["groupConfigs", "context"])
			),
			/** @type {HookMap>} */
			sortResults: new HookMap(
				() => new SyncBailHook(["comparators", "context"])
			),
			/** @type {HookMap>} */
			filterResults: new HookMap(
				() => new SyncBailHook(["item", "context", "index", "unfilteredIndex"])
			),
			/** @type {HookMap>} */
			merge: new HookMap(() => new SyncBailHook(["items", "context"])),
			/** @type {HookMap>} */
			result: new HookMap(() => new SyncWaterfallHook(["result", "context"])),
			/** @type {HookMap>} */
			getItemName: new HookMap(() => new SyncBailHook(["item", "context"])),
			/** @type {HookMap>} */
			getItemFactory: new HookMap(() => new SyncBailHook(["item", "context"]))
		});
		const hooks = this.hooks;
		this._caches =
			/** @type {Record[]>>} */ ({});
		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} 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;




© 2015 - 2024 Weber Informatics LLC | Privacy Policy