| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** @typedef {import("enhanced-resolve/lib/Resolver")} Resolver */ | 
					
						
							|  |  |  | /** @typedef {import("../Compiler")} Compiler */ | 
					
						
							|  |  |  | /** @typedef {import("../FileSystemInfo")} FileSystemInfo */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-05 04:09:36 +08:00
										 |  |  | const requestToString = request => { | 
					
						
							|  |  |  | 	let str = ""; | 
					
						
							|  |  |  | 	for (const key in request) { | 
					
						
							|  |  |  | 		const value = request[key]; | 
					
						
							|  |  |  | 		if (typeof value === "object" && value !== null) { | 
					
						
							|  |  |  | 			str += `/${key}={${requestToString(value)}}`; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			str += `/${key}=${value}`; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return str; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | class ResolverCachePlugin { | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {Compiler} compiler Webpack compiler | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	apply(compiler) { | 
					
						
							|  |  |  | 		const cache = compiler.cache; | 
					
						
							|  |  |  | 		/** @type {FileSystemInfo} */ | 
					
						
							|  |  |  | 		let fileSystemInfo; | 
					
						
							|  |  |  | 		compiler.hooks.thisCompilation.tap("ResolverCachePlugin", compilation => { | 
					
						
							|  |  |  | 			fileSystemInfo = compilation.fileSystemInfo; | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		const doRealResolve = ( | 
					
						
							|  |  |  | 			identifier, | 
					
						
							|  |  |  | 			type, | 
					
						
							|  |  |  | 			resolver, | 
					
						
							|  |  |  | 			resolveContext, | 
					
						
							|  |  |  | 			request, | 
					
						
							|  |  |  | 			callback | 
					
						
							|  |  |  | 		) => { | 
					
						
							|  |  |  | 			const newRequest = Object.assign( | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					_ResolverCachePluginCacheMiss: true | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				request | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 			const newResolveContext = Object.assign({}, resolveContext, { | 
					
						
							|  |  |  | 				stack: new Set(), | 
					
						
							|  |  |  | 				missing: new Set(), | 
					
						
							|  |  |  | 				fileDependencies: new Set(), | 
					
						
							|  |  |  | 				contextDependencies: new Set() | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			const propagate = key => { | 
					
						
							|  |  |  | 				if (resolveContext[key]) { | 
					
						
							|  |  |  | 					for (const dep of newResolveContext[key]) { | 
					
						
							|  |  |  | 						resolveContext[key].add(dep); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			const resolveTime = Date.now(); | 
					
						
							|  |  |  | 			resolver.doResolve( | 
					
						
							|  |  |  | 				resolver.hooks.resolve, | 
					
						
							|  |  |  | 				newRequest, | 
					
						
							|  |  |  | 				"Cache miss", | 
					
						
							|  |  |  | 				newResolveContext, | 
					
						
							|  |  |  | 				(err, result) => { | 
					
						
							|  |  |  | 					propagate("missing"); | 
					
						
							|  |  |  | 					propagate("fileDependencies"); | 
					
						
							|  |  |  | 					propagate("contextDependencies"); | 
					
						
							|  |  |  | 					if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 					const fileDependencies = newResolveContext.fileDependencies; | 
					
						
							|  |  |  | 					const contextDependencies = newResolveContext.contextDependencies; | 
					
						
							|  |  |  | 					const missingDependencies = newResolveContext.missing; | 
					
						
							| 
									
										
										
										
											2019-01-05 02:17:37 +08:00
										 |  |  | 					// TODO remove this when enhanced-resolve supports fileDependencies
 | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 					if (result && result.path) { | 
					
						
							| 
									
										
										
										
											2018-12-21 19:29:49 +08:00
										 |  |  | 						fileDependencies.add(result.path); | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 					fileSystemInfo.createSnapshot( | 
					
						
							|  |  |  | 						resolveTime, | 
					
						
							|  |  |  | 						fileDependencies, | 
					
						
							|  |  |  | 						contextDependencies, | 
					
						
							|  |  |  | 						missingDependencies, | 
					
						
							|  |  |  | 						null, | 
					
						
							|  |  |  | 						(err, snapshot) => { | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 							cache.store( | 
					
						
							|  |  |  | 								identifier, | 
					
						
							|  |  |  | 								null, | 
					
						
							|  |  |  | 								{ | 
					
						
							|  |  |  | 									result, | 
					
						
							|  |  |  | 									missing: newResolveContext.missing, | 
					
						
							|  |  |  | 									fileDependencies: newResolveContext.fileDependencies, | 
					
						
							|  |  |  | 									contextDependencies: newResolveContext.contextDependencies, | 
					
						
							|  |  |  | 									snapshot | 
					
						
							|  |  |  | 								}, | 
					
						
							|  |  |  | 								storeErr => { | 
					
						
							|  |  |  | 									if (storeErr) return callback(storeErr); | 
					
						
							|  |  |  | 									if (result) return callback(null, result); | 
					
						
							|  |  |  | 									callback(); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							); | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 						} | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		compiler.resolverFactory.hooks.resolver.intercept({ | 
					
						
							|  |  |  | 			factory(type, hook) { | 
					
						
							|  |  |  | 				hook.tap( | 
					
						
							|  |  |  | 					"ResolverCachePlugin", | 
					
						
							|  |  |  | 					/** | 
					
						
							|  |  |  | 					 * @param {Resolver} resolver the resolver | 
					
						
							|  |  |  | 					 * @param {Object} options resolve options | 
					
						
							|  |  |  | 					 * @returns {void} | 
					
						
							|  |  |  | 					 */ | 
					
						
							|  |  |  | 					(resolver, options) => { | 
					
						
							| 
									
										
										
										
											2018-10-31 20:53:38 +08:00
										 |  |  | 						if (options.cache !== true) return; | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 						resolver.hooks.resolve.tapAsync( | 
					
						
							|  |  |  | 							{ | 
					
						
							|  |  |  | 								name: "ResolverCachePlugin", | 
					
						
							|  |  |  | 								stage: -100 | 
					
						
							|  |  |  | 							}, | 
					
						
							|  |  |  | 							(request, resolveContext, callback) => { | 
					
						
							|  |  |  | 								if (request._ResolverCachePluginCacheMiss || !fileSystemInfo) { | 
					
						
							|  |  |  | 									return callback(); | 
					
						
							|  |  |  | 								} | 
					
						
							| 
									
										
										
										
											2019-01-05 04:09:36 +08:00
										 |  |  | 								const identifier = `/resolve/${type}${requestToString( | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 									request | 
					
						
							|  |  |  | 								)}`;
 | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 								const processCacheResult = (err, cacheEntry) => { | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 									if (err) return callback(err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 									if (cacheEntry) { | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 										fileSystemInfo.checkSnapshotValid( | 
					
						
							|  |  |  | 											cacheEntry.snapshot, | 
					
						
							|  |  |  | 											(err, valid) => { | 
					
						
							|  |  |  | 												if (err || !valid) { | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 													return doRealResolve( | 
					
						
							|  |  |  | 														identifier, | 
					
						
							|  |  |  | 														type, | 
					
						
							|  |  |  | 														resolver, | 
					
						
							|  |  |  | 														resolveContext, | 
					
						
							|  |  |  | 														request, | 
					
						
							|  |  |  | 														callback | 
					
						
							|  |  |  | 													); | 
					
						
							|  |  |  | 												} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 												if (resolveContext.missing) { | 
					
						
							|  |  |  | 													for (const item of cacheEntry.missing) { | 
					
						
							|  |  |  | 														resolveContext.missing.add(item); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 												if (resolveContext.fileDependencies) { | 
					
						
							|  |  |  | 													for (const item of cacheEntry.fileDependencies) { | 
					
						
							|  |  |  | 														resolveContext.fileDependencies.add(item); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 												if (resolveContext.contextDependencies) { | 
					
						
							|  |  |  | 													for (const item of cacheEntry.contextDependencies) { | 
					
						
							|  |  |  | 														resolveContext.contextDependencies.add(item); | 
					
						
							|  |  |  | 													} | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 												callback(null, cacheEntry.result); | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 											} | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} else { | 
					
						
							|  |  |  | 										doRealResolve( | 
					
						
							|  |  |  | 											identifier, | 
					
						
							|  |  |  | 											type, | 
					
						
							|  |  |  | 											resolver, | 
					
						
							|  |  |  | 											resolveContext, | 
					
						
							|  |  |  | 											request, | 
					
						
							|  |  |  | 											callback | 
					
						
							|  |  |  | 										); | 
					
						
							|  |  |  | 									} | 
					
						
							| 
									
										
										
										
											2019-01-05 21:58:06 +08:00
										 |  |  | 								}; | 
					
						
							|  |  |  | 								cache.get(identifier, null, processCacheResult); | 
					
						
							| 
									
										
										
										
											2018-10-23 13:22:44 +08:00
										 |  |  | 							} | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				return hook; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = ResolverCachePlugin; |