| 
									
										
										
										
											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"); | 
					
						
							| 
									
										
										
										
											2019-05-13 21:16:23 +08:00
										 |  |  | const { cachedCleverMerge } = require("./util/cleverMerge"); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} WithOptions | 
					
						
							|  |  |  |  * @property {function(Object): ResolverWithOptions} withOptions create a resolver with additional/different options | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** @typedef {Resolver & WithOptions} ResolverWithOptions */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | const EMPTY_RESOLVE_OPTIONS = {}; | 
					
						
							| 
									
										
										
										
											2019-07-17 21:38:07 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} ResolverCache | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  |  * @property {WeakMap<Object, ResolverWithOptions>} direct | 
					
						
							|  |  |  |  * @property {Map<string, ResolverWithOptions>} stringified | 
					
						
							| 
									
										
										
										
											2018-11-24 04:50:26 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-12-09 19:54:17 +08:00
										 |  |  | 			/** @type {HookMap<SyncWaterfallHook<[Object]>>} */ | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			resolveOptions: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncWaterfallHook(["resolveOptions"]) | 
					
						
							|  |  |  | 			), | 
					
						
							| 
									
										
										
										
											2019-11-08 17:13:46 +08:00
										 |  |  | 			/** @type {HookMap<SyncHook<[Resolver, Object, Object]>>} */ | 
					
						
							|  |  |  | 			resolver: new HookMap( | 
					
						
							|  |  |  | 				() => new SyncHook(["resolver", "resolveOptions", "userResolveOptions"]) | 
					
						
							|  |  |  | 			) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2019-07-17 22:04:34 +08:00
										 |  |  | 	 * @param {Object=} resolveOptions options | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 	 * @returns {ResolverWithOptions} the resolver | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-03-10 09:59:46 +08:00
										 |  |  | 	get(type, resolveOptions = EMPTY_RESOLVE_OPTIONS) { | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 	 * @returns {ResolverWithOptions} the resolver | 
					
						
							| 
									
										
										
										
											2018-10-22 15:02:39 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 	_create(type, resolveOptions) { | 
					
						
							| 
									
										
										
										
											2019-06-19 19:16:05 +08:00
										 |  |  | 		const originalResolveOptions = { ...resolveOptions }; | 
					
						
							| 
									
										
										
										
											2017-11-28 23:45:27 +08:00
										 |  |  | 		resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions); | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 		const resolver = /** @type {ResolverWithOptions} */ (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"); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-11 22:25:03 +08:00
										 |  |  | 		/** @type {Map<Object, ResolverWithOptions>} */ | 
					
						
							| 
									
										
										
										
											2018-12-03 19:42:28 +08:00
										 |  |  | 		const childCache = new Map(); | 
					
						
							|  |  |  | 		resolver.withOptions = options => { | 
					
						
							|  |  |  | 			const cacheEntry = childCache.get(options); | 
					
						
							|  |  |  | 			if (cacheEntry !== undefined) return cacheEntry; | 
					
						
							| 
									
										
										
										
											2019-05-13 21:16:23 +08:00
										 |  |  | 			const mergedOptions = cachedCleverMerge(originalResolveOptions, options); | 
					
						
							| 
									
										
										
										
											2018-12-03 19:42:28 +08:00
										 |  |  | 			const resolver = this.get(type, mergedOptions); | 
					
						
							|  |  |  | 			childCache.set(options, resolver); | 
					
						
							|  |  |  | 			return resolver; | 
					
						
							|  |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2019-11-08 17:13:46 +08:00
										 |  |  | 		this.hooks.resolver | 
					
						
							|  |  |  | 			.for(type) | 
					
						
							|  |  |  | 			.call(resolver, resolveOptions, originalResolveOptions); | 
					
						
							| 
									
										
										
										
											2017-11-17 21:26:23 +08:00
										 |  |  | 		return resolver; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; |