| /* |
| MIT License http://www.opensource.org/licenses/mit-license.php |
| Author Tobias Koppers @sokra |
| */ |
| "use strict"; |
| |
| const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable"); |
| const Factory = require("enhanced-resolve").ResolverFactory; |
| const { cachedCleverMerge } = require("./util/cleverMerge"); |
| |
| /** @typedef {import("enhanced-resolve").Resolver} Resolver */ |
| |
| const EMTPY_RESOLVE_OPTIONS = {}; |
| |
| module.exports = class ResolverFactory extends Tapable { |
| constructor() { |
| super(); |
| this.hooks = { |
| resolveOptions: new HookMap( |
| () => new SyncWaterfallHook(["resolveOptions"]) |
| ), |
| resolver: new HookMap(() => new SyncHook(["resolver", "resolveOptions"])) |
| }; |
| this._pluginCompat.tap("ResolverFactory", options => { |
| let match; |
| match = /^resolve-options (.+)$/.exec(options.name); |
| if (match) { |
| this.hooks.resolveOptions |
| .for(match[1]) |
| .tap(options.fn.name || "unnamed compat plugin", options.fn); |
| return true; |
| } |
| match = /^resolver (.+)$/.exec(options.name); |
| if (match) { |
| this.hooks.resolver |
| .for(match[1]) |
| .tap(options.fn.name || "unnamed compat plugin", options.fn); |
| return true; |
| } |
| }); |
| this.cache2 = new Map(); |
| } |
| |
| get(type, resolveOptions) { |
| resolveOptions = resolveOptions || EMTPY_RESOLVE_OPTIONS; |
| const ident = `${type}|${JSON.stringify(resolveOptions)}`; |
| const resolver = this.cache2.get(ident); |
| if (resolver) return resolver; |
| const newResolver = this._create(type, resolveOptions); |
| this.cache2.set(ident, newResolver); |
| return newResolver; |
| } |
| |
| _create(type, resolveOptions) { |
| const originalResolveOptions = Object.assign({}, resolveOptions); |
| resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions); |
| const resolver = Factory.createResolver(resolveOptions); |
| if (!resolver) { |
| throw new Error("No resolver created"); |
| } |
| /** @type {Map<Object, Resolver>} */ |
| const childCache = new Map(); |
| resolver.withOptions = options => { |
| const cacheEntry = childCache.get(options); |
| if (cacheEntry !== undefined) return cacheEntry; |
| const mergedOptions = cachedCleverMerge(originalResolveOptions, options); |
| const resolver = this.get(type, mergedOptions); |
| childCache.set(options, resolver); |
| return resolver; |
| }; |
| this.hooks.resolver.for(type).call(resolver, resolveOptions); |
| return resolver; |
| } |
| }; |