js
复制代码
/**
* 生成一个判断 runtime 条件的表达式字符串。
* 常用于确定某段 runtime-specific 的代码是否应在当前 runtime 下执行。
*
* @param {Object} options
* @param {ChunkGraph} options.chunkGraph - ChunkGraph 实例,用于获取模块 runtimeId。
* @param {RuntimeSpec | boolean | undefined} options.runtimeCondition - 条件 runtime。
* @param {RuntimeSpec} options.runtime - 当前运行时上下文。
* @param {Set<string>} options.runtimeRequirements - 收集 runtime 的依赖变量。
* @returns {string} 表达式字符串,例如:"__webpack_require__.r === 'main'"
*/
runtimeConditionExpression({
chunkGraph,
runtimeCondition,
runtime,
runtimeRequirements
}) {
// 如果没有设置 runtime 条件,默认始终为 true
if (runtimeCondition === undefined) return "true";
// 如果 runtime 条件是布尔值,直接转换为字符串返回
if (typeof runtimeCondition === "boolean") return `${runtimeCondition}`;
// 正向匹配的 runtimeId 集合
const positiveRuntimeIds = new Set();
forEachRuntime(runtimeCondition, runtime =>
positiveRuntimeIds.add(`${chunkGraph.getRuntimeId(/** @type {string} */ (runtime))}`)
);
// 反向排除的 runtimeId 集合
const negativeRuntimeIds = new Set();
forEachRuntime(subtractRuntime(runtime, runtimeCondition), runtime =>
negativeRuntimeIds.add(`${chunkGraph.getRuntimeId(/** @type {string} */ (runtime))}`)
);
// 注册对 __webpack_require__.r 的依赖
runtimeRequirements.add(RuntimeGlobals.runtimeId);
// 生成条件匹配表达式,例如 __webpack_require__.r === "main"
return compileBooleanMatcher.fromLists(
Array.from(positiveRuntimeIds),
Array.from(negativeRuntimeIds)
)(RuntimeGlobals.runtimeId);
},
/**
* 构造模块导入语句,返回导入声明 + default 导出兼容语句。
*
* @param {Object} options
* @param {boolean} options.update - 是否是更新变量(false 表示声明)。
* @param {Module=} options.module - 被导入的模块。
* @param {ChunkGraph} options.chunkGraph - ChunkGraph 实例。
* @param {string} options.request - 请求路径。
* @param {string} options.importVar - 被赋值的变量名。
* @param {Module} options.originModule - 发起导入的模块。
* @param {boolean=} options.weak - 是否为弱依赖。
* @param {Set<string>} options.runtimeRequirements - runtime 依赖收集器。
* @returns {[string, string]} 导入语句 + 兼容 default 变量定义。
*/
importStatement({
update,
module,
chunkGraph,
request,
importVar,
originModule,
weak,
runtimeRequirements
}) {
// 如果没有 module,说明模块缺失,输出错误提示代码
if (!module) {
return [
this.missingModuleStatement({ request }),
""
];
}
// module 没有 ID,说明未包含在 chunk 中
if (chunkGraph.getModuleId(module) === null) {
// 弱依赖返回 warning,否则抛错
if (weak) {
return [
this.weakError({ module, chunkGraph, request, type: "statements" }),
""
];
}
throw new Error(
`RuntimeTemplate.importStatement(): ${noModuleIdErrorMessage(module, chunkGraph)}`
);
}
// 获取模块 ID(可能会添加到 runtimeRequirements)
const moduleId = this.moduleId({
module,
chunkGraph,
request,
weak
});
// 如果不是 update,就声明变量
const optDeclaration = update ? "" : "var ";
// 判断被导入模块的导出类型
const exportsType = module.getExportsType(
chunkGraph.moduleGraph,
/** @type {BuildMeta} */ (originModule.buildMeta).strictHarmonyModule
);
// 添加对 __webpack_require__ 的 runtime 依赖
runtimeRequirements.add(RuntimeGlobals.require);
// 正常的导入语句
const importContent = `/* harmony import */ ${optDeclaration}${importVar} = ${RuntimeGlobals.require}(${moduleId});\n`;
// 如果是 dynamic 类型(即同时支持 CommonJS 和 ESM),兼容 default
if (exportsType === "dynamic") {
runtimeRequirements.add(RuntimeGlobals.compatGetDefaultExport);
return [
importContent,
`/* harmony import */ ${optDeclaration}${importVar}_default = /*#__PURE__*/${RuntimeGlobals.compatGetDefaultExport}(${importVar});\n`
];
}
// 非 dynamic 类型不需要处理 default
return [importContent, ""];
},
/**
* 从一个 import 的模块中导出指定成员。
*
* @param {Object} options
* @param {ModuleGraph} options.moduleGraph - 模块依赖图。
* @param {Module=} options.module - 被导出的模块。
* @param {string} options.request - 请求路径。
* @param {string|string[]=} options.exportName - 要导出的成员名称。
* @param {Module} options.originModule - 发起导出的模块。
* @param {boolean} options.asiSafe - 是否是 ASI 安全(自动分号插入)。
* @param {boolean} options.isCall - 是否用于调用场景。
* @param {boolean=} options.callContext - 是否保留 this 绑定。
* @param {boolean} options.defaultInterop - 是否处理 default 兼容。
* @param {string} options.importVar - 模块变量名。
* @param {InitFragment[]} options.initFragments - 初始化片段收集器。
* @param {RuntimeSpec} options.runtime - 当前运行时上下文。
* @param {Set<string>} options.runtimeRequirements - runtime 依赖收集器。
* @returns {string} 最终生成的访问语句。
*/
exportFromImport({
moduleGraph,
module,
request,
exportName,
originModule,
asiSafe,
isCall,
callContext,
defaultInterop,
importVar,
initFragments,
runtime,
runtimeRequirements
}) {
// 模块缺失,返回报错表达式
if (!module) return this.missingModule({ request });
// 标准化 exportName 为数组
if (!Array.isArray(exportName)) {
exportName = exportName ? [exportName] : [];
}
const exportsType = module.getExportsType(
moduleGraph,
/** @type {BuildMeta} */ (originModule.buildMeta).strictHarmonyModule
);
// 处理 defaultInterop 情况
if (defaultInterop) {
if (exportName.length > 0 && exportName[0] === "default") {
switch (exportsType) {
case "dynamic":
// 动态模块:default 调用返回对象
if (isCall) {
return `${importVar}_default()${propertyAccess(exportName, 1)}`;
}
return asiSafe
? `(${importVar}_default()${propertyAccess(exportName, 1)})`
: asiSafe === false
? `;(${importVar}_default()${propertyAccess(exportName, 1)})`
: `${importVar}_default.a${propertyAccess(exportName, 1)}`;
case "default-only":
case "default-with-named":
// 直接剥离 default,改为访问子字段
exportName = exportName.slice(1);
break;
}
} else if (exportName.length > 0) {
if (exportsType === "default-only") {
return `/* non-default import from non-esm module */undefined${propertyAccess(exportName, 1)}`;
} else if (exportsType !== "namespace" && exportName[0] === "__esModule") {
return "/* __esModule */true";
}
} else if (
exportsType === "default-only" ||
exportsType === "default-with-named"
) {
// 使用 createFakeNamespaceObject 生成 fake namespace
runtimeRequirements.add(RuntimeGlobals.createFakeNamespaceObject);
initFragments.push(
new InitFragment(
`var ${importVar}_namespace_cache;\n`,
InitFragment.STAGE_CONSTANTS,
-1,
`${importVar}_namespace_cache`
)
);
return `/*#__PURE__*/ ${
asiSafe ? "" : asiSafe === false ? ";" : "Object"
}(${importVar}_namespace_cache || (${importVar}_namespace_cache = ${
RuntimeGlobals.createFakeNamespaceObject
}(${importVar}${exportsType === "default-only" ? "" : ", 2"})))`;
}
}
if (exportName.length > 0) {
const exportsInfo = moduleGraph.getExportsInfo(module);
const used = exportsInfo.getUsedName(exportName, runtime);
// 没有使用的 export,输出 undefined + 注释
if (!used) {
const comment = Template.toNormalComment(`unused export ${propertyAccess(exportName)}`);
return `${comment} undefined`;
}
const comment = equals(used, exportName)
? ""
: `${Template.toNormalComment(propertyAccess(exportName))} `;
const access = `${importVar}${comment}${propertyAccess(used)}`;
if (isCall && callContext === false) {
// 保留 this 上下文
return asiSafe
? `(0,${access})`
: asiSafe === false
? `;(0,${access})`
: `/*#__PURE__*/Object(${access})`;
}
return access;
}
// 如果没导出任何字段,直接返回模块变量本身
return importVar;
}