mirror of https://github.com/webpack/webpack.git
				
				
				
			Smaller performance improvements in NormalModuleFactory
This commit is contained in:
		
							parent
							
								
									526bc7a788
								
							
						
					
					
						commit
						90baf475af
					
				|  | @ -808,37 +808,44 @@ class Compilation { | |||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// This is nested so we need to allow one additional task
 | ||||
| 		this.processDependenciesQueue.increaseParallelism(); | ||||
| 		if (sortedDependencies.length === 0) { | ||||
| 			callback(); | ||||
| 			return; | ||||
| 		} | ||||
| 
 | ||||
| 		asyncLib.forEach( | ||||
| 			sortedDependencies, | ||||
| 			(item, callback) => { | ||||
| 				this.handleModuleCreation( | ||||
| 					{ | ||||
| 						factory: item.factory, | ||||
| 						dependencies: item.dependencies, | ||||
| 						originModule: module | ||||
| 					}, | ||||
| 					err => { | ||||
| 						// In V8, the Error objects keep a reference to the functions on the stack. These warnings &
 | ||||
| 						// errors are created inside closures that keep a reference to the Compilation, so errors are
 | ||||
| 						// leaking the Compilation object.
 | ||||
| 						if (err && this.bail) { | ||||
| 							// eslint-disable-next-line no-self-assign
 | ||||
| 							err.stack = err.stack; | ||||
| 							return callback(err); | ||||
| 		process.nextTick(() => { | ||||
| 			// This is nested so we need to allow one additional task
 | ||||
| 			this.processDependenciesQueue.increaseParallelism(); | ||||
| 
 | ||||
| 			asyncLib.forEach( | ||||
| 				sortedDependencies, | ||||
| 				(item, callback) => { | ||||
| 					this.handleModuleCreation( | ||||
| 						{ | ||||
| 							factory: item.factory, | ||||
| 							dependencies: item.dependencies, | ||||
| 							originModule: module | ||||
| 						}, | ||||
| 						err => { | ||||
| 							// In V8, the Error objects keep a reference to the functions on the stack. These warnings &
 | ||||
| 							// errors are created inside closures that keep a reference to the Compilation, so errors are
 | ||||
| 							// leaking the Compilation object.
 | ||||
| 							if (err && this.bail) { | ||||
| 								// eslint-disable-next-line no-self-assign
 | ||||
| 								err.stack = err.stack; | ||||
| 								return callback(err); | ||||
| 							} | ||||
| 							callback(); | ||||
| 						} | ||||
| 						callback(); | ||||
| 					} | ||||
| 				); | ||||
| 			}, | ||||
| 			err => { | ||||
| 				this.processDependenciesQueue.decreaseParallelism(); | ||||
| 					); | ||||
| 				}, | ||||
| 				err => { | ||||
| 					this.processDependenciesQueue.decreaseParallelism(); | ||||
| 
 | ||||
| 				return callback(err); | ||||
| 			} | ||||
| 		); | ||||
| 					return callback(err); | ||||
| 				} | ||||
| 			); | ||||
| 		}); | ||||
| 	} | ||||
| 
 | ||||
| 	/** | ||||
|  |  | |||
|  | @ -566,16 +566,18 @@ class Compiler { | |||
| 			this.hooks.make.callAsync(compilation, err => { | ||||
| 				if (err) return callback(err); | ||||
| 
 | ||||
| 				compilation.finish(err => { | ||||
| 					if (err) return callback(err); | ||||
| 
 | ||||
| 					compilation.seal(err => { | ||||
| 				process.nextTick(() => { | ||||
| 					compilation.finish(err => { | ||||
| 						if (err) return callback(err); | ||||
| 
 | ||||
| 						this.hooks.afterCompile.callAsync(compilation, err => { | ||||
| 						compilation.seal(err => { | ||||
| 							if (err) return callback(err); | ||||
| 
 | ||||
| 							return callback(null, compilation); | ||||
| 							this.hooks.afterCompile.callAsync(compilation, err => { | ||||
| 								if (err) return callback(err); | ||||
| 
 | ||||
| 								return callback(null, compilation); | ||||
| 							}); | ||||
| 						}); | ||||
| 					}); | ||||
| 				}); | ||||
|  |  | |||
|  | @ -58,6 +58,14 @@ const loaderToIdent = data => { | |||
| 	return data.loader + "?" + JSON.stringify(data.options); | ||||
| }; | ||||
| 
 | ||||
| const stringifyLoadersAndResource = (loaders, resource) => { | ||||
| 	let str = ""; | ||||
| 	for (const loader of loaders) { | ||||
| 		str += loaderToIdent(loader) + "!"; | ||||
| 	} | ||||
| 	return str + resource; | ||||
| }; | ||||
| 
 | ||||
| const identToLoaderRequest = resultString => { | ||||
| 	const idx = resultString.indexOf("?"); | ||||
| 	if (idx >= 0) { | ||||
|  | @ -75,6 +83,18 @@ const identToLoaderRequest = resultString => { | |||
| 	} | ||||
| }; | ||||
| 
 | ||||
| const needCalls = (times, callback) => { | ||||
| 	return err => { | ||||
| 		if (--times === 0) { | ||||
| 			return callback(err); | ||||
| 		} | ||||
| 		if (err && times > 0) { | ||||
| 			times = NaN; | ||||
| 			return callback(err); | ||||
| 		} | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| // TODO webpack 6 remove
 | ||||
| const deprecationChangedHookMessage = name => | ||||
| 	`NormalModuleFactory.${name} is no longer a waterfall hook, but a bailing hook instead. ` + | ||||
|  | @ -196,25 +216,33 @@ class NormalModuleFactory extends ModuleFactory { | |||
| 				const matchResourceMatch = MATCH_RESOURCE_REGEX.exec(request); | ||||
| 				if (matchResourceMatch) { | ||||
| 					matchResource = matchResourceMatch[1]; | ||||
| 					if (/^\.\.?\//.test(matchResource)) { | ||||
| 						matchResource = path.join(context, matchResource); | ||||
| 					if (matchResource.charCodeAt(0) === 46) { | ||||
| 						// 46 === ".", 47 === "/"
 | ||||
| 						const secondChar = matchResource.charCodeAt(1); | ||||
| 						if ( | ||||
| 							secondChar === 47 || | ||||
| 							(secondChar === 46 && matchResource.charCodeAt(2) === 47) | ||||
| 						) { | ||||
| 							// if matchResources startsWith ../ or ./
 | ||||
| 							matchResource = path.join(context, matchResource); | ||||
| 						} | ||||
| 					} | ||||
| 					requestWithoutMatchResource = request.substr( | ||||
| 						matchResourceMatch[0].length | ||||
| 					); | ||||
| 				} | ||||
| 
 | ||||
| 				const noPreAutoLoaders = requestWithoutMatchResource.startsWith("-!"); | ||||
| 				const noAutoLoaders = | ||||
| 					noPreAutoLoaders || requestWithoutMatchResource.startsWith("!"); | ||||
| 				const noPrePostAutoLoaders = requestWithoutMatchResource.startsWith( | ||||
| 					"!!" | ||||
| 				); | ||||
| 				const firstChar = requestWithoutMatchResource.charCodeAt(0); | ||||
| 				const secondChar = requestWithoutMatchResource.charCodeAt(1); | ||||
| 				const noPreAutoLoaders = firstChar === 45 && secondChar === 33; // startsWith "-!"
 | ||||
| 				const noAutoLoaders = noPreAutoLoaders || firstChar === 33; // startsWith "!"
 | ||||
| 				const noPrePostAutoLoaders = firstChar === 33 && secondChar === 33; // startsWith "!!";
 | ||||
| 				const rawElements = requestWithoutMatchResource | ||||
| 					.replace(/^-?!+/, "") | ||||
| 					.replace(/!!+/g, "!") | ||||
| 					.split("!"); | ||||
| 				const resource = rawElements.pop(); | ||||
| 					.slice( | ||||
| 						noPreAutoLoaders || noPrePostAutoLoaders ? 2 : noAutoLoaders ? 1 : 0 | ||||
| 					) | ||||
| 					.split(/!+/); | ||||
| 				const unresolvedResource = rawElements.pop(); | ||||
| 				const elements = rawElements.map(identToLoaderRequest); | ||||
| 
 | ||||
| 				const resolveContext = { | ||||
|  | @ -224,192 +252,197 @@ class NormalModuleFactory extends ModuleFactory { | |||
| 					contextDependencies | ||||
| 				}; | ||||
| 
 | ||||
| 				asyncLib.parallel( | ||||
| 					[ | ||||
| 						callback => | ||||
| 							this.resolveRequestArray( | ||||
| 								contextInfo, | ||||
| 								context, | ||||
| 								elements, | ||||
| 								loaderResolver, | ||||
| 								resolveContext, | ||||
| 								callback | ||||
| 							), | ||||
| 						callback => { | ||||
| 							if (resource === "" || resource[0] === "?") { | ||||
| 								return callback(null, { | ||||
| 									resource | ||||
| 								}); | ||||
| 							} | ||||
| 				/** @type {string | false} */ | ||||
| 				let resource; | ||||
| 				let resourceResolveData; | ||||
| 				let loaders; | ||||
| 
 | ||||
| 							normalResolver.resolve( | ||||
| 								contextInfo, | ||||
| 								context, | ||||
| 								resource, | ||||
| 								resolveContext, | ||||
| 								(err, resource, resourceResolveData) => { | ||||
| 									if (err) return callback(err); | ||||
| 				const continueCallback = needCalls(2, err => { | ||||
| 					if (err) return callback(err); | ||||
| 
 | ||||
| 									// TODO remove this when enhanced-resolve supports fileDependencies
 | ||||
| 									if (resource) { | ||||
| 										fileDependencies.add(resource); | ||||
| 									} | ||||
| 
 | ||||
| 									callback(null, { | ||||
| 										resourceResolveData, | ||||
| 										resource | ||||
| 									}); | ||||
| 								} | ||||
| 							); | ||||
| 						} | ||||
| 					], | ||||
| 					(err, results) => { | ||||
| 						if (err) return callback(err); | ||||
| 						let loaders = results[0]; | ||||
| 						const resourceResolveData = results[1].resourceResolveData; | ||||
| 						const resource = results[1].resource; | ||||
| 
 | ||||
| 						// translate option idents
 | ||||
| 						try { | ||||
| 							for (const item of loaders) { | ||||
| 								if ( | ||||
| 									typeof item.options === "string" && | ||||
| 									item.options[0] === "?" | ||||
| 								) { | ||||
| 									const ident = item.options.substr(1); | ||||
| 									item.options = this.ruleSet.findOptionsByIdent(ident); | ||||
| 									item.ident = ident; | ||||
| 								} | ||||
| 							} | ||||
| 						} catch (e) { | ||||
| 							return callback(e); | ||||
| 						} | ||||
| 
 | ||||
| 						if (resource === false) { | ||||
| 							// ignored
 | ||||
| 							return callback( | ||||
| 								null, | ||||
| 								new RawModule( | ||||
| 									"/* (ignored) */", | ||||
| 									`ignored|${request}`, | ||||
| 									`${request} (ignored)` | ||||
| 								) | ||||
| 							); | ||||
| 						} | ||||
| 
 | ||||
| 						const userRequest = | ||||
| 							(matchResource !== undefined ? `${matchResource}!=!` : "") + | ||||
| 							loaders | ||||
| 								.map(loaderToIdent) | ||||
| 								.concat([resource]) | ||||
| 								.join("!"); | ||||
| 
 | ||||
| 						let resourcePath = | ||||
| 							matchResource !== undefined ? matchResource : resource; | ||||
| 						let resourceQuery = ""; | ||||
| 						const queryIndex = resourcePath.indexOf("?"); | ||||
| 						if (queryIndex >= 0) { | ||||
| 							resourceQuery = resourcePath.substr(queryIndex); | ||||
| 							resourcePath = resourcePath.substr(0, queryIndex); | ||||
| 						} | ||||
| 
 | ||||
| 						const result = this.ruleSet.exec({ | ||||
| 							resource: resourcePath, | ||||
| 							realResource: | ||||
| 								matchResource !== undefined | ||||
| 									? resource.replace(/\?.*/, "") | ||||
| 									: resourcePath, | ||||
| 							resourceQuery, | ||||
| 							issuer: contextInfo.issuer, | ||||
| 							compiler: contextInfo.compiler | ||||
| 						}); | ||||
| 						const settings = {}; | ||||
| 						const useLoadersPost = []; | ||||
| 						const useLoaders = []; | ||||
| 						const useLoadersPre = []; | ||||
| 						for (const r of result) { | ||||
| 							if (r.type === "use") { | ||||
| 								if (r.enforce === "post" && !noPrePostAutoLoaders) { | ||||
| 									useLoadersPost.push(r.value); | ||||
| 								} else if ( | ||||
| 									r.enforce === "pre" && | ||||
| 									!noPreAutoLoaders && | ||||
| 									!noPrePostAutoLoaders | ||||
| 								) { | ||||
| 									useLoadersPre.push(r.value); | ||||
| 								} else if ( | ||||
| 									!r.enforce && | ||||
| 									!noAutoLoaders && | ||||
| 									!noPrePostAutoLoaders | ||||
| 								) { | ||||
| 									useLoaders.push(r.value); | ||||
| 								} | ||||
| 							} else if ( | ||||
| 								typeof r.value === "object" && | ||||
| 								r.value !== null && | ||||
| 								typeof settings[r.type] === "object" && | ||||
| 								settings[r.type] !== null | ||||
| 							) { | ||||
| 								settings[r.type] = cachedMerge(settings[r.type], r.value); | ||||
| 							} else { | ||||
| 								settings[r.type] = r.value; | ||||
| 					// translate option idents
 | ||||
| 					try { | ||||
| 						for (const item of loaders) { | ||||
| 							if (typeof item.options === "string" && item.options[0] === "?") { | ||||
| 								const ident = item.options.substr(1); | ||||
| 								item.options = this.ruleSet.findOptionsByIdent(ident); | ||||
| 								item.ident = ident; | ||||
| 							} | ||||
| 						} | ||||
| 						asyncLib.parallel( | ||||
| 							[ | ||||
| 								this.resolveRequestArray.bind( | ||||
| 									this, | ||||
| 									contextInfo, | ||||
| 									this.context, | ||||
| 									useLoadersPost, | ||||
| 									loaderResolver, | ||||
| 									resolveContext | ||||
| 								), | ||||
| 								this.resolveRequestArray.bind( | ||||
| 									this, | ||||
| 									contextInfo, | ||||
| 									this.context, | ||||
| 									useLoaders, | ||||
| 									loaderResolver, | ||||
| 									resolveContext | ||||
| 								), | ||||
| 								this.resolveRequestArray.bind( | ||||
| 									this, | ||||
| 									contextInfo, | ||||
| 									this.context, | ||||
| 									useLoadersPre, | ||||
| 									loaderResolver, | ||||
| 									resolveContext | ||||
| 								) | ||||
| 							], | ||||
| 							(err, results) => { | ||||
| 								if (err) { | ||||
| 									return callback(err); | ||||
| 								} | ||||
| 								loaders = results[0].concat(loaders, results[1], results[2]); | ||||
| 								const type = settings.type; | ||||
| 								const resolveOptions = settings.resolve; | ||||
| 								Object.assign(data.createData, { | ||||
| 									request: loaders | ||||
| 										.map(loaderToIdent) | ||||
| 										.concat([resource]) | ||||
| 										.join("!"), | ||||
| 									userRequest, | ||||
| 									rawRequest: request, | ||||
| 									loaders, | ||||
| 									resource, | ||||
| 									matchResource, | ||||
| 									resourceResolveData, | ||||
| 									settings, | ||||
| 									type, | ||||
| 									parser: this.getParser(type, settings.parser), | ||||
| 									generator: this.getGenerator(type, settings.generator), | ||||
| 									resolveOptions | ||||
| 								}); | ||||
| 								callback(); | ||||
| 							} | ||||
| 					} catch (e) { | ||||
| 						return callback(e); | ||||
| 					} | ||||
| 
 | ||||
| 					if (resource === false) { | ||||
| 						// ignored
 | ||||
| 						return callback( | ||||
| 							null, | ||||
| 							new RawModule( | ||||
| 								"/* (ignored) */", | ||||
| 								`ignored|${request}`, | ||||
| 								`${request} (ignored)` | ||||
| 							) | ||||
| 						); | ||||
| 					} | ||||
| 
 | ||||
| 					const userRequest = | ||||
| 						(matchResource !== undefined ? `${matchResource}!=!` : "") + | ||||
| 						stringifyLoadersAndResource(loaders, resource); | ||||
| 
 | ||||
| 					let resourcePath = | ||||
| 						matchResource !== undefined ? matchResource : resource; | ||||
| 					let resourceQuery = ""; | ||||
| 					const queryIndex = resourcePath.indexOf("?"); | ||||
| 					if (queryIndex >= 0) { | ||||
| 						resourceQuery = resourcePath.substr(queryIndex); | ||||
| 						resourcePath = resourcePath.substr(0, queryIndex); | ||||
| 					} | ||||
| 
 | ||||
| 					const result = this.ruleSet.exec({ | ||||
| 						resource: resourcePath, | ||||
| 						realResource: | ||||
| 							matchResource !== undefined | ||||
| 								? resource.replace(/\?.*/, "") | ||||
| 								: resourcePath, | ||||
| 						resourceQuery, | ||||
| 						issuer: contextInfo.issuer, | ||||
| 						compiler: contextInfo.compiler | ||||
| 					}); | ||||
| 					const settings = {}; | ||||
| 					const useLoadersPost = []; | ||||
| 					const useLoaders = []; | ||||
| 					const useLoadersPre = []; | ||||
| 					for (const r of result) { | ||||
| 						if (r.type === "use") { | ||||
| 							if (r.enforce === "post" && !noPrePostAutoLoaders) { | ||||
| 								useLoadersPost.push(r.value); | ||||
| 							} else if ( | ||||
| 								r.enforce === "pre" && | ||||
| 								!noPreAutoLoaders && | ||||
| 								!noPrePostAutoLoaders | ||||
| 							) { | ||||
| 								useLoadersPre.push(r.value); | ||||
| 							} else if ( | ||||
| 								!r.enforce && | ||||
| 								!noAutoLoaders && | ||||
| 								!noPrePostAutoLoaders | ||||
| 							) { | ||||
| 								useLoaders.push(r.value); | ||||
| 							} | ||||
| 						} else if ( | ||||
| 							typeof r.value === "object" && | ||||
| 							r.value !== null && | ||||
| 							typeof settings[r.type] === "object" && | ||||
| 							settings[r.type] !== null | ||||
| 						) { | ||||
| 							settings[r.type] = cachedMerge(settings[r.type], r.value); | ||||
| 						} else { | ||||
| 							settings[r.type] = r.value; | ||||
| 						} | ||||
| 					} | ||||
| 
 | ||||
| 					let postLoaders, normalLoaders, preLoaders; | ||||
| 
 | ||||
| 					const continueCallback = needCalls(3, err => { | ||||
| 						if (err) { | ||||
| 							return callback(err); | ||||
| 						} | ||||
| 						const allLoaders = postLoaders; | ||||
| 						for (const loader of loaders) allLoaders.push(loader); | ||||
| 						for (const loader of normalLoaders) allLoaders.push(loader); | ||||
| 						for (const loader of preLoaders) allLoaders.push(loader); | ||||
| 						const type = settings.type; | ||||
| 						const resolveOptions = settings.resolve; | ||||
| 						Object.assign(data.createData, { | ||||
| 							request: stringifyLoadersAndResource(allLoaders, resource), | ||||
| 							userRequest, | ||||
| 							rawRequest: request, | ||||
| 							loaders: allLoaders, | ||||
| 							resource, | ||||
| 							matchResource, | ||||
| 							resourceResolveData, | ||||
| 							settings, | ||||
| 							type, | ||||
| 							parser: this.getParser(type, settings.parser), | ||||
| 							generator: this.getGenerator(type, settings.generator), | ||||
| 							resolveOptions | ||||
| 						}); | ||||
| 						callback(); | ||||
| 					}); | ||||
| 					this.resolveRequestArray( | ||||
| 						contextInfo, | ||||
| 						this.context, | ||||
| 						useLoadersPost, | ||||
| 						loaderResolver, | ||||
| 						resolveContext, | ||||
| 						(err, result) => { | ||||
| 							postLoaders = result; | ||||
| 							continueCallback(err); | ||||
| 						} | ||||
| 					); | ||||
| 					this.resolveRequestArray( | ||||
| 						contextInfo, | ||||
| 						this.context, | ||||
| 						useLoaders, | ||||
| 						loaderResolver, | ||||
| 						resolveContext, | ||||
| 						(err, result) => { | ||||
| 							normalLoaders = result; | ||||
| 							continueCallback(err); | ||||
| 						} | ||||
| 					); | ||||
| 					this.resolveRequestArray( | ||||
| 						contextInfo, | ||||
| 						this.context, | ||||
| 						useLoadersPre, | ||||
| 						loaderResolver, | ||||
| 						resolveContext, | ||||
| 						(err, result) => { | ||||
| 							preLoaders = result; | ||||
| 							continueCallback(err); | ||||
| 						} | ||||
| 					); | ||||
| 				}); | ||||
| 
 | ||||
| 				this.resolveRequestArray( | ||||
| 					contextInfo, | ||||
| 					context, | ||||
| 					elements, | ||||
| 					loaderResolver, | ||||
| 					resolveContext, | ||||
| 					(err, result) => { | ||||
| 						if (err) return continueCallback(err); | ||||
| 						loaders = result; | ||||
| 						continueCallback(); | ||||
| 					} | ||||
| 				); | ||||
| 
 | ||||
| 				if ( | ||||
| 					unresolvedResource === "" || | ||||
| 					unresolvedResource.charCodeAt(0) === 63 | ||||
| 				) { | ||||
| 					// 63 === "?"
 | ||||
| 					resource = unresolvedResource; | ||||
| 					return continueCallback(); | ||||
| 				} | ||||
| 
 | ||||
| 				normalResolver.resolve( | ||||
| 					contextInfo, | ||||
| 					context, | ||||
| 					unresolvedResource, | ||||
| 					resolveContext, | ||||
| 					(err, resolvedResource, resolvedResourceResolveData) => { | ||||
| 						if (err) return continueCallback(err); | ||||
| 
 | ||||
| 						// TODO remove this when enhanced-resolve supports fileDependencies
 | ||||
| 						if (resolvedResource) { | ||||
| 							fileDependencies.add(resolvedResource); | ||||
| 						} | ||||
| 
 | ||||
| 						resource = resolvedResource; | ||||
| 						resourceResolveData = resolvedResourceResolveData; | ||||
| 						continueCallback(); | ||||
| 					} | ||||
| 				); | ||||
| 			} | ||||
| 		); | ||||
|  | @ -484,7 +517,7 @@ class NormalModuleFactory extends ModuleFactory { | |||
| 		resolveContext, | ||||
| 		callback | ||||
| 	) { | ||||
| 		if (array.length === 0) return callback(null, []); | ||||
| 		if (array.length === 0) return callback(null, array); | ||||
| 		asyncLib.map( | ||||
| 			array, | ||||
| 			(item, callback) => { | ||||
|  | @ -503,7 +536,7 @@ class NormalModuleFactory extends ModuleFactory { | |||
| 								contextInfo, | ||||
| 								context, | ||||
| 								item.loader + "-loader", | ||||
| 								{}, | ||||
| 								resolveContext, | ||||
| 								err2 => { | ||||
| 									if (!err2) { | ||||
| 										err.message = | ||||
|  | @ -521,18 +554,15 @@ class NormalModuleFactory extends ModuleFactory { | |||
| 						} | ||||
| 						if (err) return callback(err); | ||||
| 
 | ||||
| 						const optionsOnly = item.options | ||||
| 							? { | ||||
| 									options: item.options | ||||
| 							  } | ||||
| 							: undefined; | ||||
| 
 | ||||
| 						const resolved = Object.assign( | ||||
| 							{}, | ||||
| 							item, | ||||
| 							identToLoaderRequest(result), | ||||
| 							optionsOnly | ||||
| 						); | ||||
| 						const parsedResult = identToLoaderRequest(result); | ||||
| 						const resolved = { | ||||
| 							loader: parsedResult.loader, | ||||
| 							options: | ||||
| 								item.options === undefined | ||||
| 									? parsedResult.options | ||||
| 									: item.options, | ||||
| 							ident: item.options === undefined ? undefined : item.ident | ||||
| 						}; | ||||
| 
 | ||||
| 						// TODO remove this when enhanced-resolve supports fileDependencies
 | ||||
| 						if (resolved.loader) { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue