-
构造函数
constructor
- 初始化
compilation
、outputOptions
、requestShortener
- 设置
globalObject
(全局对象名,如window
、globalThis
) - 构造 content hash 的占位符字符串(如
XXXXXXXX
)
- 初始化
-
环境能力判断方法
isIIFE()
:是否使用 IIFE 包裹输出代码isModule()
:是否输出为 ESM 模块格式isNeutralPlatform()
:是否为非浏览器、非 Node 的中立平台supportsConst()
:是否支持const
supportsArrowFunction()
:是否支持箭头函数supportsAsyncFunction()
:是否支持 async 函数supportsOptionalChaining()
:是否支持可选链?.
supportsForOf()
:是否支持for...of
supportsDestructuring()
:是否支持解构赋值supportsBigIntLiteral()
:是否支持BigInt
supportsDynamicImport()
:是否支持import()
supportsEcmaScriptModuleSyntax()
:是否支持原生模块语法supportTemplateLiteral()
:是否支持模板字符串supportNodePrefixForCoreModules()
:是否支持node:
前缀
-
函数生成工具方法
returningFunction(returnValue, args)
:生成一个返回指定值的函数basicFunction(args, body)
:生成一个普通函数或箭头函数expressionFunction(expression, args)
:生成包含表达式的函数体代码emptyFunction()
:生成空函数代码iife(args, body)
:生成 IIFE(立即执行函数表达式)
-
代码片段生成方法
concatenation(...args)
:根据目标环境选择模板字符串或 ES5 拼接_es5Concatenation(args)
:使用+
拼接字符串/表达式(兼容旧环境)destructureArray(items, value)
:生成数组解构赋值或兼容代码destructureObject(items, value)
:生成对象解构赋值或兼容代码forEach(variable, array, body)
:生成for...of
或Array.prototype.forEach
循环
js
/**
* 构造函数
* @param {Compilation} compilation 当前的编译对象
* @param {OutputOptions} outputOptions 输出配置项
* @param {RequestShortener} requestShortener 请求路径简化工具
*/
constructor(compilation, outputOptions, requestShortener) {
this.compilation = compilation;
this.outputOptions = /** @type {OutputOptions} */ (outputOptions || {});
this.requestShortener = requestShortener;
// 获取运行时的全局对象(如 window、globalThis 等)
this.globalObject = /** @type {string} */ (getGlobalObject(outputOptions.globalObject));
// 构建 contentHash 占位符,例如 "XXXXXXXX"
this.contentHashReplacement = "X".repeat(
/** @type {NonNullable<OutputOptions["hashDigestLength"]>} */
(outputOptions.hashDigestLength)
);
}
// 判断是否使用 IIFE(立即执行函数表达式)包裹输出代码
isIIFE() {
return this.outputOptions.iife;
}
// 判断是否生成 ESM(原生模块)语法
isModule() {
return this.outputOptions.module;
}
// 判断是否是中立平台(如无 document 对象,非 node 环境)
isNeutralPlatform() {
return (
!this.outputOptions.environment.document &&
!this.compilation.compiler.platform.node
);
}
// 以下为环境能力判断函数,根据配置判断是否支持某些 JS 特性
supportsConst() {
return this.outputOptions.environment.const;
}
supportsArrowFunction() {
return this.outputOptions.environment.arrowFunction;
}
supportsAsyncFunction() {
return this.outputOptions.environment.asyncFunction;
}
supportsOptionalChaining() {
return this.outputOptions.environment.optionalChaining;
}
supportsForOf() {
return this.outputOptions.environment.forOf;
}
supportsDestructuring() {
return this.outputOptions.environment.destructuring;
}
supportsBigIntLiteral() {
return this.outputOptions.environment.bigIntLiteral;
}
supportsDynamicImport() {
return this.outputOptions.environment.dynamicImport;
}
supportsEcmaScriptModuleSyntax() {
return this.outputOptions.environment.module;
}
supportTemplateLiteral() {
return this.outputOptions.environment.templateLiteral;
}
supportNodePrefixForCoreModules() {
return this.outputOptions.environment.nodePrefixForCoreModules;
}
/**
* 返回一个返回指定值的函数字符串表示
* @param {string} returnValue 返回值
* @param {string} args 参数名
* @returns {string} 函数字符串
*/
returningFunction(returnValue, args = "") {
return this.supportsArrowFunction()
? `(${args}) => (${returnValue})`
: `function(${args}) { return ${returnValue}; }`;
}
/**
* 返回一个基本函数代码字符串
* @param {string} args 参数名
* @param {string|string[]} body 函数体内容
* @returns {string} 函数字符串
*/
basicFunction(args, body) {
return this.supportsArrowFunction()
? `(${args}) => {\n${Template.indent(body)}\n}`
: `function(${args}) {\n${Template.indent(body)}\n}`;
}
/**
* 拼接表达式或字符串
* @param {Array<string|{expr: string}>} args 拼接的内容
* @returns {string} 拼接后的表达式字符串
*/
concatenation(...args) {
const len = args.length;
if (len === 2) return this._es5Concatenation(args);
if (len === 0) return '""';
if (len === 1) {
return typeof args[0] === "string"
? JSON.stringify(args[0])
: `"" + ${args[0].expr}`;
}
if (!this.supportTemplateLiteral()) return this._es5Concatenation(args);
// 模板字符串与字符串拼接的开销分析(用于做生成方式的选择)
let templateCost = 0;
let concatenationCost = 0;
let lastWasExpr = false;
for (const arg of args) {
const isExpr = typeof arg !== "string";
if (isExpr) {
templateCost += 3;
concatenationCost += lastWasExpr ? 1 : 4;
}
lastWasExpr = isExpr;
}
if (lastWasExpr) concatenationCost -= 3;
if (typeof args[0] !== "string" && typeof args[1] === "string")
concatenationCost -= 3;
if (concatenationCost <= templateCost) return this._es5Concatenation(args);
return `\`${args.map(arg => (typeof arg === "string" ? arg : `\${${arg.expr}}`)).join("")}\``;
}
/**
* 使用旧的字符串拼接方式
* @param {Array<string|{expr: string}>} args 拼接项(长度 >= 2)
* @returns {string} 拼接表达式
* @private
*/
_es5Concatenation(args) {
const str = args.map(arg => (typeof arg === "string" ? JSON.stringify(arg) : arg.expr)).join(" + ");
// 若前两个都是表达式,则需强制转字符串防止数值相加
return typeof args[0] !== "string" && typeof args[1] !== "string"
? `"" + ${str}`
: str;
}
/**
* 返回一个包含表达式的函数
* @param {string} expression 表达式
* @param {string} args 参数
* @returns {string} 函数字符串
*/
expressionFunction(expression, args = "") {
return this.supportsArrowFunction()
? `(${args}) => (${expression})`
: `function(${args}) { ${expression}; }`;
}
/**
* 返回一个空函数
* @returns {string} 空函数代码
*/
emptyFunction() {
return this.supportsArrowFunction() ? "x => {}" : "function() {}";
}
/**
* 解构数组形式赋值
* @param {string[]} items 变量名数组
* @param {string} value 被解构的数组表达式
* @returns {string} 解构代码
*/
destructureArray(items, value) {
return this.supportsDestructuring()
? `var [${items.join(", ")}] = ${value};`
: Template.asString(items.map((item, i) => `var ${item} = ${value}[${i}];`));
}
/**
* 解构对象形式赋值
* @param {string[]} items 变量名数组
* @param {string} value 被解构的对象表达式
* @returns {string} 解构代码
*/
destructureObject(items, value) {
return this.supportsDestructuring()
? `var {${items.join(", ")}} = ${value};`
: Template.asString(items.map(item => `var ${item} = ${value}${propertyAccess([item])};`));
}
/**
* 创建 IIFE(立即执行函数表达式)代码
* @param {string} args 参数
* @param {string} body 函数体
* @returns {string} IIFE 代码
*/
iife(args, body) {
return `(${this.basicFunction(args, body)})()`;
}
/**
* 构建 forEach 代码,支持 for-of 或 Array.prototype.forEach
* @param {string} variable 遍历的变量名
* @param {string} array 被遍历的数组
* @param {string|string[]} body 循环体代码
* @returns {string} 生成的 forEach 代码
*/
forEach(variable, array, body) {
return this.supportsForOf()
? `for(const ${variable} of ${array}) {\n${Template.indent(body)}\n}`
: `${array}.forEach(function(${variable}) {\n${Template.indent(body)}\n});`;
}