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");
|
2020-06-18 04:17:14 +08:00
|
|
|
const {
|
|
|
|
cachedCleverMerge,
|
2020-12-30 17:42:11 +08:00
|
|
|
removeOperations,
|
|
|
|
resolveByProperty
|
2020-06-18 04:17:14 +08:00
|
|
|
} = require("./util/cleverMerge");
|
2017-11-17 21:26:23 +08:00
|
|
|
|
2020-06-17 19:22:37 +08:00
|
|
|
/** @typedef {import("enhanced-resolve").ResolveOptions} ResolveOptions */
|
2018-12-03 19:42:28 +08:00
|
|
|
/** @typedef {import("enhanced-resolve").Resolver} Resolver */
|
2020-06-17 19:22:37 +08:00
|
|
|
/** @typedef {import("../declarations/WebpackOptions").ResolveOptions} WebpackResolveOptions */
|
2020-07-13 23:15:20 +08:00
|
|
|
/** @typedef {import("../declarations/WebpackOptions").ResolvePluginInstance} ResolvePluginInstance */
|
2018-10-22 15:02:39 +08:00
|
|
|
|
2025-04-03 00:02:22 +08:00
|
|
|
/** @typedef {WebpackResolveOptions & { dependencyType?: string, resolveToContext?: boolean }} ResolveOptionsWithDependencyType */
|
2019-11-11 22:25:03 +08:00
|
|
|
/**
|
2024-06-11 21:09:50 +08:00
|
|
|
* @typedef {object} WithOptions
|
2025-03-12 09:56:14 +08:00
|
|
|
* @property {(options: Partial<ResolveOptionsWithDependencyType>) => ResolverWithOptions} withOptions create a resolver with additional/different options
|
2019-11-11 22:25:03 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/** @typedef {Resolver & WithOptions} ResolverWithOptions */
|
|
|
|
|
2020-06-18 04:17:14 +08:00
|
|
|
// need to be hoisted on module level for caching identity
|
2025-04-03 00:02:22 +08:00
|
|
|
/** @type {ResolveOptionsWithDependencyType} */
|
2020-03-10 09:59:46 +08:00
|
|
|
const EMPTY_RESOLVE_OPTIONS = {};
|
2020-06-18 04:17:14 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {ResolveOptionsWithDependencyType} resolveOptionsWithDepType enhanced options
|
2025-09-10 03:11:22 +08:00
|
|
|
* @param {ResolveOptionsWithDependencyType} issuerResolveOptions enhanced options
|
2020-06-18 04:17:14 +08:00
|
|
|
* @returns {ResolveOptions} merged options
|
|
|
|
*/
|
2025-09-10 03:11:22 +08:00
|
|
|
const convertToResolveOptions = (
|
|
|
|
resolveOptionsWithDepType,
|
|
|
|
issuerResolveOptions
|
|
|
|
) => {
|
2020-12-30 17:42:11 +08:00
|
|
|
const { dependencyType, plugins, ...remaining } = resolveOptionsWithDepType;
|
2020-06-18 04:17:14 +08:00
|
|
|
|
|
|
|
// check type compat
|
2025-04-07 21:09:05 +08:00
|
|
|
/** @type {Partial<ResolveOptionsWithDependencyType>} */
|
2020-07-13 23:15:20 +08:00
|
|
|
const partialOptions = {
|
|
|
|
...remaining,
|
|
|
|
plugins:
|
|
|
|
plugins &&
|
2021-05-11 15:31:46 +08:00
|
|
|
/** @type {ResolvePluginInstance[]} */ (
|
2025-07-17 00:13:14 +08:00
|
|
|
plugins.filter((item) => item !== "...")
|
2021-05-11 15:31:46 +08:00
|
|
|
)
|
2020-07-13 23:15:20 +08:00
|
|
|
};
|
2020-06-18 04:17:14 +08:00
|
|
|
|
|
|
|
if (!partialOptions.fileSystem) {
|
|
|
|
throw new Error(
|
|
|
|
"fileSystem is missing in resolveOptions, but it's required for enhanced-resolve"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
// These weird types validate that we checked all non-optional properties
|
2021-05-11 15:31:46 +08:00
|
|
|
const options =
|
2025-04-07 21:09:05 +08:00
|
|
|
/** @type {Partial<ResolveOptionsWithDependencyType> & Pick<ResolveOptionsWithDependencyType, "fileSystem">} */ (
|
2021-05-11 15:31:46 +08:00
|
|
|
partialOptions
|
|
|
|
);
|
2025-09-10 03:11:22 +08:00
|
|
|
const { issuerDependency } = issuerResolveOptions;
|
|
|
|
|
|
|
|
const resolvedByDependency = resolveByProperty(
|
|
|
|
options,
|
|
|
|
"byDependency",
|
|
|
|
dependencyType
|
|
|
|
);
|
2020-06-18 04:17:14 +08:00
|
|
|
|
2025-04-07 21:09:05 +08:00
|
|
|
return /** @type {ResolveOptions} */ (
|
2025-09-10 03:11:22 +08:00
|
|
|
// byIssuerDependencyType
|
2025-04-07 21:09:05 +08:00
|
|
|
removeOperations(
|
2025-09-10 03:11:22 +08:00
|
|
|
issuerDependency
|
|
|
|
? resolveByProperty(
|
|
|
|
resolvedByDependency,
|
|
|
|
"byIssuerDependency",
|
|
|
|
issuerDependency
|
|
|
|
)
|
|
|
|
: resolvedByDependency,
|
2025-04-07 21:09:05 +08:00
|
|
|
// Keep the `unsafeCache` because it can be a `Proxy`
|
|
|
|
["unsafeCache"]
|
|
|
|
)
|
2020-12-30 17:42:11 +08:00
|
|
|
);
|
2020-06-18 04:17:14 +08:00
|
|
|
};
|
2019-07-17 21:38:07 +08:00
|
|
|
|
2018-11-24 04:50:26 +08:00
|
|
|
/**
|
2024-06-11 21:09:50 +08:00
|
|
|
* @typedef {object} ResolverCache
|
2025-04-03 00:02:22 +08:00
|
|
|
* @property {WeakMap<ResolveOptionsWithDependencyType, ResolverWithOptions>} direct
|
2019-11-11 22:25:03 +08:00
|
|
|
* @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({
|
2020-06-17 02:01:06 +08:00
|
|
|
/** @type {HookMap<SyncWaterfallHook<[ResolveOptionsWithDependencyType]>>} */
|
2018-02-25 09:00:20 +08:00
|
|
|
resolveOptions: new HookMap(
|
2020-06-09 06:32:24 +08:00
|
|
|
() => new SyncWaterfallHook(["resolveOptions"])
|
2018-02-25 09:00:20 +08:00
|
|
|
),
|
2020-06-17 02:01:06 +08:00
|
|
|
/** @type {HookMap<SyncHook<[Resolver, ResolveOptions, ResolveOptionsWithDependencyType]>>} */
|
2019-11-08 17:13:46 +08:00
|
|
|
resolver: new HookMap(
|
2020-06-09 06:32:24 +08:00
|
|
|
() => new SyncHook(["resolver", "resolveOptions", "userResolveOptions"])
|
2019-11-08 17:13:46 +08:00
|
|
|
)
|
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
|
2020-06-17 02:01:06 +08:00
|
|
|
* @param {ResolveOptionsWithDependencyType=} resolveOptions options
|
2025-09-10 03:11:22 +08:00
|
|
|
* @param {ResolveOptionsWithDependencyType=} issuerResolveOptions options
|
2019-11-11 22:25:03 +08:00
|
|
|
* @returns {ResolverWithOptions} the resolver
|
2018-10-22 15:02:39 +08:00
|
|
|
*/
|
2025-09-10 03:11:22 +08:00
|
|
|
get(
|
|
|
|
type,
|
|
|
|
resolveOptions = EMPTY_RESOLVE_OPTIONS,
|
|
|
|
issuerResolveOptions = EMPTY_RESOLVE_OPTIONS
|
|
|
|
) {
|
|
|
|
// 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);
|
|
|
|
// if (cachedResolver) {
|
|
|
|
// return cachedResolver;
|
|
|
|
// }
|
|
|
|
// const ident = JSON.stringify(resolveOptions);
|
|
|
|
// const resolver = typedCaches.stringified.get(ident);
|
|
|
|
// if (resolver) {
|
|
|
|
// typedCaches.direct.set(resolveOptions, resolver);
|
|
|
|
// return resolver;
|
|
|
|
// }
|
|
|
|
const newResolver = this._create(
|
|
|
|
type,
|
|
|
|
resolveOptions,
|
|
|
|
issuerResolveOptions
|
|
|
|
);
|
|
|
|
// 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
|
2020-06-18 04:17:14 +08:00
|
|
|
* @param {ResolveOptionsWithDependencyType} resolveOptionsWithDepType options
|
2025-09-10 03:11:22 +08:00
|
|
|
* @param {ResolveOptionsWithIssuerDependencyType} issuerResolveOptions options
|
2019-11-11 22:25:03 +08:00
|
|
|
* @returns {ResolverWithOptions} the resolver
|
2018-10-22 15:02:39 +08:00
|
|
|
*/
|
2025-09-10 03:11:22 +08:00
|
|
|
_create(type, resolveOptionsWithDepType, issuerResolveOptions) {
|
2020-06-17 02:01:06 +08:00
|
|
|
/** @type {ResolveOptionsWithDependencyType} */
|
2020-06-18 04:17:14 +08:00
|
|
|
const originalResolveOptions = { ...resolveOptionsWithDepType };
|
2020-06-17 19:22:37 +08:00
|
|
|
|
2020-06-18 04:17:14 +08:00
|
|
|
const resolveOptions = convertToResolveOptions(
|
2025-09-10 03:11:22 +08:00
|
|
|
this.hooks.resolveOptions.for(type).call(resolveOptionsWithDepType),
|
|
|
|
issuerResolveOptions
|
2020-06-18 04:17:14 +08:00
|
|
|
);
|
2021-05-11 15:31:46 +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");
|
|
|
|
}
|
2020-06-18 04:17:14 +08:00
|
|
|
/** @type {WeakMap<Partial<ResolveOptionsWithDependencyType>, ResolverWithOptions>} */
|
|
|
|
const childCache = new WeakMap();
|
2025-07-17 00:13:14 +08:00
|
|
|
resolver.withOptions = (options) => {
|
2018-12-03 19:42:28 +08:00
|
|
|
const cacheEntry = childCache.get(options);
|
|
|
|
if (cacheEntry !== undefined) return cacheEntry;
|
2019-05-13 21:16:23 +08:00
|
|
|
const mergedOptions = cachedCleverMerge(originalResolveOptions, options);
|
2020-06-09 06:32:24 +08:00
|
|
|
const resolver = this.get(type, mergedOptions);
|
2018-12-03 19:42:28 +08:00
|
|
|
childCache.set(options, resolver);
|
|
|
|
return resolver;
|
|
|
|
};
|
2019-11-08 17:13:46 +08:00
|
|
|
this.hooks.resolver
|
|
|
|
.for(type)
|
2020-06-09 06:32:24 +08:00
|
|
|
.call(resolver, resolveOptions, originalResolveOptions);
|
2017-11-17 21:26:23 +08:00
|
|
|
return resolver;
|
|
|
|
}
|
|
|
|
};
|