#18198·babel

`sourceType: "unambiguous"` 不一致地处理顶层 await 表单

作者: magic-akari创建于 2026年8月23日更新于 2026年9月11日

问题描述

  • 您是否想要修复此问题?
  • 如何使用 Babel?
  • 程序化 API(babel.transformbabel.parse
  • 输入代码
javascript
import { parse } from "@babel/parser";

const cases = {
  "await value": "await value;",
  "await + 0": "await + 0;",
  "for await": "for await (const value of values) {}",
  "await using": "await using resource = acquire();",
};

for (const [name, code] of Object.entries(cases)) {
  const ast = parse(code, {
    sourceType: "unambiguous",
    plugins: ["explicitResourceManagement"],
  });

  console.log(`${name}: ${ast.program.sourceType}`);
}
  • 配置文件名:babel.config.json
  • 配置
json
{
  "sourceType": "unambiguous",
  "plugins": ["explicitResourceManagement"]
}
  • 当前行为和预期行为
  • 当前结果如下:
输入 program.sourceType
await value; module
await + 0; script
for await (const value of values) {} script
await using resource = acquire(); script

这里存在两种不同的不一致性。首先,不同形式的顶级 await 会被不同对待,这表明文件是模块的证据:

  • 一个明确的顶级 await 表达式,例如 await value,会产生一个模块。
  • 一个同时具有有效脚本解释的表达式,例如 await + 0,会产生一个脚本和不同的 AST 形状。
  • for await (...)await using ... 会产生一个脚本。
  • 其次,即使 AST 标记为脚本,但在明确指定了 sourceType: "script" 时,Babel 会拒绝此语法:
javascript
parse("for await (const value of values) {}", {
  sourceType: "script",
}); // throws
parse("await using resource = acquire();", {
  sourceType: "script",
  plugins: ["explicitResourceManagement"],
}); // throws

这意味着使用 sourceType: "unambiguous" 进行解析的结果无法通过使用已解析的 program.sourceType 来重现。后续工具如果保留并再次使用已解析的源类型,则可能会在 Babel 最初接受的源上失败。

  • 我希望 sourceType: "unambiguous" 遵循一致的策略。以下两种情况都是可以理解的:

选项 1:不要使用顶级 await 进行源类型检测

仅明确的导入/导出族语法才会导致不含歧义的源类型解析为模块。无论顶级 await 以何种形式出现,例如 await valueawait + 0for await (...)await using ...,都不会被视为模块证据。 这是一种保守的策略:源类型检测仍然基于明确的模块语法,而对仅包含 TLA 的输入的接受和 AST 表示可以单独指定。

选项 2:将每个顶级 await 形式都视为模块证据

如果顶级 await 参与源类型检测,则其所有语法形式都应参与。特别是,顶级 for await (...)