mirror of https://github.com/webpack/webpack.git
				
				
				
			Merge pull request #9422 from webpack/feature/dot-dot-dot-merge
improve merging of resolve and parsing options
This commit is contained in:
		
						commit
						aa1216c460
					
				|  | @ -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); | ||||
|  |  | |||
|  | @ -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"; | ||||
		Loading…
	
		Reference in New Issue