mirror of https://github.com/webpack/webpack.git
add ability to set resolve options per dependency category
- byDependency option - create resolver per dependency category
This commit is contained in:
parent
0dd9aaf25b
commit
d4b1819749
|
|
@ -1111,6 +1111,15 @@ export interface ResolveOptions {
|
|||
* Fields in the description file (usually package.json) which are used to redirect requests inside the module.
|
||||
*/
|
||||
aliasFields?: (string[] | string)[];
|
||||
/**
|
||||
* Extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".
|
||||
*/
|
||||
byDependency?: {
|
||||
/**
|
||||
* Options object for resolving requests.
|
||||
*/
|
||||
[k: string]: ResolveOptions;
|
||||
};
|
||||
/**
|
||||
* Enable caching of successfully resolved requests (cache entries are revalidated).
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -44,8 +44,6 @@ const { join } = require("./util/fs");
|
|||
*/
|
||||
|
||||
const EMPTY_OBJECT = {};
|
||||
const REQUIRE_CONDITION_NAME = { conditionNames: ["require", "..."] };
|
||||
const IMPORT_CONDITION_NAME = { conditionNames: ["import", "..."] };
|
||||
|
||||
const MATCH_RESOURCE_REGEX = /^([^!]+)!=!/;
|
||||
|
||||
|
|
@ -234,57 +232,22 @@ class NormalModuleFactory extends ModuleFactory {
|
|||
stage: 100
|
||||
},
|
||||
(data, callback) => {
|
||||
let { resolveOptions } = data;
|
||||
const {
|
||||
contextInfo,
|
||||
context,
|
||||
dependencies,
|
||||
request,
|
||||
resolveOptions,
|
||||
fileDependencies,
|
||||
missingDependencies,
|
||||
contextDependencies
|
||||
} = data;
|
||||
const category =
|
||||
dependencies.length > 0 ? dependencies[0].category : undefined;
|
||||
|
||||
const loaderResolver = this.getResolver("loader");
|
||||
|
||||
if (category === "commonjs") {
|
||||
if (!resolveOptions) {
|
||||
resolveOptions = REQUIRE_CONDITION_NAME;
|
||||
} else {
|
||||
if (Array.isArray(resolveOptions.conditionNames)) {
|
||||
if (resolveOptions.conditionNames.indexOf("require") === -1) {
|
||||
resolveOptions = { ...resolveOptions };
|
||||
resolveOptions.conditionNames = REQUIRE_CONDITION_NAME.conditionNames.slice();
|
||||
}
|
||||
} else {
|
||||
resolveOptions = {
|
||||
...resolveOptions,
|
||||
conditionNames: REQUIRE_CONDITION_NAME.conditionNames.slice()
|
||||
};
|
||||
}
|
||||
}
|
||||
} else if (category === "esm") {
|
||||
if (!resolveOptions) {
|
||||
resolveOptions = IMPORT_CONDITION_NAME;
|
||||
} else {
|
||||
if (Array.isArray(resolveOptions.conditionNames)) {
|
||||
if (resolveOptions.conditionNames.indexOf("import") === -1) {
|
||||
resolveOptions = { ...resolveOptions };
|
||||
resolveOptions.conditionNames = IMPORT_CONDITION_NAME.conditionNames.slice();
|
||||
}
|
||||
} else {
|
||||
resolveOptions = {
|
||||
...resolveOptions,
|
||||
conditionNames: IMPORT_CONDITION_NAME.conditionNames.slice()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const normalResolver = this.getResolver(
|
||||
"normal",
|
||||
resolveOptions || EMPTY_OBJECT
|
||||
resolveOptions,
|
||||
dependencies.length > 0 ? dependencies[0].category : undefined
|
||||
);
|
||||
|
||||
/** @type {string} */
|
||||
|
|
@ -544,7 +507,7 @@ class NormalModuleFactory extends ModuleFactory {
|
|||
if (cacheEntry) return callback(null, cacheEntry);
|
||||
}
|
||||
const context = data.context || this.context;
|
||||
const resolveOptions = data.resolveOptions;
|
||||
const resolveOptions = data.resolveOptions || EMPTY_OBJECT;
|
||||
const dependency = dependencies[0];
|
||||
const request = dependency.request;
|
||||
const contextInfo = data.contextInfo;
|
||||
|
|
@ -727,8 +690,12 @@ class NormalModuleFactory extends ModuleFactory {
|
|||
return generator;
|
||||
}
|
||||
|
||||
getResolver(type, resolveOptions) {
|
||||
return this.resolverFactory.get(type, resolveOptions || EMPTY_OBJECT);
|
||||
getResolver(type, resolveOptions, category) {
|
||||
return this.resolverFactory.get(
|
||||
type,
|
||||
resolveOptions || EMPTY_OBJECT,
|
||||
category
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,13 +29,19 @@ const EMPTY_RESOLVE_OPTIONS = {};
|
|||
module.exports = class ResolverFactory {
|
||||
constructor() {
|
||||
this.hooks = Object.freeze({
|
||||
/** @type {HookMap<SyncWaterfallHook<[Object]>>} */
|
||||
/** @type {HookMap<SyncWaterfallHook<[Object, string]>>} */
|
||||
resolveOptions: new HookMap(
|
||||
() => new SyncWaterfallHook(["resolveOptions"])
|
||||
() => new SyncWaterfallHook(["resolveOptions", "category"])
|
||||
),
|
||||
/** @type {HookMap<SyncHook<[Resolver, Object, Object]>>} */
|
||||
/** @type {HookMap<SyncHook<[Resolver, Object, Object, string]>>} */
|
||||
resolver: new HookMap(
|
||||
() => new SyncHook(["resolver", "resolveOptions", "userResolveOptions"])
|
||||
() =>
|
||||
new SyncHook([
|
||||
"resolver",
|
||||
"resolveOptions",
|
||||
"userResolveOptions",
|
||||
"category"
|
||||
])
|
||||
)
|
||||
});
|
||||
/** @type {Map<string, ResolverCache>} */
|
||||
|
|
@ -45,16 +51,18 @@ module.exports = class ResolverFactory {
|
|||
/**
|
||||
* @param {string} type type of resolver
|
||||
* @param {Object=} resolveOptions options
|
||||
* @param {string=} category dependency category if any
|
||||
* @returns {ResolverWithOptions} the resolver
|
||||
*/
|
||||
get(type, resolveOptions = EMPTY_RESOLVE_OPTIONS) {
|
||||
let typedCaches = this.cache.get(type);
|
||||
get(type, resolveOptions = EMPTY_RESOLVE_OPTIONS, category = "unknown") {
|
||||
const typedCacheId = category === "esm" ? type : `${type}-${category}`;
|
||||
let typedCaches = this.cache.get(typedCacheId);
|
||||
if (!typedCaches) {
|
||||
typedCaches = {
|
||||
direct: new WeakMap(),
|
||||
stringified: new Map()
|
||||
};
|
||||
this.cache.set(type, typedCaches);
|
||||
this.cache.set(typedCacheId, typedCaches);
|
||||
}
|
||||
const cachedResolver = typedCaches.direct.get(resolveOptions);
|
||||
if (cachedResolver) {
|
||||
|
|
@ -66,7 +74,7 @@ module.exports = class ResolverFactory {
|
|||
typedCaches.direct.set(resolveOptions, resolver);
|
||||
return resolver;
|
||||
}
|
||||
const newResolver = this._create(type, resolveOptions);
|
||||
const newResolver = this._create(type, resolveOptions, category);
|
||||
typedCaches.direct.set(resolveOptions, newResolver);
|
||||
typedCaches.stringified.set(ident, newResolver);
|
||||
return newResolver;
|
||||
|
|
@ -75,11 +83,14 @@ module.exports = class ResolverFactory {
|
|||
/**
|
||||
* @param {string} type type of resolver
|
||||
* @param {Object} resolveOptions options
|
||||
* @param {string} category category
|
||||
* @returns {ResolverWithOptions} the resolver
|
||||
*/
|
||||
_create(type, resolveOptions) {
|
||||
_create(type, resolveOptions, category) {
|
||||
const originalResolveOptions = { ...resolveOptions };
|
||||
resolveOptions = this.hooks.resolveOptions.for(type).call(resolveOptions);
|
||||
resolveOptions = this.hooks.resolveOptions
|
||||
.for(type)
|
||||
.call(resolveOptions, category);
|
||||
const resolver = /** @type {ResolverWithOptions} */ (Factory.createResolver(
|
||||
resolveOptions
|
||||
));
|
||||
|
|
@ -92,13 +103,13 @@ module.exports = class ResolverFactory {
|
|||
const cacheEntry = childCache.get(options);
|
||||
if (cacheEntry !== undefined) return cacheEntry;
|
||||
const mergedOptions = cachedCleverMerge(originalResolveOptions, options);
|
||||
const resolver = this.get(type, mergedOptions);
|
||||
const resolver = this.get(type, mergedOptions, category);
|
||||
childCache.set(options, resolver);
|
||||
return resolver;
|
||||
};
|
||||
this.hooks.resolver
|
||||
.for(type)
|
||||
.call(resolver, resolveOptions, originalResolveOptions);
|
||||
.call(resolver, resolveOptions, originalResolveOptions, category);
|
||||
return resolver;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ const DefaultStatsFactoryPlugin = require("./stats/DefaultStatsFactoryPlugin");
|
|||
const DefaultStatsPresetPlugin = require("./stats/DefaultStatsPresetPlugin");
|
||||
const DefaultStatsPrinterPlugin = require("./stats/DefaultStatsPrinterPlugin");
|
||||
|
||||
const { cachedCleverMerge } = require("./util/cleverMerge");
|
||||
const { cachedCleverMerge, cleverMerge } = require("./util/cleverMerge");
|
||||
|
||||
/** @typedef {import("../declarations/WebpackOptions").WebpackOptionsNormalized} WebpackOptions */
|
||||
/** @typedef {import("./Compiler")} Compiler */
|
||||
|
|
@ -593,29 +593,59 @@ class WebpackOptionsApply extends OptionsApply {
|
|||
if (!compiler.inputFileSystem) {
|
||||
throw new Error("No input filesystem provided");
|
||||
}
|
||||
|
||||
const resolveOptionsByCategory = new Map();
|
||||
const baseResolveOptions = {
|
||||
...options.resolve
|
||||
};
|
||||
resolveOptionsByCategory.set("unknown", baseResolveOptions);
|
||||
|
||||
if (baseResolveOptions.byDependency) {
|
||||
const categories = Object.keys(baseResolveOptions.byDependency);
|
||||
delete baseResolveOptions.byDependency;
|
||||
for (const category of categories) {
|
||||
resolveOptionsByCategory.set(
|
||||
category,
|
||||
cleverMerge(
|
||||
baseResolveOptions,
|
||||
options.resolve.byDependency[category]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
compiler.resolverFactory.hooks.resolveOptions
|
||||
.for("normal")
|
||||
.tap("WebpackOptionsApply", resolveOptions => {
|
||||
.tap("WebpackOptionsApply", (resolveOptions, category) => {
|
||||
return {
|
||||
fileSystem: compiler.inputFileSystem,
|
||||
...cachedCleverMerge(options.resolve, resolveOptions)
|
||||
...cachedCleverMerge(
|
||||
resolveOptionsByCategory.get(category) || baseResolveOptions,
|
||||
resolveOptions
|
||||
)
|
||||
};
|
||||
});
|
||||
compiler.resolverFactory.hooks.resolveOptions
|
||||
.for("context")
|
||||
.tap("WebpackOptionsApply", resolveOptions => {
|
||||
.tap("WebpackOptionsApply", (resolveOptions, category) => {
|
||||
return {
|
||||
fileSystem: compiler.inputFileSystem,
|
||||
resolveToContext: true,
|
||||
...cachedCleverMerge(options.resolve, resolveOptions)
|
||||
...cachedCleverMerge(
|
||||
resolveOptionsByCategory.get(category) || baseResolveOptions,
|
||||
resolveOptions
|
||||
)
|
||||
};
|
||||
});
|
||||
compiler.resolverFactory.hooks.resolveOptions
|
||||
.for("loader")
|
||||
.tap("WebpackOptionsApply", resolveOptions => {
|
||||
.tap("WebpackOptionsApply", (resolveOptions, category) => {
|
||||
return {
|
||||
fileSystem: compiler.inputFileSystem,
|
||||
...cachedCleverMerge(options.resolveLoader, resolveOptions)
|
||||
...cachedCleverMerge(
|
||||
resolveOptionsByCategory.get(category) || baseResolveOptions,
|
||||
resolveOptions
|
||||
)
|
||||
};
|
||||
});
|
||||
compiler.hooks.afterResolvers.call(compiler);
|
||||
|
|
|
|||
|
|
@ -201,9 +201,10 @@ class ResolverCachePlugin {
|
|||
* @param {Resolver} resolver the resolver
|
||||
* @param {Object} options resolve options
|
||||
* @param {Object} userOptions resolve options passed by the user
|
||||
* @param {string} category category
|
||||
* @returns {void}
|
||||
*/
|
||||
(resolver, options, userOptions) => {
|
||||
(resolver, options, userOptions, category) => {
|
||||
if (options.cache !== true) return;
|
||||
const optionsIdent = objectToString(userOptions, false);
|
||||
const cacheWithContext =
|
||||
|
|
@ -219,7 +220,7 @@ class ResolverCachePlugin {
|
|||
if (request._ResolverCachePluginCacheMiss || !fileSystemInfo) {
|
||||
return callback();
|
||||
}
|
||||
const identifier = `/resolve/${type}${optionsIdent}${objectToString(
|
||||
const identifier = `/resolve/${type}${category}${optionsIdent}${objectToString(
|
||||
request,
|
||||
!cacheWithContext
|
||||
)}`;
|
||||
|
|
|
|||
|
|
@ -608,10 +608,17 @@ const applyResolveDefaults = (
|
|||
return conditions;
|
||||
}
|
||||
|
||||
conditions.push("node");
|
||||
|
||||
if (target === "electron-main" || target === "electron-preload") {
|
||||
conditions.push("electron");
|
||||
switch (target) {
|
||||
case "node":
|
||||
case "async-node":
|
||||
case "node-webkit":
|
||||
conditions.push("node");
|
||||
break;
|
||||
case "electron-main":
|
||||
case "electron-preload":
|
||||
conditions.push("node");
|
||||
conditions.push("electron");
|
||||
break;
|
||||
}
|
||||
|
||||
return conditions;
|
||||
|
|
@ -628,6 +635,14 @@ const applyResolveDefaults = (
|
|||
F(resolve, "mainFields", () =>
|
||||
webTarget ? ["browser", "module", "main"] : ["module", "main"]
|
||||
);
|
||||
F(resolve, "byDependency", () => ({
|
||||
commonjs: {
|
||||
conditionNames: ["require", "..."]
|
||||
},
|
||||
esm: {
|
||||
conditionNames: ["import", "..."]
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@ class RequireEnsureItemDependency extends ModuleDependency {
|
|||
get type() {
|
||||
return "require.ensure item";
|
||||
}
|
||||
|
||||
get category() {
|
||||
return "commonjs";
|
||||
}
|
||||
}
|
||||
|
||||
makeSerializable(
|
||||
|
|
|
|||
|
|
@ -2115,6 +2115,18 @@
|
|||
]
|
||||
}
|
||||
},
|
||||
"byDependency": {
|
||||
"description": "Extra resolve options per dependency category. Typical categories are \"commonjs\", \"amd\", \"esm\".",
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"description": "Options object for resolving requests.",
|
||||
"oneOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ResolveOptions"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"cache": {
|
||||
"description": "Enable caching of successfully resolved requests (cache entries are revalidated).",
|
||||
"type": "boolean"
|
||||
|
|
|
|||
|
|
@ -4361,7 +4361,7 @@ declare abstract class NormalModuleFactory extends ModuleFactory {
|
|||
createParser(type?: any, parserOptions?: {}): any;
|
||||
getGenerator(type?: any, generatorOptions?: {}): Generator;
|
||||
createGenerator(type?: any, generatorOptions?: {}): any;
|
||||
getResolver(type?: any, resolveOptions?: any): any;
|
||||
getResolver(type?: any, resolveOptions?: any, category?: any): any;
|
||||
}
|
||||
declare class NormalModuleReplacementPlugin {
|
||||
/**
|
||||
|
|
@ -5646,6 +5646,11 @@ declare interface ResolveOptions {
|
|||
*/
|
||||
aliasFields?: LibraryExport[];
|
||||
|
||||
/**
|
||||
* Extra resolve options per dependency category. Typical categories are "commonjs", "amd", "esm".
|
||||
*/
|
||||
byDependency?: { [index: string]: ResolveOptions };
|
||||
|
||||
/**
|
||||
* Enable caching of successfully resolved requests (cache entries are revalidated).
|
||||
*/
|
||||
|
|
@ -5762,11 +5767,15 @@ declare interface ResolverCache {
|
|||
}
|
||||
declare abstract class ResolverFactory {
|
||||
hooks: Readonly<{
|
||||
resolveOptions: HookMap<SyncWaterfallHook<[any]>>;
|
||||
resolver: HookMap<SyncHook<[Resolver, any, any], void>>;
|
||||
resolveOptions: HookMap<SyncWaterfallHook<[any, string]>>;
|
||||
resolver: HookMap<SyncHook<[Resolver, any, any, string], void>>;
|
||||
}>;
|
||||
cache: Map<string, ResolverCache>;
|
||||
get(type: string, resolveOptions?: any): Resolver & WithOptions;
|
||||
get(
|
||||
type: string,
|
||||
resolveOptions?: any,
|
||||
category?: string
|
||||
): Resolver & WithOptions;
|
||||
}
|
||||
declare interface RuleSet {
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue