ResolverFactory.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. /*
  2. MIT License http://www.opensource.org/licenses/mit-license.php
  3. Author Tobias Koppers @sokra
  4. */
  5. "use strict";
  6. const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
  7. const Factory = require("enhanced-resolve").ResolverFactory;
  8. /** @typedef {import("enhanced-resolve").Resolver} Resolver */
  9. module.exports = class ResolverFactory extends Tapable {
  10. constructor() {
  11. super();
  12. this.hooks = {
  13. resolveOptions: new HookMap(
  14. () => new SyncWaterfallHook(["resolveOptions"])
  15. ),
  16. resolver: new HookMap(() => new SyncHook(["resolver", "resolveOptions"]))
  17. };
  18. this._pluginCompat.tap("ResolverFactory", options => {
  19. let match;
  20. match = /^resolve-options (.+)$/.exec(options.name);
  21. if (match) {
  22. this.hooks.resolveOptions.tap(
  23. match[1],
  24. options.fn.name || "unnamed compat plugin",
  25. options.fn
  26. );
  27. return true;
  28. }
  29. match = /^resolver (.+)$/.exec(options.name);
  30. if (match) {
  31. this.hooks.resolver.tap(
  32. match[1],
  33. options.fn.name || "unnamed compat plugin",
  34. options.fn
  35. );
  36. return true;
  37. }
  38. });
  39. this.cache1 = new WeakMap();
  40. this.cache2 = new Map();
  41. }
  42. get(type, resolveOptions) {
  43. const cachedResolver = this.cache1.get(resolveOptions);
  44. if (cachedResolver) return cachedResolver();
  45. const ident = `${type}|${JSON.stringify(resolveOptions)}`;
  46. const resolver = this.cache2.get(ident);
  47. if (resolver) return resolver;
  48. const newResolver = this._create(type, resolveOptions);
  49. this.cache2.set(ident, newResolver);
  50. return newResolver;
  51. }
  52. _create(type, resolveOptions) {
  53. const originalResolveOptions = Object.assign({}, resolveOptions);
  54. resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions);
  55. const resolver = Factory.createResolver(resolveOptions);
  56. if (!resolver) {
  57. throw new Error("No resolver created");
  58. }
  59. /** @type {Map<Object, Resolver>} */
  60. const childCache = new Map();
  61. resolver.withOptions = options => {
  62. const cacheEntry = childCache.get(options);
  63. if (cacheEntry !== undefined) return cacheEntry;
  64. const mergedOptions = Object.assign({}, originalResolveOptions, options);
  65. const resolver = this.get(type, mergedOptions);
  66. childCache.set(options, resolver);
  67. return resolver;
  68. };
  69. this.hooks.resolver.for(type).call(resolver, resolveOptions);
  70. return resolver;
  71. }
  72. };