improve merging of resolve and parsing options
by rules and via loader API Arrays overwrite by default, but can reference old value with "..."
This commit is contained in:
parent
53a5ae2143
commit
b3ec775453
|
@ -17,7 +17,7 @@ const {
|
||||||
const NormalModule = require("./NormalModule");
|
const NormalModule = require("./NormalModule");
|
||||||
const RawModule = require("./RawModule");
|
const RawModule = require("./RawModule");
|
||||||
const RuleSet = require("./RuleSet");
|
const RuleSet = require("./RuleSet");
|
||||||
const cachedMerge = require("./util/cachedMerge");
|
const { cachedCleverMerge } = require("./util/cleverMerge");
|
||||||
|
|
||||||
const EMPTY_RESOLVE_OPTIONS = {};
|
const EMPTY_RESOLVE_OPTIONS = {};
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ class NormalModuleFactory extends Tapable {
|
||||||
typeof settings[r.type] === "object" &&
|
typeof settings[r.type] === "object" &&
|
||||||
settings[r.type] !== null
|
settings[r.type] !== null
|
||||||
) {
|
) {
|
||||||
settings[r.type] = cachedMerge(settings[r.type], r.value);
|
settings[r.type] = cachedCleverMerge(settings[r.type], r.value);
|
||||||
} else {
|
} else {
|
||||||
settings[r.type] = r.value;
|
settings[r.type] = r.value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
|
const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
|
||||||
const Factory = require("enhanced-resolve").ResolverFactory;
|
const Factory = require("enhanced-resolve").ResolverFactory;
|
||||||
|
const { cachedCleverMerge } = require("./util/cleverMerge");
|
||||||
|
|
||||||
/** @typedef {import("enhanced-resolve").Resolver} Resolver */
|
/** @typedef {import("enhanced-resolve").Resolver} Resolver */
|
||||||
|
|
||||||
|
@ -66,7 +67,7 @@ module.exports = class ResolverFactory extends Tapable {
|
||||||
resolver.withOptions = options => {
|
resolver.withOptions = options => {
|
||||||
const cacheEntry = childCache.get(options);
|
const cacheEntry = childCache.get(options);
|
||||||
if (cacheEntry !== undefined) return cacheEntry;
|
if (cacheEntry !== undefined) return cacheEntry;
|
||||||
const mergedOptions = Object.assign({}, originalResolveOptions, options);
|
const mergedOptions = cachedCleverMerge(originalResolveOptions, options);
|
||||||
const resolver = this.get(type, mergedOptions);
|
const resolver = this.get(type, mergedOptions);
|
||||||
childCache.set(options, resolver);
|
childCache.set(options, resolver);
|
||||||
return resolver;
|
return resolver;
|
||||||
|
|
|
@ -37,6 +37,8 @@ const RequireContextPlugin = require("./dependencies/RequireContextPlugin");
|
||||||
const RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin");
|
const RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin");
|
||||||
const RequireIncludePlugin = require("./dependencies/RequireIncludePlugin");
|
const RequireIncludePlugin = require("./dependencies/RequireIncludePlugin");
|
||||||
|
|
||||||
|
const { cachedCleverMerge } = require("./util/cleverMerge");
|
||||||
|
|
||||||
/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
|
/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
|
||||||
/** @typedef {import("./Compiler")} Compiler */
|
/** @typedef {import("./Compiler")} Compiler */
|
||||||
|
|
||||||
|
@ -512,8 +514,7 @@ class WebpackOptionsApply extends OptionsApply {
|
||||||
{
|
{
|
||||||
fileSystem: compiler.inputFileSystem
|
fileSystem: compiler.inputFileSystem
|
||||||
},
|
},
|
||||||
options.resolve,
|
cachedCleverMerge(options.resolve, resolveOptions)
|
||||||
resolveOptions
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
compiler.resolverFactory.hooks.resolveOptions
|
compiler.resolverFactory.hooks.resolveOptions
|
||||||
|
@ -524,8 +525,7 @@ class WebpackOptionsApply extends OptionsApply {
|
||||||
fileSystem: compiler.inputFileSystem,
|
fileSystem: compiler.inputFileSystem,
|
||||||
resolveToContext: true
|
resolveToContext: true
|
||||||
},
|
},
|
||||||
options.resolve,
|
cachedCleverMerge(options.resolve, resolveOptions)
|
||||||
resolveOptions
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
compiler.resolverFactory.hooks.resolveOptions
|
compiler.resolverFactory.hooks.resolveOptions
|
||||||
|
@ -535,8 +535,7 @@ class WebpackOptionsApply extends OptionsApply {
|
||||||
{
|
{
|
||||||
fileSystem: compiler.inputFileSystem
|
fileSystem: compiler.inputFileSystem
|
||||||
},
|
},
|
||||||
options.resolveLoader,
|
cachedCleverMerge(options.resolveLoader, resolveOptions)
|
||||||
resolveOptions
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
compiler.hooks.afterResolvers.call(compiler);
|
compiler.hooks.afterResolvers.call(compiler);
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
||||||
|
Author Tobias Koppers @sokra
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const mergeCache = new WeakMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two given objects and caches the result to avoid computation if same objects passed as arguments again.
|
||||||
|
* @example
|
||||||
|
* // performs cleverMerge(first, second), stores the result in WeakMap and returns result
|
||||||
|
* cachedCleverMerge({a: 1}, {a: 2})
|
||||||
|
* {a: 2}
|
||||||
|
* // when same arguments passed, gets the result from WeakMap and returns it.
|
||||||
|
* cachedCleverMerge({a: 1}, {a: 2})
|
||||||
|
* {a: 2}
|
||||||
|
* @param {object} first first object
|
||||||
|
* @param {object} second second object
|
||||||
|
* @returns {object} merged object of first and second object
|
||||||
|
*/
|
||||||
|
const cachedCleverMerge = (first, second) => {
|
||||||
|
let innerCache = mergeCache.get(first);
|
||||||
|
if (innerCache === undefined) {
|
||||||
|
innerCache = new WeakMap();
|
||||||
|
mergeCache.set(first, innerCache);
|
||||||
|
}
|
||||||
|
const prevMerge = innerCache.get(second);
|
||||||
|
if (prevMerge !== undefined) return prevMerge;
|
||||||
|
const newMerge = cleverMerge(first, second);
|
||||||
|
innerCache.set(second, newMerge);
|
||||||
|
return newMerge;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges two objects. Objects are not deeply merged.
|
||||||
|
* TODO webpack 5: merge objects deeply clever.
|
||||||
|
* Arrays might reference the old value with "..."
|
||||||
|
* @param {object} first first object
|
||||||
|
* @param {object} second second object
|
||||||
|
* @returns {object} merged object of first and second object
|
||||||
|
*/
|
||||||
|
const cleverMerge = (first, second) => {
|
||||||
|
const newObject = Object.assign({}, first);
|
||||||
|
for (const key of Object.keys(second)) {
|
||||||
|
if (!(key in newObject)) {
|
||||||
|
newObject[key] = second[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const secondValue = second[key];
|
||||||
|
if (!Array.isArray(secondValue)) {
|
||||||
|
newObject[key] = secondValue;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const firstValue = newObject[key];
|
||||||
|
if (Array.isArray(firstValue)) {
|
||||||
|
const newArray = [];
|
||||||
|
for (const item of secondValue) {
|
||||||
|
if (item === "...") {
|
||||||
|
for (const item of firstValue) {
|
||||||
|
newArray.push(item);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newArray.push(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newObject[key] = newArray;
|
||||||
|
} else {
|
||||||
|
newObject[key] = secondValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObject;
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.cachedCleverMerge = cachedCleverMerge;
|
||||||
|
exports.cleverMerge = cleverMerge;
|
|
@ -2,6 +2,9 @@ it("should be possible to create resolver with different options", () => {
|
||||||
const result = require("./loader!");
|
const result = require("./loader!");
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
one: "index.js",
|
one: "index.js",
|
||||||
two: "index.xyz"
|
two: "index.xyz",
|
||||||
|
three: "index.js",
|
||||||
|
four: "index.xyz",
|
||||||
|
five: "index.js"
|
||||||
});
|
});
|
||||||
})
|
});
|
||||||
|
|
|
@ -4,13 +4,28 @@ module.exports = function() {
|
||||||
const resolve2 = this.getResolve({
|
const resolve2 = this.getResolve({
|
||||||
extensions: [".xyz", ".js"]
|
extensions: [".xyz", ".js"]
|
||||||
});
|
});
|
||||||
|
const resolve3 = this.getResolve({
|
||||||
|
extensions: [".hee", "..."]
|
||||||
|
});
|
||||||
|
const resolve4 = this.getResolve({
|
||||||
|
extensions: [".xyz", "..."]
|
||||||
|
});
|
||||||
|
const resolve5 = this.getResolve({
|
||||||
|
extensions: ["...", ".xyz"]
|
||||||
|
});
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
resolve1(__dirname, "./index"),
|
resolve1(__dirname, "./index"),
|
||||||
resolve2(__dirname, "./index")
|
resolve2(__dirname, "./index"),
|
||||||
]).then(([one, two]) => {
|
resolve3(__dirname, "./index"),
|
||||||
|
resolve4(__dirname, "./index"),
|
||||||
|
resolve5(__dirname, "./index")
|
||||||
|
]).then(([one, two, three, four, five]) => {
|
||||||
return `module.exports = ${JSON.stringify({
|
return `module.exports = ${JSON.stringify({
|
||||||
one: path.basename(one),
|
one: path.basename(one),
|
||||||
two: path.basename(two),
|
two: path.basename(two),
|
||||||
|
three: path.basename(three),
|
||||||
|
four: path.basename(four),
|
||||||
|
five: path.basename(five)
|
||||||
})}`;
|
})}`;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
module.exports = require("./wrong");
|
module.exports = require("./wrong") + require("./normal") + require("./wrong2");
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
module.exports = require("./wrong");
|
module.exports = require("./wrong") + require("./normal") + require("./wrong2");
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require("./wrong") + require("./normal") + require("./wrong2");
|
|
@ -1,6 +1,8 @@
|
||||||
it("should allow to set custom resolving rules", function() {
|
it("should allow to set custom resolving rules", function() {
|
||||||
var a = require("./a");
|
var a = require("./a");
|
||||||
expect(a).toBe("ok");
|
expect(a).toBe("ok-normal-wrong2");
|
||||||
var b = require("./b");
|
var b = require("./b");
|
||||||
expect(b).toBe("wrong");
|
expect(b).toBe("ok-normal-wrong2-yes");
|
||||||
|
var c = require("./c");
|
||||||
|
expect(c).toBe("wrong-normal-ok2");
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "-normal-";
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "ok-ok";
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "ok2";
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "ok2-yes";
|
|
@ -1,4 +1,9 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"./wrong2": "./ok2"
|
||||||
|
}
|
||||||
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
{
|
{
|
||||||
|
@ -6,7 +11,23 @@ module.exports = {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"./wrong": "./ok"
|
"./wrong": "./ok"
|
||||||
}
|
},
|
||||||
|
extensions: [".js", ".ok.js"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: require.resolve("./b"),
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"./wrong": "./ok"
|
||||||
|
},
|
||||||
|
extensions: ["...", ".ok.js"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: require.resolve("./b"),
|
||||||
|
resolve: {
|
||||||
|
extensions: [".yes.js", "..."]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "wrong2";
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = "wrong2-yes";
|
Loading…
Reference in New Issue