Browse Source

improve merging of resolve and parsing options

by rules and via loader API

Arrays overwrite by default, but can reference old value with "..."
tags/v4.36.0
Tobias Koppers 1 month ago
parent
commit
b3ec775453

+ 2
- 2
lib/NormalModuleFactory.js View File

@@ -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;
}

+ 2
- 1
lib/ResolverFactory.js View File

@@ -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;

+ 5
- 6
lib/WebpackOptionsApply.js View File

@@ -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
- 0
lib/util/cleverMerge.js View File

@@ -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;

+ 5
- 2
test/cases/loaders/resolve/index.js View File

@@ -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"
});
})
});

+ 17
- 2
test/cases/loaders/resolve/loader.js View File

@@ -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
test/configCases/rule-set/resolve-options/a.js View File

@@ -1 +1 @@
module.exports = require("./wrong");
module.exports = require("./wrong") + require("./normal") + require("./wrong2");

+ 1
- 1
test/configCases/rule-set/resolve-options/b.js View File

@@ -1 +1 @@
module.exports = require("./wrong");
module.exports = require("./wrong") + require("./normal") + require("./wrong2");

+ 1
- 0
test/configCases/rule-set/resolve-options/c.js View File

@@ -0,0 +1 @@
module.exports = require("./wrong") + require("./normal") + require("./wrong2");

+ 4
- 2
test/configCases/rule-set/resolve-options/index.js View File

@@ -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");
});

+ 1
- 0
test/configCases/rule-set/resolve-options/normal.js View File

@@ -0,0 +1 @@
module.exports = "-normal-";

+ 1
- 0
test/configCases/rule-set/resolve-options/ok.ok.js View File

@@ -0,0 +1 @@
module.exports = "ok-ok";

+ 1
- 0
test/configCases/rule-set/resolve-options/ok2.js View File

@@ -0,0 +1 @@
module.exports = "ok2";

+ 1
- 0
test/configCases/rule-set/resolve-options/ok2.yes.js View File

@@ -0,0 +1 @@
module.exports = "ok2-yes";

+ 22
- 1
test/configCases/rule-set/resolve-options/webpack.config.js View File

@@ -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", "..."]
}
}
]

+ 1
- 0
test/configCases/rule-set/resolve-options/wrong2.js View File

@@ -0,0 +1 @@
module.exports = "wrong2";

+ 1
- 0
test/configCases/rule-set/resolve-options/wrong2.yes.js View File

@@ -0,0 +1 @@
module.exports = "wrong2-yes";

Loading…
Cancel
Save