-
插件名称定义为
JavascriptMetaInfoPlugin
,并应用于 Webpack 的编译流程中。 -
监听
compiler.hooks.compilation
钩子,在编译过程中注入逻辑。 -
为三种 JavaScript 模块类型(AUTO、DYNAMIC、ESM)注册 parser 钩子,统一处理逻辑。
-
在解析阶段监听
eval()
的调用:- 标记
buildInfo.moduleConcatenationBailout = "eval()"
,阻止模块作用域连接优化。 - 设置
buildInfo.usingEval = true
,记录模块使用了eval()
。 - 使用
InnerGraph.getTopLevelSymbol()
尝试获取当前上下文的顶层符号。 - 如果存在顶层符号,则调用
InnerGraph.addUsage()
记录引用关系。 - 否则调用
InnerGraph.bailout()
,中断内部图优化。
- 标记
-
在解析结束时(parser.finish)记录顶层变量声明:
- 初始化
buildInfo.topLevelDeclarations
为一个Set
。 - 遍历作用域中定义的变量名。
- 如果变量没有自由变量信息(非闭包变量),视为顶层声明加入集合。
- 初始化
-
最终导出插件类用于 Webpack 插件系统使用。
js
/*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Sergey Melyukov @smelukov
*/
// 这是插件的许可证信息和作者署名
"use strict";
// 启用严格模式,提高代码的规范性和安全性
const {
JAVASCRIPT_MODULE_TYPE_AUTO,
JAVASCRIPT_MODULE_TYPE_DYNAMIC,
JAVASCRIPT_MODULE_TYPE_ESM
} = require("./ModuleTypeConstants");
// 引入三种 JavaScript 模块类型常量:自动识别、动态模块、ES 模块
const InnerGraph = require("./optimize/InnerGraph");
// 引入 InnerGraph,用于分析模块的内部依赖关系图(变量引用、作用域等)
/** @typedef {import("./Compiler")} Compiler */
// 引入类型定义:Compiler
/** @typedef {import("./Module").BuildInfo} BuildInfo */
// 引入类型定义:模块的构建信息 BuildInfo
/** @typedef {import("./javascript/JavascriptParser")} JavascriptParser */
// 引入类型定义:JavaScript 语法解析器 JavascriptParser
const PLUGIN_NAME = "JavascriptMetaInfoPlugin";
// 定义插件名称常量,后续钩子绑定中使用
class JavascriptMetaInfoPlugin {
/**
* 插件应用函数
* @param {Compiler} compiler 编译器实例
* @returns {void}
*/
apply(compiler) {
// 在 compilation 阶段注册钩子
compiler.hooks.compilation.tap(
PLUGIN_NAME,
(compilation, { normalModuleFactory }) => {
/**
* 处理 JavaScript 解析器的钩子函数
* @param {JavascriptParser} parser 解析器实例
* @returns {void}
*/
const handler = parser => {
// 为 eval() 函数调用绑定钩子
parser.hooks.call.for("eval").tap(PLUGIN_NAME, () => {
const buildInfo =
/** @type {BuildInfo} */
(parser.state.module.buildInfo);
// 使用 eval() 会导致模块无法进行作用域提升优化
buildInfo.moduleConcatenationBailout = "eval()";
// 标记当前模块使用了 eval
buildInfo.usingEval = true;
// 获取当前作用域的顶层符号
const currentSymbol = InnerGraph.getTopLevelSymbol(parser.state);
if (currentSymbol) {
// 如果获取到了符号,则记录其引用关系
InnerGraph.addUsage(parser.state, null, currentSymbol);
} else {
// 否则标记当前模块无法进行内联图分析(bailout)
InnerGraph.bailout(parser.state);
}
});
// 在解析完成时记录顶层声明
parser.hooks.finish.tap(PLUGIN_NAME, () => {
const buildInfo =
/** @type {BuildInfo} */
(parser.state.module.buildInfo);
let topLevelDeclarations = buildInfo.topLevelDeclarations;
if (topLevelDeclarations === undefined) {
// 初始化 topLevelDeclarations 为 Set 集合
topLevelDeclarations = buildInfo.topLevelDeclarations = new Set();
}
// 遍历当前作用域中定义的变量名
for (const name of parser.scope.definitions.asSet()) {
const freeInfo = parser.getFreeInfoFromVariable(name);
// 如果变量没有被视为自由变量,认为它是顶层声明
if (freeInfo === undefined) {
topLevelDeclarations.add(name);
}
}
});
};
// 为三种 JS 模块类型分别注册 parser 钩子
normalModuleFactory.hooks.parser
.for(JAVASCRIPT_MODULE_TYPE_AUTO)
.tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser
.for(JAVASCRIPT_MODULE_TYPE_DYNAMIC)
.tap(PLUGIN_NAME, handler);
normalModuleFactory.hooks.parser
.for(JAVASCRIPT_MODULE_TYPE_ESM)
.tap(PLUGIN_NAME, handler);
}
);
}
}
module.exports = JavascriptMetaInfoPlugin;
// 导出插件类