mirror of https://github.com/webpack/webpack.git
				
				
				
			
		
			
				
	
	
		
			350 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			350 lines
		
	
	
		
			8.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
/*
 | 
						|
	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
						|
	Author Tobias Koppers @sokra
 | 
						|
*/
 | 
						|
 | 
						|
"use strict";
 | 
						|
 | 
						|
const { forEachBail } = require("enhanced-resolve");
 | 
						|
const asyncLib = require("neo-async");
 | 
						|
const getLazyHashedEtag = require("./cache/getLazyHashedEtag");
 | 
						|
const mergeEtags = require("./cache/mergeEtags");
 | 
						|
 | 
						|
/** @typedef {import("./Cache")} Cache */
 | 
						|
/** @typedef {import("./Cache").Etag} Etag */
 | 
						|
/** @typedef {import("./WebpackError")} WebpackError */
 | 
						|
/** @typedef {import("./cache/getLazyHashedEtag").HashableObject} HashableObject */
 | 
						|
/** @typedef {typeof import("./util/Hash")} HashConstructor */
 | 
						|
 | 
						|
/**
 | 
						|
 * @template T
 | 
						|
 * @callback CallbackCache
 | 
						|
 * @param {(Error | null)=} err
 | 
						|
 * @param {(T | null)=} result
 | 
						|
 * @returns {void}
 | 
						|
 */
 | 
						|
 | 
						|
/**
 | 
						|
 * @template T
 | 
						|
 * @callback CallbackNormalErrorCache
 | 
						|
 * @param {(Error | null)=} err
 | 
						|
 * @param {T=} result
 | 
						|
 * @returns {void}
 | 
						|
 */
 | 
						|
 | 
						|
class MultiItemCache {
 | 
						|
	/**
 | 
						|
	 * @param {ItemCacheFacade[]} items item caches
 | 
						|
	 */
 | 
						|
	constructor(items) {
 | 
						|
		this._items = items;
 | 
						|
		// eslint-disable-next-line no-constructor-return
 | 
						|
		if (items.length === 1) return /** @type {any} */ (items[0]);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {CallbackCache<T>} callback signals when the value is retrieved
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	get(callback) {
 | 
						|
		forEachBail(this._items, (item, callback) => item.get(callback), callback);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @returns {Promise<T>} promise with the data
 | 
						|
	 */
 | 
						|
	getPromise() {
 | 
						|
		/**
 | 
						|
		 * @param {number} i index
 | 
						|
		 * @returns {Promise<T>} promise with the data
 | 
						|
		 */
 | 
						|
		const next = i =>
 | 
						|
			this._items[i].getPromise().then(result => {
 | 
						|
				if (result !== undefined) return result;
 | 
						|
				if (++i < this._items.length) return next(i);
 | 
						|
			});
 | 
						|
		return next(0);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @param {CallbackCache<void>} callback signals when the value is stored
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	store(data, callback) {
 | 
						|
		asyncLib.each(
 | 
						|
			this._items,
 | 
						|
			(item, callback) => item.store(data, callback),
 | 
						|
			callback
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @returns {Promise<void>} promise signals when the value is stored
 | 
						|
	 */
 | 
						|
	storePromise(data) {
 | 
						|
		return Promise.all(this._items.map(item => item.storePromise(data))).then(
 | 
						|
			() => {}
 | 
						|
		);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
class ItemCacheFacade {
 | 
						|
	/**
 | 
						|
	 * @param {Cache} cache the root cache
 | 
						|
	 * @param {string} name the child cache item name
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 */
 | 
						|
	constructor(cache, name, etag) {
 | 
						|
		this._cache = cache;
 | 
						|
		this._name = name;
 | 
						|
		this._etag = etag;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {CallbackCache<T>} callback signals when the value is retrieved
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	get(callback) {
 | 
						|
		this._cache.get(this._name, this._etag, callback);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @returns {Promise<T>} promise with the data
 | 
						|
	 */
 | 
						|
	getPromise() {
 | 
						|
		return new Promise((resolve, reject) => {
 | 
						|
			this._cache.get(this._name, this._etag, (err, data) => {
 | 
						|
				if (err) {
 | 
						|
					reject(err);
 | 
						|
				} else {
 | 
						|
					resolve(data);
 | 
						|
				}
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @param {CallbackCache<void>} callback signals when the value is stored
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	store(data, callback) {
 | 
						|
		this._cache.store(this._name, this._etag, data, callback);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @returns {Promise<void>} promise signals when the value is stored
 | 
						|
	 */
 | 
						|
	storePromise(data) {
 | 
						|
		return new Promise((resolve, reject) => {
 | 
						|
			this._cache.store(this._name, this._etag, data, err => {
 | 
						|
				if (err) {
 | 
						|
					reject(err);
 | 
						|
				} else {
 | 
						|
					resolve();
 | 
						|
				}
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
 | 
						|
	 * @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	provide(computer, callback) {
 | 
						|
		this.get((err, cacheEntry) => {
 | 
						|
			if (err) return callback(err);
 | 
						|
			if (cacheEntry !== undefined) return cacheEntry;
 | 
						|
			computer((err, result) => {
 | 
						|
				if (err) return callback(err);
 | 
						|
				this.store(result, err => {
 | 
						|
					if (err) return callback(err);
 | 
						|
					callback(null, result);
 | 
						|
				});
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {function(): Promise<T> | T} computer function to compute the value if not cached
 | 
						|
	 * @returns {Promise<T>} promise with the data
 | 
						|
	 */
 | 
						|
	async providePromise(computer) {
 | 
						|
		const cacheEntry = await this.getPromise();
 | 
						|
		if (cacheEntry !== undefined) return cacheEntry;
 | 
						|
		const result = await computer();
 | 
						|
		await this.storePromise(result);
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
class CacheFacade {
 | 
						|
	/**
 | 
						|
	 * @param {Cache} cache the root cache
 | 
						|
	 * @param {string} name the child cache name
 | 
						|
	 * @param {(string | HashConstructor)=} hashFunction the hash function to use
 | 
						|
	 */
 | 
						|
	constructor(cache, name, hashFunction) {
 | 
						|
		this._cache = cache;
 | 
						|
		this._name = name;
 | 
						|
		this._hashFunction = hashFunction;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param {string} name the child cache name#
 | 
						|
	 * @returns {CacheFacade} child cache
 | 
						|
	 */
 | 
						|
	getChildCache(name) {
 | 
						|
		return new CacheFacade(
 | 
						|
			this._cache,
 | 
						|
			`${this._name}|${name}`,
 | 
						|
			this._hashFunction
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @returns {ItemCacheFacade} item cache
 | 
						|
	 */
 | 
						|
	getItemCache(identifier, etag) {
 | 
						|
		return new ItemCacheFacade(
 | 
						|
			this._cache,
 | 
						|
			`${this._name}|${identifier}`,
 | 
						|
			etag
 | 
						|
		);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param {HashableObject} obj an hashable object
 | 
						|
	 * @returns {Etag} an etag that is lazy hashed
 | 
						|
	 */
 | 
						|
	getLazyHashedEtag(obj) {
 | 
						|
		return getLazyHashedEtag(obj, this._hashFunction);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @param {Etag} a an etag
 | 
						|
	 * @param {Etag} b another etag
 | 
						|
	 * @returns {Etag} an etag that represents both
 | 
						|
	 */
 | 
						|
	mergeEtags(a, b) {
 | 
						|
		return mergeEtags(a, b);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @param {CallbackCache<T>} callback signals when the value is retrieved
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	get(identifier, etag, callback) {
 | 
						|
		this._cache.get(`${this._name}|${identifier}`, etag, callback);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @returns {Promise<T>} promise with the data
 | 
						|
	 */
 | 
						|
	getPromise(identifier, etag) {
 | 
						|
		return new Promise((resolve, reject) => {
 | 
						|
			this._cache.get(`${this._name}|${identifier}`, etag, (err, data) => {
 | 
						|
				if (err) {
 | 
						|
					reject(err);
 | 
						|
				} else {
 | 
						|
					resolve(data);
 | 
						|
				}
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @param {CallbackCache<void>} callback signals when the value is stored
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	store(identifier, etag, data, callback) {
 | 
						|
		this._cache.store(`${this._name}|${identifier}`, etag, data, callback);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @param {T} data the value to store
 | 
						|
	 * @returns {Promise<void>} promise signals when the value is stored
 | 
						|
	 */
 | 
						|
	storePromise(identifier, etag, data) {
 | 
						|
		return new Promise((resolve, reject) => {
 | 
						|
			this._cache.store(`${this._name}|${identifier}`, etag, data, err => {
 | 
						|
				if (err) {
 | 
						|
					reject(err);
 | 
						|
				} else {
 | 
						|
					resolve();
 | 
						|
				}
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @param {function(CallbackNormalErrorCache<T>): void} computer function to compute the value if not cached
 | 
						|
	 * @param {CallbackNormalErrorCache<T>} callback signals when the value is retrieved
 | 
						|
	 * @returns {void}
 | 
						|
	 */
 | 
						|
	provide(identifier, etag, computer, callback) {
 | 
						|
		this.get(identifier, etag, (err, cacheEntry) => {
 | 
						|
			if (err) return callback(err);
 | 
						|
			if (cacheEntry !== undefined) return cacheEntry;
 | 
						|
			computer((err, result) => {
 | 
						|
				if (err) return callback(err);
 | 
						|
				this.store(identifier, etag, result, err => {
 | 
						|
					if (err) return callback(err);
 | 
						|
					callback(null, result);
 | 
						|
				});
 | 
						|
			});
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	 * @template T
 | 
						|
	 * @param {string} identifier the cache identifier
 | 
						|
	 * @param {Etag | null} etag the etag
 | 
						|
	 * @param {function(): Promise<T> | T} computer function to compute the value if not cached
 | 
						|
	 * @returns {Promise<T>} promise with the data
 | 
						|
	 */
 | 
						|
	async providePromise(identifier, etag, computer) {
 | 
						|
		const cacheEntry = await this.getPromise(identifier, etag);
 | 
						|
		if (cacheEntry !== undefined) return cacheEntry;
 | 
						|
		const result = await computer();
 | 
						|
		await this.storePromise(identifier, etag, result);
 | 
						|
		return result;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
module.exports = CacheFacade;
 | 
						|
module.exports.ItemCacheFacade = ItemCacheFacade;
 | 
						|
module.exports.MultiItemCache = MultiItemCache;
 |