mirror of https://github.com/webpack/webpack.git
				
				
				
			use the full hash on modules that need the full hash of the compilation
use getFullHash runtime module for updating the hash in HMR
This commit is contained in:
		
							parent
							
								
									54a90430a2
								
							
						
					
					
						commit
						50ec72a6ca
					
				|  | @ -115,8 +115,10 @@ class APIPlugin { | ||||||
| 
 | 
 | ||||||
| 				compilation.hooks.runtimeRequirementInTree | 				compilation.hooks.runtimeRequirementInTree | ||||||
| 					.for(RuntimeGlobals.getFullHash) | 					.for(RuntimeGlobals.getFullHash) | ||||||
| 					.tap("APIPlugin", chunk => { | 					.tap("APIPlugin", (chunk, set) => { | ||||||
| 						compilation.addRuntimeModule(chunk, new GetFullHashRuntimeModule()); | 						const module = new GetFullHashRuntimeModule(); | ||||||
|  | 						compilation.addRuntimeModule(chunk, module); | ||||||
|  | 						compilation.chunkGraph.addFullHashModuleToChunk(chunk, module); | ||||||
| 						return true; | 						return true; | ||||||
| 					}); | 					}); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -184,6 +184,8 @@ class ChunkGraphChunk { | ||||||
| 		this.entryModules = new Map(); | 		this.entryModules = new Map(); | ||||||
| 		/** @type {SortableSet<RuntimeModule>} */ | 		/** @type {SortableSet<RuntimeModule>} */ | ||||||
| 		this.runtimeModules = new SortableSet(); | 		this.runtimeModules = new SortableSet(); | ||||||
|  | 		/** @type {Set<RuntimeModule> | undefined} */ | ||||||
|  | 		this.fullHashModules = undefined; | ||||||
| 		/** @type {Set<string> | undefined} */ | 		/** @type {Set<string> | undefined} */ | ||||||
| 		this.runtimeRequirements = undefined; | 		this.runtimeRequirements = undefined; | ||||||
| 		/** @type {Set<string>} */ | 		/** @type {Set<string>} */ | ||||||
|  | @ -346,6 +348,19 @@ class ChunkGraph { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {Chunk} chunk the chunk | ||||||
|  | 	 * @param {Iterable<RuntimeModule>} modules the modules that require a full hash | ||||||
|  | 	 * @returns {void} | ||||||
|  | 	 */ | ||||||
|  | 	attachFullHashModules(chunk, modules) { | ||||||
|  | 		const cgc = this._getChunkGraphChunk(chunk); | ||||||
|  | 		if (cgc.fullHashModules === undefined) cgc.fullHashModules = new Set(); | ||||||
|  | 		for (const module of modules) { | ||||||
|  | 			cgc.fullHashModules.add(module); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {Module} oldModule the replaced module | 	 * @param {Module} oldModule the replaced module | ||||||
| 	 * @param {Module} newModule the replacing module | 	 * @param {Module} newModule the replacing module | ||||||
|  | @ -394,6 +409,13 @@ class ChunkGraph { | ||||||
| 				cgc.runtimeModules.delete(/** @type {RuntimeModule} */ (oldModule)); | 				cgc.runtimeModules.delete(/** @type {RuntimeModule} */ (oldModule)); | ||||||
| 				cgc.runtimeModules.add(/** @type {RuntimeModule} */ (newModule)); | 				cgc.runtimeModules.add(/** @type {RuntimeModule} */ (newModule)); | ||||||
| 				newCgm.runtimeInChunks.add(chunk); | 				newCgm.runtimeInChunks.add(chunk); | ||||||
|  | 				if ( | ||||||
|  | 					cgc.fullHashModules !== undefined && | ||||||
|  | 					cgc.fullHashModules.has(/** @type {RuntimeModule} */ (oldModule)) | ||||||
|  | 				) { | ||||||
|  | 					cgc.fullHashModules.delete(/** @type {RuntimeModule} */ (oldModule)); | ||||||
|  | 					cgc.fullHashModules.add(/** @type {RuntimeModule} */ (newModule)); | ||||||
|  | 				} | ||||||
| 			} | 			} | ||||||
| 			oldCgm.runtimeInChunks = undefined; | 			oldCgm.runtimeInChunks = undefined; | ||||||
| 		} | 		} | ||||||
|  | @ -886,6 +908,17 @@ class ChunkGraph { | ||||||
| 		cgc.runtimeModules.add(module); | 		cgc.runtimeModules.add(module); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {Chunk} chunk the new chunk | ||||||
|  | 	 * @param {RuntimeModule} module the module that require a full hash | ||||||
|  | 	 * @returns {void} | ||||||
|  | 	 */ | ||||||
|  | 	addFullHashModuleToChunk(chunk, module) { | ||||||
|  | 		const cgc = this._getChunkGraphChunk(chunk); | ||||||
|  | 		if (cgc.fullHashModules === undefined) cgc.fullHashModules = new Set(); | ||||||
|  | 		cgc.fullHashModules.add(module); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * @param {Chunk} chunk the new chunk | 	 * @param {Chunk} chunk the new chunk | ||||||
| 	 * @param {Module} module the entry module | 	 * @param {Module} module the entry module | ||||||
|  | @ -1038,6 +1071,15 @@ class ChunkGraph { | ||||||
| 		return array; | 		return array; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * @param {Chunk} chunk the chunk | ||||||
|  | 	 * @returns {Iterable<RuntimeModule> | undefined} iterable of modules (do not modify) | ||||||
|  | 	 */ | ||||||
|  | 	getChunkFullHashModulesIterable(chunk) { | ||||||
|  | 		const cgc = this._getChunkGraphChunk(chunk); | ||||||
|  | 		return cgc.fullHashModules; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	/** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */ | 	/** @typedef {[Module, Entrypoint | undefined]} EntryModuleWithChunkGroup */ | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -2316,6 +2316,7 @@ class Compilation { | ||||||
| 			return byId(a, b); | 			return byId(a, b); | ||||||
| 		}); | 		}); | ||||||
| 		this.logger.timeEnd("hashing: sort chunks"); | 		this.logger.timeEnd("hashing: sort chunks"); | ||||||
|  | 		const fullHashChunks = new Set(); | ||||||
| 		for (let i = 0; i < chunks.length; i++) { | 		for (let i = 0; i < chunks.length; i++) { | ||||||
| 			const chunk = chunks[i]; | 			const chunk = chunks[i]; | ||||||
| 			// Last minute module hash generation for modules that depend on chunk hashes
 | 			// Last minute module hash generation for modules that depend on chunk hashes
 | ||||||
|  | @ -2347,10 +2348,20 @@ class Compilation { | ||||||
| 					moduleGraph: this.moduleGraph, | 					moduleGraph: this.moduleGraph, | ||||||
| 					runtimeTemplate: this.runtimeTemplate | 					runtimeTemplate: this.runtimeTemplate | ||||||
| 				}); | 				}); | ||||||
| 				chunk.hash = /** @type {string} */ (chunkHash.digest(hashDigest)); | 				const chunkHashDigest = /** @type {string} */ (chunkHash.digest( | ||||||
| 				hash.update(chunk.hash); | 					hashDigest | ||||||
|  | 				)); | ||||||
|  | 				hash.update(chunkHashDigest); | ||||||
|  | 				chunk.hash = chunkHashDigest; | ||||||
| 				chunk.renderedHash = chunk.hash.substr(0, hashDigestLength); | 				chunk.renderedHash = chunk.hash.substr(0, hashDigestLength); | ||||||
| 				this.hooks.contentHash.call(chunk); | 				const fullHashModules = chunkGraph.getChunkFullHashModulesIterable( | ||||||
|  | 					chunk | ||||||
|  | 				); | ||||||
|  | 				if (fullHashModules) { | ||||||
|  | 					fullHashChunks.add(chunk); | ||||||
|  | 				} else { | ||||||
|  | 					this.hooks.contentHash.call(chunk); | ||||||
|  | 				} | ||||||
| 			} catch (err) { | 			} catch (err) { | ||||||
| 				this.errors.push(new ChunkRenderError(chunk, "", err)); | 				this.errors.push(new ChunkRenderError(chunk, "", err)); | ||||||
| 			} | 			} | ||||||
|  | @ -2362,6 +2373,32 @@ class Compilation { | ||||||
| 		this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); | 		this.fullHash = /** @type {string} */ (hash.digest(hashDigest)); | ||||||
| 		this.hash = this.fullHash.substr(0, hashDigestLength); | 		this.hash = this.fullHash.substr(0, hashDigestLength); | ||||||
| 		this.logger.timeEnd("hashing: hash digest"); | 		this.logger.timeEnd("hashing: hash digest"); | ||||||
|  | 
 | ||||||
|  | 		this.logger.time("hashing: process full hash modules"); | ||||||
|  | 		for (const chunk of fullHashChunks) { | ||||||
|  | 			for (const module of chunkGraph.getChunkFullHashModulesIterable(chunk)) { | ||||||
|  | 				const moduleHash = createHash(hashFunction); | ||||||
|  | 				module.updateHash(moduleHash, chunkGraph); | ||||||
|  | 				const moduleHashDigest = /** @type {string} */ (moduleHash.digest( | ||||||
|  | 					hashDigest | ||||||
|  | 				)); | ||||||
|  | 				chunkGraph.setModuleHashes( | ||||||
|  | 					module, | ||||||
|  | 					moduleHashDigest, | ||||||
|  | 					moduleHashDigest.substr(0, hashDigestLength) | ||||||
|  | 				); | ||||||
|  | 			} | ||||||
|  | 			const chunkHash = createHash(hashFunction); | ||||||
|  | 			chunkHash.update(chunk.hash); | ||||||
|  | 			chunkHash.update(this.hash); | ||||||
|  | 			const chunkHashDigest = /** @type {string} */ (chunkHash.digest( | ||||||
|  | 				hashDigest | ||||||
|  | 			)); | ||||||
|  | 			chunk.hash = chunkHashDigest; | ||||||
|  | 			chunk.renderedHash = chunk.hash.substr(0, hashDigestLength); | ||||||
|  | 			this.hooks.contentHash.call(chunk); | ||||||
|  | 		} | ||||||
|  | 		this.logger.timeEnd("hashing: process full hash modules"); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
|  |  | ||||||
|  | @ -301,7 +301,6 @@ class HotModuleReplacementPlugin { | ||||||
| 							} | 							} | ||||||
| 						} | 						} | ||||||
| 						const hotUpdateMainContent = { | 						const hotUpdateMainContent = { | ||||||
| 							h: compilation.hash, |  | ||||||
| 							c: [], | 							c: [], | ||||||
| 							r: [], | 							r: [], | ||||||
| 							m: undefined | 							m: undefined | ||||||
|  | @ -320,6 +319,14 @@ class HotModuleReplacementPlugin { | ||||||
| 								const newRuntimeModules = Array.from( | 								const newRuntimeModules = Array.from( | ||||||
| 									chunkGraph.getChunkRuntimeModulesIterable(currentChunk) | 									chunkGraph.getChunkRuntimeModulesIterable(currentChunk) | ||||||
| 								).filter(module => updatedModules.has(module)); | 								).filter(module => updatedModules.has(module)); | ||||||
|  | 								const fullHashModules = chunkGraph.getChunkFullHashModulesIterable( | ||||||
|  | 									currentChunk | ||||||
|  | 								); | ||||||
|  | 								const newFullHashModules = | ||||||
|  | 									fullHashModules && | ||||||
|  | 									Array.from(fullHashModules).filter(module => | ||||||
|  | 										updatedModules.has(module) | ||||||
|  | 									); | ||||||
| 								/** @type {Set<number|string>} */ | 								/** @type {Set<number|string>} */ | ||||||
| 								const allModules = new Set(); | 								const allModules = new Set(); | ||||||
| 								for (const module of chunkGraph.getChunkModulesIterable( | 								for (const module of chunkGraph.getChunkModulesIterable( | ||||||
|  | @ -339,6 +346,12 @@ class HotModuleReplacementPlugin { | ||||||
| 										hotUpdateChunk, | 										hotUpdateChunk, | ||||||
| 										newRuntimeModules | 										newRuntimeModules | ||||||
| 									); | 									); | ||||||
|  | 									if (newFullHashModules) { | ||||||
|  | 										chunkGraph.attachFullHashModules( | ||||||
|  | 											hotUpdateChunk, | ||||||
|  | 											newRuntimeModules | ||||||
|  | 										); | ||||||
|  | 									} | ||||||
| 									hotUpdateChunk.removedModules = removedModules; | 									hotUpdateChunk.removedModules = removedModules; | ||||||
| 									const renderManifest = compilation.getRenderManifest({ | 									const renderManifest = compilation.getRenderManifest({ | ||||||
| 										chunk: hotUpdateChunk, | 										chunk: hotUpdateChunk, | ||||||
|  | @ -401,7 +414,6 @@ class HotModuleReplacementPlugin { | ||||||
| 					(chunk, runtimeRequirements) => { | 					(chunk, runtimeRequirements) => { | ||||||
| 						runtimeRequirements.add(RuntimeGlobals.hmrDownloadManifest); | 						runtimeRequirements.add(RuntimeGlobals.hmrDownloadManifest); | ||||||
| 						runtimeRequirements.add(RuntimeGlobals.hmrDownloadUpdateHandlers); | 						runtimeRequirements.add(RuntimeGlobals.hmrDownloadUpdateHandlers); | ||||||
| 						runtimeRequirements.add(RuntimeGlobals.getFullHash); |  | ||||||
| 						runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution); | 						runtimeRequirements.add(RuntimeGlobals.interceptModuleExecution); | ||||||
| 						runtimeRequirements.add(RuntimeGlobals.moduleCache); | 						runtimeRequirements.add(RuntimeGlobals.moduleCache); | ||||||
| 						compilation.addRuntimeModule( | 						compilation.addRuntimeModule( | ||||||
|  |  | ||||||
|  | @ -5,7 +5,6 @@ | ||||||
| 
 | 
 | ||||||
| "use strict"; | "use strict"; | ||||||
| 
 | 
 | ||||||
| var $getFullHash$ = undefined; |  | ||||||
| var $interceptModuleExecution$ = undefined; | var $interceptModuleExecution$ = undefined; | ||||||
| var $moduleCache$ = undefined; | var $moduleCache$ = undefined; | ||||||
| // eslint-disable-next-line no-unused-vars
 | // eslint-disable-next-line no-unused-vars
 | ||||||
|  | @ -16,7 +15,6 @@ var $hmrInvalidateModuleHandlers$ = undefined; | ||||||
| var __webpack_require__ = undefined; | var __webpack_require__ = undefined; | ||||||
| 
 | 
 | ||||||
| module.exports = function () { | module.exports = function () { | ||||||
| 	var currentHash = $getFullHash$(); |  | ||||||
| 	var currentModuleData = {}; | 	var currentModuleData = {}; | ||||||
| 	var installedModules = $moduleCache$; | 	var installedModules = $moduleCache$; | ||||||
| 
 | 
 | ||||||
|  | @ -33,15 +31,10 @@ module.exports = function () { | ||||||
| 
 | 
 | ||||||
| 	// The update info
 | 	// The update info
 | ||||||
| 	var currentUpdateApplyHandlers; | 	var currentUpdateApplyHandlers; | ||||||
| 	var currentUpdateNewHash; |  | ||||||
| 	var queuedInvalidatedModules; | 	var queuedInvalidatedModules; | ||||||
| 
 | 
 | ||||||
| 	$hmrModuleData$ = currentModuleData; | 	$hmrModuleData$ = currentModuleData; | ||||||
| 
 | 
 | ||||||
| 	$getFullHash$ = function () { |  | ||||||
| 		return currentHash; |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	$interceptModuleExecution$.push(function (options) { | 	$interceptModuleExecution$.push(function (options) { | ||||||
| 		var module = options.module; | 		var module = options.module; | ||||||
| 		var require = createRequire(options.require, options.id); | 		var require = createRequire(options.require, options.id); | ||||||
|  | @ -251,7 +244,6 @@ module.exports = function () { | ||||||
| 
 | 
 | ||||||
| 			setStatus("prepare"); | 			setStatus("prepare"); | ||||||
| 
 | 
 | ||||||
| 			currentUpdateNewHash = update.h; |  | ||||||
| 			var updatedModules = []; | 			var updatedModules = []; | ||||||
| 			blockingPromises = []; | 			blockingPromises = []; | ||||||
| 			currentUpdateApplyHandlers = []; | 			currentUpdateApplyHandlers = []; | ||||||
|  | @ -328,11 +320,6 @@ module.exports = function () { | ||||||
| 		// Now in "apply" phase
 | 		// Now in "apply" phase
 | ||||||
| 		setStatus("apply"); | 		setStatus("apply"); | ||||||
| 
 | 
 | ||||||
| 		if (currentUpdateNewHash !== undefined) { |  | ||||||
| 			currentHash = currentUpdateNewHash; |  | ||||||
| 			currentUpdateNewHash = undefined; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		var error; | 		var error; | ||||||
| 		var reportError = function (err) { | 		var reportError = function (err) { | ||||||
| 			if (!error) error = err; | 			if (!error) error = err; | ||||||
|  |  | ||||||
|  | @ -337,6 +337,16 @@ class JavascriptModulesPlugin { | ||||||
| 							hash.update(chunkGraph.getModuleHash(m)); | 							hash.update(chunkGraph.getModuleHash(m)); | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
|  | 					const runtimeModules = chunkGraph.getOrderedChunkModulesIterableBySourceType( | ||||||
|  | 						chunk, | ||||||
|  | 						"runtime", | ||||||
|  | 						compareModulesByIdentifier | ||||||
|  | 					); | ||||||
|  | 					if (runtimeModules) { | ||||||
|  | 						for (const m of runtimeModules) { | ||||||
|  | 							hash.update(chunkGraph.getModuleHash(m)); | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
| 					if (hotUpdateChunk) { | 					if (hotUpdateChunk) { | ||||||
| 						hash.update(JSON.stringify(hotUpdateChunk.removedModules)); | 						hash.update(JSON.stringify(hotUpdateChunk.removedModules)); | ||||||
| 					} | 					} | ||||||
|  |  | ||||||
|  | @ -0,0 +1,21 @@ | ||||||
|  | import "./module"; | ||||||
|  | 
 | ||||||
|  | const getFile = name => | ||||||
|  | 	__non_webpack_require__("fs").readFileSync( | ||||||
|  | 		__non_webpack_require__("path").join(__dirname, name), | ||||||
|  | 		"utf-8" | ||||||
|  | 	); | ||||||
|  | 
 | ||||||
|  | it("should generate the main file and change full hash on update", done => { | ||||||
|  | 	const hash1 = __webpack_hash__; | ||||||
|  | 	expect(getFile("bundle.js")).toContain(hash1); | ||||||
|  | 	module.hot.accept("./module", () => { | ||||||
|  | 		const hash2 = __webpack_hash__; | ||||||
|  | 		expect(hash1).toBeTypeOf("string"); | ||||||
|  | 		expect(hash2).not.toBe(hash1); | ||||||
|  | 		expect(getFile("bundle.js")).toContain(hash2); | ||||||
|  | 		expect(getFile("bundle.js")).not.toContain(hash1); | ||||||
|  | 		done(); | ||||||
|  | 	}); | ||||||
|  | 	NEXT(require("../../update")(done)); | ||||||
|  | }); | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | import("./thing"); | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | export default 1; | ||||||
|  | --- | ||||||
|  | export default 2; | ||||||
|  | @ -0,0 +1,5 @@ | ||||||
|  | module.exports = { | ||||||
|  | 	node: { | ||||||
|  | 		__dirname: false | ||||||
|  | 	} | ||||||
|  | }; | ||||||
		Loading…
	
		Reference in New Issue