Allowlist forbuild/flow/src/parser/test/flow
【免费下载链接】babel🐠 Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel
[!NOTE]> This file is automatically generated by
utils/parser-test-runner. Only single-line comments after the list item will be preserved.
两个要点值得特别注意: 1. **自动生成**:文件头部明确声明它由 `utils/parser-test-runner` 自动生成,`--update-allowlist` 会整体重写它(机制细节见本文第五节); 2. **注释保留规则**:列表项之后只有**单行注释**会被保留,这是维护者给条目补充差异原因说明的唯一合法渠道(在 TypeScript 的 allowlist 中可以看到大量 `<!-- ... -->` 形式的注释,例如 [scripts/parser-tests/typescript/allowlist.md](https://link.gitcode.com/i/1515cc31b5b8db5e75793e2ce706c7c3) 中对 `duplicateIdentifierRelatedSpans1.ts` 标注的 "TS checks duplicate identifiers across related files, but babel-parser handles them separately")。 ### 两类差异条目 文件主体是两个编号小节,分别对应两类方向相反的"解析行为差异": ```markdown ## 15 invalid programs did not produce a parsing error ## 254 valid programs produced a parsing errorinvalid programs did not produce a parsing error(无效程序未报错):Flow 认为这些程序是非法的(会报语法错误),但 Babel 解析器却成功解析了。这类差异在测试术语中称为false positive(误放行),即"本应拒绝却没有拒绝",说明 Babel 的语法检查比 Flow 更宽松。valid programs produced a parsing error(有效程序报错):Flow 认为这些程序是合法的,但 Babel 解析器抛出了错误。这类差异称为false negative(误报错),即"本应通过却拒绝",说明 Babel 对某些 Flow 合法语法支持不到位——这是兼容性缺口的主要来源。
当前 Flow allowlist 中 15 条误放行、254 条误报错,合计 269 个"已知差异点"。
条目路径的解析
每个条目都是 Markdown 链接格式:- 显示名。例如:
- JSX_invalid/migrated_0000.js这里的相对路径是相对于 allowlist 文件自身(即scripts/parser-tests/flow/)计算的,向上三级进入build/flow/src/parser/test/flow/,这正是 scripts/parser-tests/flow/index.js 第 99 行配置的testDir。需要说明的是,build/flow/是存放 Flow 官方 parser 测试快照的目录,由构建/拉取流程生成,在本镜像仓库中并未直接提交;运行器通过getTestIdFromAllowlistLine(正则^- \[(.+?)\])从每一行提取[显示名]部分作为用例 ID,用于和测试结果匹配。
三、15 条"无效程序未报错"(误放行)条目详解
这一小节体量小但信息密度高,全部 15 条列举如下(完整内容见 scripts/parser-tests/flow/allowlist.md):
| 分类 | 用例 |
|---|---|
| JSX 非法用例 | JSX_invalid/migrated_0000.js |
| 箭头函数非法用例 | arrow_function_invalid/migrated_0002.js |
| 类成员 | class_method_kinds/polymorphic_getter.js、class_properties/migrated_0026.js |
| 导出语句 | export_statements/invalid_export_enum_disabled.js |
| 循环 | for_await_loops/migrated_0000.js |
| TS 语法别名(6 条,占比最高) | ts_syntax/invalid_alias_keyof.js、invalid_alias_never.js、invalid_alias_readonly.js、invalid_alias_undefined.js、invalid_alias_unknown.js、invalid_readonly_type.js |
| Flow 类型保留字 | types/aliases/invalid_keyword_symbol.js、types/reserved/function.js |
| typeof 类型实参 | types/typeof/with-targs-bad-newline.js |
从这组数据可以看出两个值得关注的事实:
ts_syntax类别独占 6 条:Flow 自身为了兼容 TypeScript 而接受的若干别名写法(如type T = keyof、never、readonly、undefined、unknown等作为类型别名的使用),Babel 解析器同样放行了。这侧面说明 Babel 对这些 TS 风格别名的解析策略与 Flow 在"宽松度"上保持一致。- 这些是"过于宽松"的信号:如果某天 Babel 决定严格对齐 Flow 的错误行为,就需要从这一节移除对应条目,并在解析器中补上相应校验——移除后若仍不报错,测试将直接失败。
四、254 条"有效程序报错"(误报错)条目分类梳理
这是 allowlist 的主体,也真实反映了 Babel 解析器与 Flow 语法之间的兼容性差距分布。按条目目录前缀归类统计如下(已逐一核对原文条目,合计恰好 254 条):
| 分类 | 条目数 | 涉及语法特性说明 |
|---|---|---|
types/*(declare 语句、命名空间等) | 52 | declare_module、declare_namespace、declare_statements、declare_export、export_declare、mapped_types、tuples(inexact/labeled/optional/spread/variance)、render_types、type_params的 const 参数、writeonly_variance等 Flow 特有的声明语法 |
ts_syntax/* | 33 | satisfies、readonly_type、template_literal_type、unique_symbol、export_as_namespace、parameter_properties、override、mapped_type_key_remapping、optional_indexer等 Flow 对 TS 语法的移植 |
enums/* | 27 | Flow 枚举:const/declare 枚举、布尔/数字/字符串成员初始化规则、大小写、重复成员名、bigint 等 |
match/* | 23 | Flow pattern matching 提案语法:表达式/语句形态、守卫(guards)、模式(pattern-array/core/instance/member/object/or-as)等 |
records/* | 22 | Flow records 提案:声明与表达式形态、字面量键、方法、static 属性、spread、typeargs 等 |
comment_interning/* | 14 | 注释内联(comment interning)机制:类型别名、接口、装饰器、导入、对象类型等节点上的注释归属 |
type_guards/* | 14 | asserts is、implies、is/as参数、poly 守卫等 Flow 类型守卫语法 |
components/* | 12 | Flow 组件语法:component声明、类型参数、as重命名、rest 参数、declare 形态等 |
ambient_declarations/* | 10 | 命名空间/模块内的声明(function、getter/setter、variable、namespace) |
abstract_class/* | 9 | 抽象类:抽象方法/属性、declare、protected/public、export 形态 |
class_declare_method/* | 5 | declare class中的方法:泛型、重载、static |
import_equals_declaration/* | 5 | import ... = require(...)、import type ... = require(...)及限定名形态 |
types/tuples/*、types/mapped_types/*等已在types/*统计 | — | — |
| 其余(ES6/modules、JSX、async_await、decorators、export_assignment、hook_syntax、computed_keys、call_properties_invalid、arrow_function、assert_operator、conditional_types、opaque_aliases 等) | 28 | 分散的迁移用例(migrated_00xx.js后缀表明来自 Flow 测试的自动迁移)与少量独立特性用例 |
从条目看兼容性缺口的两类成因
结合 scripts/parser-tests/flow/index.js 的选项转换逻辑,这 254 条可以归因到两类情况:
- Flow 独有/提案级语法,Babel 明确未实现:
components、assert_operator、pattern_matching、records、intern_comments这五个测试选项在 flow/index.js 中直接被continue跳过(注释写着 "we don't support this syntax")。所以match/*、records/*、components/*、assert_operator/*下的条目是结构性缺口——Babel 现阶段根本不解析这些语法,自然全部误报错。 - 已实现但行为有细微差异:
enums/*(Babel 通过flowOptions.enums = true开启)、comment_interning/*、abstract_class/*、ts_syntax/*等大多属于这一类,每条都对应一个具体的解析行为差异,是后续逐步修复、逐条从 allowlist 中移除的对象。
五、自动生成机制:parser-test-runner 源码剖析
allowlist 不是手写的,其全部生命周期都由 scripts/parser-tests/utils/parser-test-runner.js 中的TestRunner类驱动。整体流程(run(),第 46-61 行)为:读取 allowlist → 逐个跑测试 → 汇总解释 → 输出报告(或重写 allowlist)。
判定:expectedError 与 actualError
每个测试用例在getTests()中携带expectedError(Flow 期望它报错与否,来自 Flow 测试自带的.tree.json中errors字段),随后runTest()(第 69-81 行)用 Babel 解析器实际跑一遍,得到actualError:
test.result = test.expectedError !== test.actualError ? "fail" : "pass";四分类解释:interpret
interpret()(第 200-255 行)将每个用例归入 8 个桶之一,本质是两个维度的交叉:allowed(在 allowlist 中)/disallowed(不在) ×success/failure/falsePositive/falseNegative:
falsePositive:Flow 期望报错、Babel 未报错;falseNegative:Flow 期望通过、Babel 报错;success:双方都通过;failure:双方都报错。
测试通过的条件是:所有不在 allowlist 中的用例行为一致,且 allowlist 中不存在指向已消失用例的悬空条目(unrecognized)。任何新的差异都会落到disallowed.*桶中,导致退出码为 1(output()第 335 行process.exitCode = summary.passed ? 0 : 1)。
重写:updateAllowlist
传入--update-allowlist时,updateAllowlist()(第 115-192 行)会:
- 从现有文件解析所有条目,剔除已不再差异的用例(
disallowed.success、disallowed.failure)与失效条目(unrecognized); - 把新出现的
disallowed.falsePositive追加到"无效程序未报错"小节、disallowed.falseNegative追加到"有效程序报错"小节; - 两个小节各自按 ID 排序,并重新计算小节标题中的计数,最后整体写回文件。
这就是为什么文件头部的计数(15 / 254)永远与正文条数一致——它完全由生成逻辑维护。同时,只有保留在行内的单行注释会随条目存活,这印证了文件头部的注释保留声明。
六、Flow 测试驱动细节:选项映射与 module/script 重试
scripts/parser-tests/flow/index.js 承担了"把 Flow 测试格式翻译成 Babel 解析器配置"的适配工作,是理解 allowlist 条目的关键上下文。
测试用例的加载格式
loadTests()(第 77-96 行)约定每个.js用例可配有两个伴生文件:.tree.json(期望的 AST 与错误信息,缺失视为{})和.options.json(Flow 测试选项)。expectedError由 tree 中errors数组是否有内容决定:
const shouldSuccess = test.tree && (!test.tree.errors || !test.tree.errors.length);Flow 选项 → Babel 选项的映射表
convertFlowParserTestOptionsToBabelParserOptions()(第 11-56 行)实现了映射,默认配置固定为:
{ plugins: [["flow", { all: true }], "flowComments", "jsx"], }各 Flow 测试选项的处理策略如下表(这是"Babel 到底支不支持 Flow 的哪些特性"的第一手证据):
| Flow 测试选项 | Babel 侧处理 | 含义 |
|---|---|---|
components | continue(忽略) | 不支持该语法,相关用例全部落入误报错 |
assert_operator | continue | 不支持assert操作符 |
pattern_matching | continue | 不支持 match 模式匹配 |
records | continue | 不支持 records |
intern_comments | continue | 不支持注释内联 |
enums | flowOptions.enums = true | 开启 Flow 枚举解析 |
esproposal_decorators | 追加"decorators-legacy"插件 | 用 legacy 装饰器模式对齐 |
types(为 false 时) | options.plugins = [] | 关闭全部类型解析插件 |
use_strict | options.strictMode = enabled | 控制严格模式 |
| 其他未知选项 | 抛出Unknown flow parser test option | 防止静默遗漏 |
这张表解释了为什么 allowlist 中会成片出现match/*、records/*、components/*条目——它们不是"待修复的 bug",而是"明确暂不支持的语法",被整体登记在案。
module 失败后在 script 模式重试
parse()(第 118-138 行)还有一个务实细节:当用例以sourceType: "module"解析失败、而该用例本不应报错时,会改用sourceType: "script"再试一次。这降低了因模块模式误判(例如把某些代码当模块解析时产生合法但多余的报错)而产生的假差异,避免不必要的 allowlist 条目。
七、如何运行 Flow 兼容性测试与更新 allowlist
运行前提
build/flow/src/parser/test/flow目录下需存在 Flow 官方测试快照(含.js、.tree.json、.options.json);- 运行器在第 6 行直接导入
packages/babel-parser/lib/index.js,即依赖 babel-parser 的编译产物,因此通常需要先完成 babel-parser 的构建。
运行与更新命令
# 常规运行:跑完全部用例并输出报告 yarn node scripts/parser-tests/flow/index.js # 更新 allowlist:把当前差异同步写回 allowlist.md yarn node scripts/parser-tests/flow/index.js --update-allowlist【免费下载链接】babel🐠 Babel is a compiler for writing next generation JavaScript.项目地址: https://gitcode.com/gh_mirrors/ba/babel
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考