webpack 格式化模块工厂 第 三 节

cleanupForCache() :

  • 作用:清理缓存时调用的清理方法。遍历所有恢复的不安全缓存条目,执行相关的清理操作,包括清除模块的 Chunk 图、模块图,并执行模块的自清理操作。
js 复制代码
	// 清理缓存时需要调用的清理方法
	cleanupForCache() {
		// 遍历所有恢复的不安全缓存条目,执行相关清理操作
		for (const module of this._restoredUnsafeCacheEntries) {
			ChunkGraph.clearChunkGraphForModule(module); // 清除模块的 Chunk 图
			ModuleGraph.clearModuleGraphForModule(module); // 清除模块的模块图
			module.cleanupForCache(); // 执行模块自身的清理操作
		}
	}

create(data, callback) :

  • 作用 :创建模块。通过处理依赖项,解析请求,并根据解析结果生成模块。它依赖 beforeResolvefactorize 钩子函数,执行相应的钩子操作,最终返回包含模块、文件依赖、缺失依赖等信息的结果。
js 复制代码
/**
	 * 创建模块的工厂方法
	 * @param {ModuleFactoryCreateData} data 数据对象
	 * @param {function((Error | null)=, ModuleFactoryResult=): void} callback 回调函数
	 * @returns {void}
	 */
	create(data, callback) {
		// 获取依赖项数组
		const dependencies = /** @type {ModuleDependency[]} */ (data.dependencies);
		const context = data.context || this.context; // 上下文
		const resolveOptions = data.resolveOptions || EMPTY_RESOLVE_OPTIONS; // 解析选项
		const dependency = dependencies[0]; // 获取第一个依赖项
		const request = dependency.request; // 依赖的请求
		const assertions = dependency.assertions; // 依赖的断言
		const dependencyType = dependency.category || ""; // 依赖的类型
		const contextInfo = data.contextInfo; // 上下文信息
		// 文件依赖、缺失依赖、上下文依赖集合
		const fileDependencies = new LazySet();
		const missingDependencies = new LazySet();
		const contextDependencies = new LazySet();
		/** @type {ResolveData} */
		const resolveData = {
			contextInfo,
			resolveOptions,
			context,
			request,
			assertions,
			dependencies,
			dependencyType,
			fileDependencies,
			missingDependencies,
			contextDependencies,
			createData: {},
			cacheable: true // 缓存标志
		};
		// 调用 beforeResolve 钩子
		this.hooks.beforeResolve.callAsync(resolveData, (err, result) => {
			if (err) {
				// 如果出现错误,返回并且传递依赖项
				return callback(err, {
					fileDependencies,
					missingDependencies,
					contextDependencies,
					cacheable: false
				});
			}

			// 如果结果为 false,表示忽略此模块
			if (result === false) {
				/** @type {ModuleFactoryResult} */
				const factoryResult = {
					fileDependencies,
					missingDependencies,
					contextDependencies,
					cacheable: resolveData.cacheable
				};

				if (resolveData.ignoredModule) {
					factoryResult.module = resolveData.ignoredModule;
				}

				return callback(null, factoryResult);
			}

			// 如果结果为对象,则抛出错误
			if (typeof result === "object")
				throw new Error(
					deprecationChangedHookMessage(
						"beforeResolve",
						this.hooks.beforeResolve
					)
				);

			// 调用 factorize 钩子生成模块
			this.hooks.factorize.callAsync(resolveData, (err, module) => {
				if (err) {
					// 如果出现错误,返回并且传递依赖项
					return callback(err, {
						fileDependencies,
						missingDependencies,
						contextDependencies,
						cacheable: false
					});
				}

				/** @type {ModuleFactoryResult} */
				const factoryResult = {
					module,
					fileDependencies,
					missingDependencies,
					contextDependencies,
					cacheable: resolveData.cacheable
				};

				callback(null, factoryResult);
			});
		});
	}

resolveResource(contextInfo, context, unresolvedResource, resolver, resolveContext, callback) :

  • 作用 :解析资源。使用给定的解析器 resolver 来解析资源路径。如果解析失败,调用 _resolveResourceErrorHints 方法生成错误提示,并在解析成功后返回解析结果。
js 复制代码
	/**
	 * 解析资源方法
	 * @param {ModuleFactoryCreateDataContextInfo} contextInfo 上下文信息
	 * @param {string} context 上下文
	 * @param {string} unresolvedResource 未解析的资源
	 * @param {ResolverWithOptions} resolver 解析器
	 * @param {ResolveContext} resolveContext 解析上下文
	 * @param {(err: null | Error, res?: string | false, req?: ResolveRequest) => void} callback 回调函数
	 */
	resolveResource(
		contextInfo,
		context,
		unresolvedResource,
		resolver,
		resolveContext,
		callback
	) {
		// 调用解析器的 resolve 方法解析资源
		resolver.resolve(
			contextInfo,
			context,
			unresolvedResource,
			resolveContext,
			(err, resolvedResource, resolvedResourceResolveData) => {
				if (err) {
					// 如果解析失败,调用 _resolveResourceErrorHints 生成错误提示
					return this._resolveResourceErrorHints(
						err,
						contextInfo,
						context,
						unresolvedResource,
						resolver,
						resolveContext,
						(err2, hints) => {
							if (err2) {
								err.message += `\n发生了解析附加提示时的致命错误:${err2.message}`;
								err.stack += `\n发生了解析附加提示时的致命错误:\n${err2.stack}`;
								return callback(err);
							}
							if (hints && hints.length > 0) {
								err.message += `\n${hints.join("\n\n")}`;
							}

							// 检查扩展名是否缺少前导点(例如 "js" 而不是 ".js")
							let appendResolveExtensionsHint = false;
							const specifiedExtensions = Array.from(
								resolver.options.extensions
							);
							const expectedExtensions = specifiedExtensions.map(extension => {
								if (LEADING_DOT_EXTENSION_REGEX.test(extension)) {
									appendResolveExtensionsHint = true;
									return `.${extension}`;
								}
								return extension;
							});
							if (appendResolveExtensionsHint) {
								err.message += `\n你是不是在 'resolve.extensions' 中遗漏了前导点?你是否打算将 '${JSON.stringify(
									expectedExtensions
								)}' 替换为 '${JSON.stringify(specifiedExtensions)}'?`;
							}

							callback(err);
						}
					);
				}
				// 返回解析的结果
				callback(err, resolvedResource, resolvedResourceResolveData);
			}
		);
	}

_resolveResourceErrorHints(error, contextInfo, context, unresolvedResource, resolver, resolveContext, callback) :

  • 作用:当解析失败时,生成详细的错误提示。它通过多个回调异步地提供错误解决的建议,比如缺少扩展名、错误的资源路径或强制扩展名的配置问题等。
js 复制代码
	/**
	 * 解析资源错误时生成提示信息
	 * @param {Error} error 错误信息
	 * @param {ModuleFactoryCreateDataContextInfo} contextInfo 上下文信息
	 * @param {string} context 上下文
	 * @param {string} unresolvedResource 未解析的资源
	 * @param {ResolverWithOptions} resolver 解析器
	 * @param {ResolveContext} resolveContext 解析上下文
	 * @param {Callback<string[]>} callback 回调函数
	 * @private
	 */
	_resolveResourceErrorHints(
		error,
		contextInfo,
		context,
		unresolvedResource,
		resolver,
		resolveContext,
		callback
	) {
		// 异步执行多个回调并获取错误提示
		asyncLib.parallel(
			[
				callback => {
					if (!resolver.options.fullySpecified) return callback();
					// 在 fullySpecified 为 false 的情况下,尝试重新解析资源
					resolver
						.withOptions({
							fullySpecified: false
						})
						.resolve(
							contextInfo,
							context,
							unresolvedResource,
							resolveContext,
							(err, resolvedResource) => {
								if (!err && resolvedResource) {
									const resource = parseResource(resolvedResource).path.replace(
										/^.*[\\/]/,
										""
									);
									return callback(
										null,
										`你是否想要使用 '${resource}'?
突破性更改:请求 '${unresolvedResource}' 失败,仅仅因为它被解析为完全指定的(可能是因为来源是严格的 EcmaScript 模块,如具有 javascript 类型的模块,'*.mjs' 文件,或包含 '"type": "module"' 的 '*.js' 文件)。
此请求需要附加扩展名才能完全指定。`
									);
								}
								callback();
							}
						);
				},
				callback => {
					if (!resolver.options.enforceExtension) return callback();
					// 在 enforceExtension 为 false 的情况下,尝试重新解析资源
					resolver
						.withOptions({
							enforceExtension: false,
							extensions: []
						})
						.resolve(
							contextInfo,
							context,
							unresolvedResource,
							resolveContext,
							(err, resolvedResource) => {
								if (!err && resolvedResource) {
									let hint = "";
									const match = /(\.[^.]+)(\?|$)/.exec(unresolvedResource);
									if (match) {
										const fixedRequest = unresolvedResource.replace(
											/(\.[^.]+)(\?|$)/,
											"$2"
										);
										hint = resolver.options.extensions.has(match[1])
											? `你是否想使用 '${fixedRequest}'?`
											: `你是否想使用 '${fixedRequest}'? 另请注意, '${match[1]}' 还未添加到 'resolve.extensions' 中,需将其添加进来以使其生效。`;
									} else {
										hint =
											"你是否打算省略扩展名或移除 'resolve.enforceExtension' 设置?";
									}
									return callback(
										null,
										`请求 '${unresolvedResource}' 失败,仅仅因为 'resolve.enforceExtension' 被启用。
${hint}
现在无法在请求中包含扩展名。你是否打算在请求中强制包括扩展名,并使用 'resolve.extensions: []' 来强制执行此操作?`
									);
								}
								callback();
							}
						);
				},
				callback => {
					// 解析路径中的相对请求
					if (
						/^\.\.?\//.test(unresolvedResource) ||
						resolver.options.preferRelative
					) {
						return callback();
					}
					resolver.resolve(
						contextInfo,
						context,
						`./${unresolvedResource}`,
						resolveContext,
						(err, resolvedResource) => {
							if (err || !resolvedResource) return callback();
							const moduleDirectories = resolver.options.modules
								.map(m => (Array.isArray(m) ? m.join(", ") : m))
								.join(", ");
							callback(
								null,
								`你是否想使用 './${unresolvedResource}'?
请求应该以 './' 开头,表示在当前目录解析。
如果不能更改源代码,你还可以使用 'preferRelative' 选项尝试在当前目录解析这些请求。`
							);
						}
					);
				}
			],
			(err, hints) => {
				if (err) return callback(err);
				callback(null, /** @type {string[]} */(hints).filter(Boolean));
			}
		);
	}

getParser(type, parserOptions = EMPTY_PARSER_OPTIONS) :

  • 作用 :获取解析器。根据类型 type 和解析器选项 parserOptions,从缓存中获取或创建一个新的解析器。通过缓存机制优化性能,避免重复创建解析器。
js 复制代码
	

	/**
	 * 解析请求数组
	 * @param {string} type 类型
	 * @param {ParserOptions} parserOptions 解析器选项
	 * @returns {Parser} 解析器
	 */
	getParser(type, parserOptions = EMPTY_PARSER_OPTIONS) {
		let cache = this.parserCache.get(type);

		if (cache === undefined) {
			cache = new WeakMap();
			this.parserCache.set(type, cache);
		}

		let parser = cache.get(parserOptions);

		if (parser === undefined) {
			parser = this.createParser(type, parserOptions);
			cache.set(parserOptions, parser);
		}

		return parser;
	}
相关推荐
虾球xz16 分钟前
游戏引擎学习第204天
前端·学习·游戏引擎
WindrunnerMax32 分钟前
深感一无所长,准备试着从零开始写个富文本编辑器
前端·javascript·github
Richard201238 分钟前
Linux Command Recap
linux·前端
尖椒土豆sss38 分钟前
原子化 css 框架:Tailwind Css 入门学习
前端·css·postcss
iOS阿玮1 小时前
2025年第一季度3.2f求助排名第一,你还敢违规操作么?
前端·app·apple
gyratesky1 小时前
用4种方法实现内发光的多边形区域
前端·数据可视化
敲代码的彭于晏2 小时前
前端上传与下载基础:Blob、File与ArrayBuffer详解
前端
緑水長流2 小时前
什么是Promise?什么是async和await?
前端·javascript·vue.js
Mintopia2 小时前
Three.js 相机(Camera)的使用详解
前端·javascript·three.js