improve merging of resolve and parsing options

by rules and via loader API

Arrays overwrite by default, but can reference old value with "..."
这个提交包含在:
Tobias Koppers 2019-07-17 13:08:51 +02:00
父节点 53a5ae2143
当前提交 b3ec775453
共有 17 个文件被更改,包括 143 次插入18 次删除

查看文件

@ -17,7 +17,7 @@ const {
const NormalModule = require("./NormalModule");
const RawModule = require("./RawModule");
const RuleSet = require("./RuleSet");
const cachedMerge = require("./util/cachedMerge");
const { cachedCleverMerge } = require("./util/cleverMerge");
const EMPTY_RESOLVE_OPTIONS = {};
@ -304,7 +304,7 @@ class NormalModuleFactory extends Tapable {
typeof settings[r.type] === "object" &&
settings[r.type] !== null
) {
settings[r.type] = cachedMerge(settings[r.type], r.value);
settings[r.type] = cachedCleverMerge(settings[r.type], r.value);
} else {
settings[r.type] = r.value;
}

查看文件

@ -6,6 +6,7 @@
const { Tapable, HookMap, SyncHook, SyncWaterfallHook } = require("tapable");
const Factory = require("enhanced-resolve").ResolverFactory;
const { cachedCleverMerge } = require("./util/cleverMerge");
/** @typedef {import("enhanced-resolve").Resolver} Resolver */
@ -66,7 +67,7 @@ module.exports = class ResolverFactory extends Tapable {
resolver.withOptions = options => {
const cacheEntry = childCache.get(options);
if (cacheEntry !== undefined) return cacheEntry;
const mergedOptions = Object.assign({}, originalResolveOptions, options);
const mergedOptions = cachedCleverMerge(originalResolveOptions, options);
const resolver = this.get(type, mergedOptions);
childCache.set(options, resolver);
return resolver;

查看文件

@ -37,6 +37,8 @@ const RequireContextPlugin = require("./dependencies/RequireContextPlugin");
const RequireEnsurePlugin = require("./dependencies/RequireEnsurePlugin");
const RequireIncludePlugin = require("./dependencies/RequireIncludePlugin");
const { cachedCleverMerge } = require("./util/cleverMerge");
/** @typedef {import("../declarations/WebpackOptions").WebpackOptions} WebpackOptions */
/** @typedef {import("./Compiler")} Compiler */
@ -512,8 +514,7 @@ class WebpackOptionsApply extends OptionsApply {
{
fileSystem: compiler.inputFileSystem
},
options.resolve,
resolveOptions
cachedCleverMerge(options.resolve, resolveOptions)
);
});
compiler.resolverFactory.hooks.resolveOptions
@ -524,8 +525,7 @@ class WebpackOptionsApply extends OptionsApply {
fileSystem: compiler.inputFileSystem,
resolveToContext: true
},
options.resolve,
resolveOptions
cachedCleverMerge(options.resolve, resolveOptions)
);
});
compiler.resolverFactory.hooks.resolveOptions
@ -535,8 +535,7 @@ class WebpackOptionsApply extends OptionsApply {
{
fileSystem: compiler.inputFileSystem
},
options.resolveLoader,
resolveOptions
cachedCleverMerge(options.resolveLoader, resolveOptions)
);
});
compiler.hooks.afterResolvers.call(compiler);

77
lib/util/cleverMerge.js 普通文件
查看文件

@ -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!");
expect(result).toEqual({
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({
extensions: [".xyz", ".js"]
});
const resolve3 = this.getResolve({
extensions: [".hee", "..."]
});
const resolve4 = this.getResolve({
extensions: [".xyz", "..."]
});
const resolve5 = this.getResolve({
extensions: ["...", ".xyz"]
});
return Promise.all([
resolve1(__dirname, "./index"),
resolve2(__dirname, "./index")
]).then(([one, two]) => {
resolve2(__dirname, "./index"),
resolve3(__dirname, "./index"),
resolve4(__dirname, "./index"),
resolve5(__dirname, "./index")
]).then(([one, two, three, four, five]) => {
return `module.exports = ${JSON.stringify({
one: path.basename(one),
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() {
var a = require("./a");
expect(a).toBe("ok");
expect(a).toBe("ok-normal-wrong2");
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 = {
resolve: {
alias: {
"./wrong2": "./ok2"
}
},
module: {
rules: [
{
@ -6,7 +11,23 @@ module.exports = {
resolve: {
alias: {
"./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";