| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | const { SyncBailHook } = require("tapable"); | 
					
						
							| 
									
										
										
										
											2020-08-20 15:54:02 +08:00
										 |  |  | const { RawSource, CachedSource, CompatSource } = require("webpack-sources"); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | const Compilation = require("../Compilation"); | 
					
						
							| 
									
										
										
										
											2020-10-09 15:14:34 +08:00
										 |  |  | const WebpackError = require("../WebpackError"); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | const { compareSelect, compareStrings } = require("../util/comparators"); | 
					
						
							|  |  |  | const createHash = require("../util/createHash"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | /** @typedef {import("webpack-sources").Source} Source */ | 
					
						
							|  |  |  | /** @typedef {import("../Compilation").AssetInfo} AssetInfo */ | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | /** @typedef {import("../Compiler")} Compiler */ | 
					
						
							| 
									
										
										
										
											2023-04-12 02:57:43 +08:00
										 |  |  | /** @typedef {typeof import("../util/Hash")} Hash */ | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | const EMPTY_SET = new Set(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | const addToList = (itemOrItems, list) => { | 
					
						
							|  |  |  | 	if (Array.isArray(itemOrItems)) { | 
					
						
							|  |  |  | 		for (const item of itemOrItems) { | 
					
						
							|  |  |  | 			list.add(item); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (itemOrItems) { | 
					
						
							|  |  |  | 		list.add(itemOrItems); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-01 16:51:46 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @template T | 
					
						
							|  |  |  |  * @param {T[]} input list | 
					
						
							|  |  |  |  * @param {function(T): Buffer} fn map function | 
					
						
							|  |  |  |  * @returns {Buffer[]} buffers without duplicates | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const mapAndDeduplicateBuffers = (input, fn) => { | 
					
						
							|  |  |  | 	// Buffer.equals compares size first so this should be efficient enough
 | 
					
						
							|  |  |  | 	// If it becomes a performance problem we can use a map and group by size
 | 
					
						
							|  |  |  | 	// instead of looping over all assets.
 | 
					
						
							|  |  |  | 	const result = []; | 
					
						
							|  |  |  | 	outer: for (const value of input) { | 
					
						
							|  |  |  | 		const buf = fn(value); | 
					
						
							|  |  |  | 		for (const other of result) { | 
					
						
							|  |  |  | 			if (buf.equals(other)) continue outer; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		result.push(buf); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Escapes regular expression metacharacters | 
					
						
							|  |  |  |  * @param {string} str String to quote | 
					
						
							|  |  |  |  * @returns {string} Escaped string | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const quoteMeta = str => { | 
					
						
							|  |  |  | 	return str.replace(/[-[\]\\/{}()*+?.^$|]/g, "\\$&"); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-20 15:54:02 +08:00
										 |  |  | const cachedSourceMap = new WeakMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const toCachedSource = source => { | 
					
						
							|  |  |  | 	if (source instanceof CachedSource) { | 
					
						
							|  |  |  | 		return source; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	const entry = cachedSourceMap.get(source); | 
					
						
							|  |  |  | 	if (entry !== undefined) return entry; | 
					
						
							|  |  |  | 	const newSource = new CachedSource(CompatSource.from(source)); | 
					
						
							|  |  |  | 	cachedSourceMap.set(source, newSource); | 
					
						
							|  |  |  | 	return newSource; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} AssetInfoForRealContentHash | 
					
						
							|  |  |  |  * @property {string} name | 
					
						
							|  |  |  |  * @property {AssetInfo} info | 
					
						
							|  |  |  |  * @property {Source} source | 
					
						
							|  |  |  |  * @property {RawSource | undefined} newSource | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  |  * @property {RawSource | undefined} newSourceWithoutOwn | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  |  * @property {string} content | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  |  * @property {Set<string>} ownHashes | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  |  * @property {Promise} contentComputePromise | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  |  * @property {Promise} contentComputeWithoutOwnPromise | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  |  * @property {Set<string>} referencedHashes | 
					
						
							|  |  |  |  * @property {Set<string>} hashes | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} CompilationHooks | 
					
						
							|  |  |  |  * @property {SyncBailHook<[Buffer[], string], string>} updateHash | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** @type {WeakMap<Compilation, CompilationHooks>} */ | 
					
						
							|  |  |  | const compilationHooksMap = new WeakMap(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | class RealContentHashPlugin { | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compilation} compilation the compilation | 
					
						
							|  |  |  | 	 * @returns {CompilationHooks} the attached hooks | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	static getCompilationHooks(compilation) { | 
					
						
							|  |  |  | 		if (!(compilation instanceof Compilation)) { | 
					
						
							|  |  |  | 			throw new TypeError( | 
					
						
							|  |  |  | 				"The 'compilation' argument must be an instance of Compilation" | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		let hooks = compilationHooksMap.get(compilation); | 
					
						
							|  |  |  | 		if (hooks === undefined) { | 
					
						
							|  |  |  | 			hooks = { | 
					
						
							|  |  |  | 				updateHash: new SyncBailHook(["content", "oldHash"]) | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			compilationHooksMap.set(compilation, hooks); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return hooks; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-04-12 02:57:43 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Object} options options object | 
					
						
							|  |  |  | 	 * @param {string | Hash} options.hashFunction the hash function to use | 
					
						
							|  |  |  | 	 * @param {string} options.hashDigest the hash digest to use | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 	constructor({ hashFunction, hashDigest }) { | 
					
						
							|  |  |  | 		this._hashFunction = hashFunction; | 
					
						
							|  |  |  | 		this._hashDigest = hashDigest; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * Apply the plugin | 
					
						
							|  |  |  | 	 * @param {Compiler} compiler the compiler instance | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	apply(compiler) { | 
					
						
							|  |  |  | 		compiler.hooks.compilation.tap("RealContentHashPlugin", compilation => { | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 			const cacheAnalyse = compilation.getCache( | 
					
						
							|  |  |  | 				"RealContentHashPlugin|analyse" | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			const cacheGenerate = compilation.getCache( | 
					
						
							|  |  |  | 				"RealContentHashPlugin|generate" | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 			const hooks = RealContentHashPlugin.getCompilationHooks(compilation); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 			compilation.hooks.processAssets.tapPromise( | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 				{ | 
					
						
							|  |  |  | 					name: "RealContentHashPlugin", | 
					
						
							|  |  |  | 					stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_HASH | 
					
						
							|  |  |  | 				}, | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 				async () => { | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					const assets = compilation.getAssets(); | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 					/** @type {AssetInfoForRealContentHash[]} */ | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					const assetsWithInfo = []; | 
					
						
							|  |  |  | 					const hashToAssets = new Map(); | 
					
						
							|  |  |  | 					for (const { source, info, name } of assets) { | 
					
						
							| 
									
										
										
										
											2020-08-20 15:54:02 +08:00
										 |  |  | 						const cachedSource = toCachedSource(source); | 
					
						
							|  |  |  | 						const content = cachedSource.source(); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 						/** @type {Set<string>} */ | 
					
						
							|  |  |  | 						const hashes = new Set(); | 
					
						
							|  |  |  | 						addToList(info.contenthash, hashes); | 
					
						
							|  |  |  | 						const data = { | 
					
						
							|  |  |  | 							name, | 
					
						
							|  |  |  | 							info, | 
					
						
							| 
									
										
										
										
											2020-08-20 15:54:02 +08:00
										 |  |  | 							source: cachedSource, | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							/** @type {RawSource | undefined} */ | 
					
						
							| 
									
										
										
										
											2020-08-19 15:46:41 +08:00
										 |  |  | 							newSource: undefined, | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 							/** @type {RawSource | undefined} */ | 
					
						
							|  |  |  | 							newSourceWithoutOwn: undefined, | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 							content, | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 							/** @type {Set<string>} */ | 
					
						
							|  |  |  | 							ownHashes: undefined, | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 							contentComputePromise: undefined, | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 							contentComputeWithoutOwnPromise: undefined, | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							/** @type {Set<string>} */ | 
					
						
							|  |  |  | 							referencedHashes: undefined, | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 							hashes | 
					
						
							|  |  |  | 						}; | 
					
						
							|  |  |  | 						assetsWithInfo.push(data); | 
					
						
							|  |  |  | 						for (const hash of hashes) { | 
					
						
							|  |  |  | 							const list = hashToAssets.get(hash); | 
					
						
							|  |  |  | 							if (list === undefined) { | 
					
						
							|  |  |  | 								hashToAssets.set(hash, [data]); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								list.push(data); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-19 17:37:17 +08:00
										 |  |  | 					if (hashToAssets.size === 0) return; | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 					const hashRegExp = new RegExp( | 
					
						
							|  |  |  | 						Array.from(hashToAssets.keys(), quoteMeta).join("|"), | 
					
						
							|  |  |  | 						"g" | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 					await Promise.all( | 
					
						
							|  |  |  | 						assetsWithInfo.map(async asset => { | 
					
						
							|  |  |  | 							const { name, source, content, hashes } = asset; | 
					
						
							|  |  |  | 							if (Buffer.isBuffer(content)) { | 
					
						
							|  |  |  | 								asset.referencedHashes = EMPTY_SET; | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 								asset.ownHashes = EMPTY_SET; | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 								return; | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							const etag = cacheAnalyse.mergeEtags( | 
					
						
							|  |  |  | 								cacheAnalyse.getLazyHashedEtag(source), | 
					
						
							|  |  |  | 								Array.from(hashes).join("|") | 
					
						
							|  |  |  | 							); | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 							[asset.referencedHashes, asset.ownHashes] = | 
					
						
							|  |  |  | 								await cacheAnalyse.providePromise(name, etag, () => { | 
					
						
							|  |  |  | 									const referencedHashes = new Set(); | 
					
						
							|  |  |  | 									let ownHashes = new Set(); | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 									const inContent = content.match(hashRegExp); | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 									if (inContent) { | 
					
						
							|  |  |  | 										for (const hash of inContent) { | 
					
						
							|  |  |  | 											if (hashes.has(hash)) { | 
					
						
							|  |  |  | 												ownHashes.add(hash); | 
					
						
							|  |  |  | 												continue; | 
					
						
							|  |  |  | 											} | 
					
						
							|  |  |  | 											referencedHashes.add(hash); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 										} | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 									return [referencedHashes, ownHashes]; | 
					
						
							|  |  |  | 								}); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 						}) | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					const getDependencies = hash => { | 
					
						
							|  |  |  | 						const assets = hashToAssets.get(hash); | 
					
						
							| 
									
										
										
										
											2020-10-09 15:14:34 +08:00
										 |  |  | 						if (!assets) { | 
					
						
							|  |  |  | 							const referencingAssets = assetsWithInfo.filter(asset => | 
					
						
							|  |  |  | 								asset.referencedHashes.has(hash) | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 							const err = new WebpackError(`RealContentHashPlugin
 | 
					
						
							|  |  |  | Some kind of unexpected caching problem occurred. | 
					
						
							|  |  |  | An asset was cached with a reference to another asset (${hash}) that's not in the compilation anymore. | 
					
						
							|  |  |  | Either the asset was incorrectly cached, or the referenced asset should also be restored from cache. | 
					
						
							|  |  |  | Referenced by: | 
					
						
							|  |  |  | ${referencingAssets | 
					
						
							|  |  |  | 	.map(a => { | 
					
						
							|  |  |  | 		const match = new RegExp(`.{0,20}${quoteMeta(hash)}.{0,20}`).exec( | 
					
						
							|  |  |  | 			a.content | 
					
						
							|  |  |  | 		); | 
					
						
							|  |  |  | 		return ` - ${a.name}: ...${match ? match[0] : "???"}...`; | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	.join("\n")}`);
 | 
					
						
							|  |  |  | 							compilation.errors.push(err); | 
					
						
							|  |  |  | 							return undefined; | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 						const hashes = new Set(); | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 						for (const { referencedHashes, ownHashes } of assets) { | 
					
						
							|  |  |  | 							if (!ownHashes.has(hash)) { | 
					
						
							|  |  |  | 								for (const hash of ownHashes) { | 
					
						
							|  |  |  | 									hashes.add(hash); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 							for (const hash of referencedHashes) { | 
					
						
							|  |  |  | 								hashes.add(hash); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return hashes; | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					const hashInfo = hash => { | 
					
						
							|  |  |  | 						const assets = hashToAssets.get(hash); | 
					
						
							|  |  |  | 						return `${hash} (${Array.from(assets, a => a.name)})`; | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					const hashesInOrder = new Set(); | 
					
						
							|  |  |  | 					for (const hash of hashToAssets.keys()) { | 
					
						
							|  |  |  | 						const add = (hash, stack) => { | 
					
						
							|  |  |  | 							const deps = getDependencies(hash); | 
					
						
							| 
									
										
										
										
											2020-10-09 15:14:34 +08:00
										 |  |  | 							if (!deps) return; | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 							stack.add(hash); | 
					
						
							|  |  |  | 							for (const dep of deps) { | 
					
						
							|  |  |  | 								if (hashesInOrder.has(dep)) continue; | 
					
						
							|  |  |  | 								if (stack.has(dep)) { | 
					
						
							|  |  |  | 									throw new Error( | 
					
						
							|  |  |  | 										`Circular hash dependency ${Array.from( | 
					
						
							|  |  |  | 											stack, | 
					
						
							|  |  |  | 											hashInfo | 
					
						
							|  |  |  | 										).join(" -> ")} -> ${hashInfo(dep)}`
 | 
					
						
							|  |  |  | 									); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 								add(dep, stack); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							hashesInOrder.add(hash); | 
					
						
							|  |  |  | 							stack.delete(hash); | 
					
						
							|  |  |  | 						}; | 
					
						
							|  |  |  | 						if (hashesInOrder.has(hash)) continue; | 
					
						
							|  |  |  | 						add(hash, new Set()); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					const hashToNewHash = new Map(); | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 					const getEtag = asset => | 
					
						
							|  |  |  | 						cacheGenerate.mergeEtags( | 
					
						
							|  |  |  | 							cacheGenerate.getLazyHashedEtag(asset.source), | 
					
						
							|  |  |  | 							Array.from(asset.referencedHashes, hash => | 
					
						
							|  |  |  | 								hashToNewHash.get(hash) | 
					
						
							|  |  |  | 							).join("|") | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 					const computeNewContent = asset => { | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 						if (asset.contentComputePromise) return asset.contentComputePromise; | 
					
						
							|  |  |  | 						return (asset.contentComputePromise = (async () => { | 
					
						
							|  |  |  | 							if ( | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 								asset.ownHashes.size > 0 || | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 								Array.from(asset.referencedHashes).some( | 
					
						
							|  |  |  | 									hash => hashToNewHash.get(hash) !== hash | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 							) { | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 								const identifier = asset.name; | 
					
						
							|  |  |  | 								const etag = getEtag(asset); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 								asset.newSource = await cacheGenerate.providePromise( | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 									identifier, | 
					
						
							|  |  |  | 									etag, | 
					
						
							|  |  |  | 									() => { | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 										const newContent = asset.content.replace(hashRegExp, hash => | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 											hashToNewHash.get(hash) | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 										return new RawSource(newContent); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						})()); | 
					
						
							|  |  |  | 					}; | 
					
						
							|  |  |  | 					const computeNewContentWithoutOwn = asset => { | 
					
						
							|  |  |  | 						if (asset.contentComputeWithoutOwnPromise) | 
					
						
							|  |  |  | 							return asset.contentComputeWithoutOwnPromise; | 
					
						
							|  |  |  | 						return (asset.contentComputeWithoutOwnPromise = (async () => { | 
					
						
							|  |  |  | 							if ( | 
					
						
							|  |  |  | 								asset.ownHashes.size > 0 || | 
					
						
							|  |  |  | 								Array.from(asset.referencedHashes).some( | 
					
						
							|  |  |  | 									hash => hashToNewHash.get(hash) !== hash | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 							) { | 
					
						
							|  |  |  | 								const identifier = asset.name + "|without-own"; | 
					
						
							|  |  |  | 								const etag = getEtag(asset); | 
					
						
							|  |  |  | 								asset.newSourceWithoutOwn = await cacheGenerate.providePromise( | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 									identifier, | 
					
						
							|  |  |  | 									etag, | 
					
						
							|  |  |  | 									() => { | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 										const newContent = asset.content.replace( | 
					
						
							|  |  |  | 											hashRegExp, | 
					
						
							|  |  |  | 											hash => { | 
					
						
							|  |  |  | 												if (asset.ownHashes.has(hash)) { | 
					
						
							|  |  |  | 													return ""; | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 												return hashToNewHash.get(hash); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 											} | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 										); | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 										return new RawSource(newContent); | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						})()); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					}; | 
					
						
							|  |  |  | 					const comparator = compareSelect(a => a.name, compareStrings); | 
					
						
							|  |  |  | 					for (const oldHash of hashesInOrder) { | 
					
						
							|  |  |  | 						const assets = hashToAssets.get(oldHash); | 
					
						
							|  |  |  | 						assets.sort(comparator); | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 						await Promise.all( | 
					
						
							|  |  |  | 							assets.map(asset => | 
					
						
							|  |  |  | 								asset.ownHashes.has(oldHash) | 
					
						
							|  |  |  | 									? computeNewContentWithoutOwn(asset) | 
					
						
							|  |  |  | 									: computeNewContent(asset) | 
					
						
							|  |  |  | 							) | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 						); | 
					
						
							| 
									
										
										
										
											2021-03-01 16:51:46 +08:00
										 |  |  | 						const assetsContent = mapAndDeduplicateBuffers(assets, asset => { | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 							if (asset.ownHashes.has(oldHash)) { | 
					
						
							|  |  |  | 								return asset.newSourceWithoutOwn | 
					
						
							|  |  |  | 									? asset.newSourceWithoutOwn.buffer() | 
					
						
							|  |  |  | 									: asset.source.buffer(); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								return asset.newSource | 
					
						
							|  |  |  | 									? asset.newSource.buffer() | 
					
						
							|  |  |  | 									: asset.source.buffer(); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						}); | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 						let newHash = hooks.updateHash.call(assetsContent, oldHash); | 
					
						
							|  |  |  | 						if (!newHash) { | 
					
						
							| 
									
										
										
										
											2022-11-19 02:08:49 +08:00
										 |  |  | 							const hash = createHash(this._hashFunction); | 
					
						
							| 
									
										
										
										
											2023-03-08 08:15:36 +08:00
										 |  |  | 							if (compilation.outputOptions.hashSalt) { | 
					
						
							|  |  |  | 								hash.update(compilation.outputOptions.hashSalt); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-11-09 18:58:18 +08:00
										 |  |  | 							for (const content of assetsContent) { | 
					
						
							|  |  |  | 								hash.update(content); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							const digest = hash.digest(this._hashDigest); | 
					
						
							|  |  |  | 							newHash = /** @type {string} */ (digest.slice(0, oldHash.length)); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-08-19 15:46:41 +08:00
										 |  |  | 						hashToNewHash.set(oldHash, newHash); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 					await Promise.all( | 
					
						
							|  |  |  | 						assetsWithInfo.map(async asset => { | 
					
						
							| 
									
										
										
										
											2020-11-11 21:01:29 +08:00
										 |  |  | 							await computeNewContent(asset); | 
					
						
							| 
									
										
										
										
											2023-03-10 23:04:26 +08:00
										 |  |  | 							const newName = asset.name.replace(hashRegExp, hash => | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 								hashToNewHash.get(hash) | 
					
						
							|  |  |  | 							); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							const infoUpdate = {}; | 
					
						
							|  |  |  | 							const hash = asset.info.contenthash; | 
					
						
							|  |  |  | 							infoUpdate.contenthash = Array.isArray(hash) | 
					
						
							|  |  |  | 								? hash.map(hash => hashToNewHash.get(hash)) | 
					
						
							|  |  |  | 								: hashToNewHash.get(hash); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							if (asset.newSource !== undefined) { | 
					
						
							|  |  |  | 								compilation.updateAsset( | 
					
						
							|  |  |  | 									asset.name, | 
					
						
							|  |  |  | 									asset.newSource, | 
					
						
							|  |  |  | 									infoUpdate | 
					
						
							|  |  |  | 								); | 
					
						
							|  |  |  | 							} else { | 
					
						
							|  |  |  | 								compilation.updateAsset(asset.name, asset.source, infoUpdate); | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-19 17:25:53 +08:00
										 |  |  | 							if (asset.name !== newName) { | 
					
						
							|  |  |  | 								compilation.renameAsset(asset.name, newName); | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2020-08-19 03:14:43 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = RealContentHashPlugin; |