| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | 	Author Tobias Koppers @sokra | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-26 04:29:48 +08:00
										 |  |  | const Factory = require("enhanced-resolve").ResolverFactory; | 
					
						
							| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | const { HookMap, SyncHook, SyncWaterfallHook } = require("tapable"); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-03 19:42:28 +08:00
										 |  |  | /** @typedef {import("enhanced-resolve").Resolver} Resolver */ | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ResolverCache | 
					
						
							|  |  |  |  * @property {WeakMap<Object, Resolver>} direct | 
					
						
							|  |  |  |  * @property {Map<string, Resolver>} stringified | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-26 14:27:44 +08:00
										 |  |  | module.exports = class ResolverFactory { | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 	constructor() { | 
					
						
							| 
									
										
										
										
											2018-07-30 20:25:40 +08:00
										 |  |  | 		this.hooks = Object.freeze({ | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | 			/** @type {HookMap<Object>} */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			resolveOptions: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncWaterfallHook(["resolveOptions"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | 			/** @type {HookMap<Resolver, Object>} */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			resolver: new HookMap(() => new SyncHook(["resolver", "resolveOptions"])) | 
					
						
							| 
									
										
										
										
											2018-07-30 20:25:40 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | 		/** @type {Map<string, ResolverCache>} */ | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 		this.cache = new Map(); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} type type of resolver | 
					
						
							|  |  |  | 	 * @param {Object} resolveOptions options | 
					
						
							|  |  |  | 	 * @returns {Resolver} the resolver | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 	get(type, resolveOptions) { | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 		let typedCaches = this.cache.get(type); | 
					
						
							|  |  |  | 		if (!typedCaches) { | 
					
						
							|  |  |  | 			typedCaches = { | 
					
						
							|  |  |  | 				direct: new WeakMap(), | 
					
						
							|  |  |  | 				stringified: new Map() | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			this.cache.set(type, typedCaches); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const cachedResolver = typedCaches.direct.get(resolveOptions); | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | 		if (cachedResolver) { | 
					
						
							|  |  |  | 			return cachedResolver; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 		const ident = JSON.stringify(resolveOptions); | 
					
						
							|  |  |  | 		const resolver = typedCaches.stringified.get(ident); | 
					
						
							|  |  |  | 		if (resolver) { | 
					
						
							|  |  |  | 			typedCaches.direct.set(resolveOptions, resolver); | 
					
						
							|  |  |  | 			return resolver; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 		const newResolver = this._create(type, resolveOptions); | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 		typedCaches.direct.set(resolveOptions, newResolver); | 
					
						
							|  |  |  | 		typedCaches.stringified.set(ident, newResolver); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 		return newResolver; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} type type of resolver | 
					
						
							|  |  |  | 	 * @param {Object} resolveOptions options | 
					
						
							|  |  |  | 	 * @returns {Resolver} the resolver | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 	_create(type, resolveOptions) { | 
					
						
							| 
									
										
										
										
											2018-12-03 19:42:28 +08:00
										 |  |  | 		const originalResolveOptions = Object.assign({}, resolveOptions); | 
					
						
							| 
									
										
										
										
											2017-11-28 23:45:27 +08:00
										 |  |  | 		resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 		const resolver = Factory.createResolver(resolveOptions); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (!resolver) { | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 			throw new Error("No resolver created"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-03 19:42:28 +08:00
										 |  |  | 		/** @type {Map<Object, Resolver>} */ | 
					
						
							|  |  |  | 		const childCache = new Map(); | 
					
						
							|  |  |  | 		resolver.withOptions = options => { | 
					
						
							|  |  |  | 			const cacheEntry = childCache.get(options); | 
					
						
							|  |  |  | 			if (cacheEntry !== undefined) return cacheEntry; | 
					
						
							|  |  |  | 			const mergedOptions = Object.assign({}, originalResolveOptions, options); | 
					
						
							|  |  |  | 			const resolver = this.get(type, mergedOptions); | 
					
						
							|  |  |  | 			childCache.set(options, resolver); | 
					
						
							|  |  |  | 			return resolver; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2017-11-28 23:45:27 +08:00
										 |  |  | 		this.hooks.resolver.for(type).call(resolver, resolveOptions); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 		return resolver; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |