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