webpack 运行时模版 第 四 节 /lib/RuntimeTemplate.js


moduleNamespace 函数

用于生成访问模块命名空间的表达式(同步)。

  • module:要访问的模块对象,类型为 Module
  • chunkGraph:Webpack 内部的 chunkGraph 实例,用于获取模块 ID 等信息
  • request:模块的请求路径,用于注释和调试信息
  • strict(可选):是否处于 strict ESM 模式,影响导出类型判断
  • weak(可选):是否为弱依赖,若模块 ID 不存在则生成错误代码而非抛出异常
  • runtimeRequirements(可选):传入后,会被填充所需的运行时代码(如 __webpack_require__, __webpack_require__.n, 等)
  • 返回值:一个用于访问该模块命名空间的 JS 表达式字符串

moduleNamespacePromise 函数

用于生成异步访问模块命名空间的 Promise 表达式。

  • chunkGraph:Webpack 的 ChunkGraph 实例
  • block(可选):异步依赖块(如 import()),类型为 AsyncDependenciesBlock
  • module:目标模块对象,类型为 Module
  • request:模块请求路径,主要用于生成注释
  • message:用于生成注释的信息,提示加载内容
  • strict(可选):是否为 strict 模式,影响导出类型处理
  • weak(可选):是否为弱依赖,决定是否在模块 ID 缺失时生成警告代码
  • runtimeRequirements(可选):用于收集运行时依赖
  • 返回值 :一个 Promise 表达式,最终 .then() 得到模块命名空间对象
js 复制代码
/**
 * 根据模块信息生成访问该模块命名空间对象的表达式字符串。
 *
 * @param {Object} options 配置项
 * @param {Module} options.module 当前依赖的模块
 * @param {ChunkGraph} options.chunkGraph ChunkGraph 实例,用于获取模块 ID 等信息
 * @param {string} options.request 当前模块请求路径,用于生成注释
 * @param {boolean=} options.strict 是否开启 strict ESM 模式(影响 exportsType 判断)
 * @param {boolean=} options.weak 是否是弱依赖(模块可能不存在)
 * @param {RuntimeRequirements=} options.runtimeRequirements 若传入,则会被填充依赖的运行时变量
 * @returns {string} 表达式字符串,用于访问模块命名空间(如 require(), __webpack_require__, fakeNamespace 等)
 */
moduleNamespace({
	module,
	chunkGraph,
	request,
	strict,
	weak,
	runtimeRequirements
}) {
	// 模块不存在时,返回缺失模块的表达式
	if (!module) {
		return this.missingModule({ request });
	}

	// 模块存在但未被分配 moduleId
	if (chunkGraph.getModuleId(module) === null) {
		if (weak) {
			// 弱依赖可以容忍没有 id,返回一个可报错表达式
			return this.weakError({
				module,
				chunkGraph,
				request,
				type: "expression"
			});
		}
		// 非弱依赖没有 id 说明是构建错误
		throw new Error(
			`RuntimeTemplate.moduleNamespace(): ${noModuleIdErrorMessage(
				module,
				chunkGraph
			)}`
		);
	}

	// 获取模块 ID 的表达式
	const moduleId = this.moduleId({
		module,
		chunkGraph,
		request,
		weak
	});

	// 获取模块的导出类型(例如 default-only, namespace 等)
	const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);

	switch (exportsType) {
		case "namespace":
			// 直接返回模块原始对象(即命名空间对象)
			return this.moduleRaw({
				module,
				chunkGraph,
				request,
				weak,
				runtimeRequirements
			});
		case "default-with-named":
			// 模块既有默认导出也有命名导出,构造 fake 命名空间,标志为 3
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 3)`;
		case "default-only":
			// 只有默认导出,标志为 1
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 1)`;
		case "dynamic":
			// 动态导出(运行时决定),标志为 7
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			return `${RuntimeGlobals.createFakeNamespaceObject}(${moduleId}, 7)`;
	}
}
/**
 * 生成异步访问模块命名空间对象的 Promise 表达式。
 *
 * @param {Object} options 配置项
 * @param {ChunkGraph} options.chunkGraph ChunkGraph 实例
 * @param {AsyncDependenciesBlock=} options.block 当前模块所在的异步依赖块
 * @param {Module} options.module 需要异步加载的模块
 * @param {string} options.request 请求字符串,用于注释
 * @param {string} options.message 异步模块加载时用于注释的提示信息
 * @param {boolean=} options.strict 是否为 strict 模式,影响模块 exportsType 判断
 * @param {boolean=} options.weak 是否为弱依赖(不强制要求模块存在)
 * @param {RuntimeRequirements=} options.runtimeRequirements 若传入,则会被填充依赖的运行时变量
 * @returns {string} 返回一个 Promise 表达式,最终 `.then` 得到模块的命名空间对象
 */
moduleNamespacePromise({
	chunkGraph,
	block,
	module,
	request,
	message,
	strict,
	weak,
	runtimeRequirements
}) {
	// 模块不存在,返回缺失模块的 Promise 表达式
	if (!module) {
		return this.missingModulePromise({ request });
	}

	// 获取模块 ID
	const moduleId = chunkGraph.getModuleId(module);
	if (moduleId === null) {
		if (weak) {
			// 弱依赖可以容忍没有 id,返回运行时报错表达式
			return this.weakError({
				module,
				chunkGraph,
				request,
				type: "promise"
			});
		}
		// 非弱依赖没有 id 抛出异常
		throw new Error(
			`RuntimeTemplate.moduleNamespacePromise(): ${noModuleIdErrorMessage(
				module,
				chunkGraph
			)}`
		);
	}

	// 获取异步加载该模块的 Promise 表达式(如 import())
	const promise = this.blockPromise({
		chunkGraph,
		block,
		message,
		runtimeRequirements
	});

	let appending; // 用于拼接的 .then 链
	let idExpr = JSON.stringify(moduleId); // moduleId 的 JSON 表达形式
	const comment = this.comment({ request }); // 注释字符串
	let header = ""; // 弱依赖时的前置 ID 检查逻辑代码

	if (weak) {
		// 如果 id 表达式太长则使用临时变量
		if (idExpr.length > 8) {
			header += `var id = ${idExpr}; `;
			idExpr = "id";
		}
		// 添加对 Runtime.moduleFactories 的依赖
		runtimeRequirements.add(RuntimeGlobals.moduleFactories);
		// 添加工厂存在性检查逻辑
		header += `if(!${
			RuntimeGlobals.moduleFactories
		}[${idExpr}]) { ${this.weakError({
			module,
			chunkGraph,
			request,
			idExpr,
			type: "statements"
		})} } `;
	}

	// 获取模块 ID 的表达式
	const moduleIdExpr = this.moduleId({
		module,
		chunkGraph,
		request,
		weak
	});

	// 获取模块导出类型
	const exportsType = module.getExportsType(chunkGraph.moduleGraph, strict);
	let fakeType = 16; // 标记为 async fake namespace 对象

	switch (exportsType) {
		case "namespace":
			if (header) {
				// 有弱依赖检查逻辑,生成包裹的 .then
				const rawModule = this.moduleRaw({
					module,
					chunkGraph,
					request,
					weak,
					runtimeRequirements
				});
				appending = `.then(${this.basicFunction(
					"",
					`${header}return ${rawModule};`
				)})`;
			} else {
				// 否则直接 require 加载
				runtimeRequirements.add(RuntimeGlobals.require);
				appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
			}
			break;

		case "dynamic":
			fakeType |= 4;
		// fallthrough
		case "default-with-named":
			fakeType |= 2;
		// fallthrough
		case "default-only":
			runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
			if (chunkGraph.moduleGraph.isAsync(module)) {
				// 异步模块需要两层 then
				if (header) {
					const rawModule = this.moduleRaw({
						module,
						chunkGraph,
						request,
						weak,
						runtimeRequirements
					});
					appending = `.then(${this.basicFunction(
						"",
						`${header}return ${rawModule};`
					)})`;
				} else {
					runtimeRequirements.add(RuntimeGlobals.require);
					appending = `.then(${RuntimeGlobals.require}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}))`;
				}
				appending += `.then(${this.returningFunction(
					`${RuntimeGlobals.createFakeNamespaceObject}(m, ${fakeType})`,
					"m"
				)})`;
			} else {
				// 同步模块一层 then 即可
				fakeType |= 1;
				if (header) {
					const returnExpression = `${RuntimeGlobals.createFakeNamespaceObject}(${moduleIdExpr}, ${fakeType})`;
					appending = `.then(${this.basicFunction(
						"",
						`${header}return ${returnExpression};`
					)})`;
				} else {
					appending = `.then(${RuntimeGlobals.createFakeNamespaceObject}.bind(${RuntimeGlobals.require}, ${comment}${idExpr}, ${fakeType}))`;
				}
			}
			break;
	}

	// 返回最终组合好的 Promise 表达式
	return `${promise || "Promise.resolve()"}${appending}`;
}
相关推荐
fakaifa3 分钟前
【最新版】西陆健身系统源码全开源+uniapp前端
前端·小程序·uni-app·开源·php·约课小程序·健身小程序
南囝coding8 分钟前
关于我的第一个产品!
前端·后端·产品
iOS阿玮15 分钟前
别等了,今天是Xcode15时代的最后一天。
前端·app·apple
沙尘暴炒饭21 分钟前
vuex持久化vuex-persistedstate,存储的数据刷新页面后导致数据丢失
开发语言·前端·javascript
2401_8370885023 分钟前
CSS清楚默认样式
前端·javascript·css
zwjapple35 分钟前
React 的 useEffect 清理函数详解
前端·react.js·前端框架
Jewel1051 小时前
如何配置Telegram Mini-App?
前端·vue.js·app
s11show_1631 小时前
hz修改后台新增keyword功能
android·java·前端
二个半engineer2 小时前
Web常见攻击方式及防御措施
前端
co松柏2 小时前
程序员必备——AI 画技术图技巧
前端·后端·ai编程