Rome JSON Formatter 的 Prettier 兼容性测试套件:快照提取、运行机制与报告生成
2026/9/20 2:21:43 网站建设 项目流程
  • 开发工具
  • CLI
  • Lint
  • 格式化
  • 静态分析
  • 代码质量
  • 构建工具

【免费下载链接】tools

Unified developer tools for JavaScript, TypeScript, and the web

项目地址:https://gitcode.com/gh_mirrors/to/tools
点击查看免费下载

Rome(本项目为 Rome 工具的完整镜像仓库,面向 JavaScript、TypeScript 与 Web 的统一开发者工具链)的 JSON 格式化器并非闭门造车,而是建立在一套与 Prettier 官方快照逐字节对齐的兼容性测试体系之上。本文以 crates/rome_json_formatter/tests/specs/prettier/README.md 为骨架,结合仓库内测试入口、快照提取脚本与差异报告器的源码实现,完整讲解这套测试套件的目录组织、运行命令、REPORT_PRETTIER报告机制以及从 Prettier 仓库同步更新快照的完整流程。读完本文,你将掌握如何运行该测试、如何解读.snap.prettier-snap文件的差异、如何生成兼容性度量报告,并理解占位符、范围格式化(range formatting)等底层细节的实现原理。

这套测试套件要解决什么问题

Rome 的格式化器在设计上以 Prettier 的输出作为兼容性基准:同一段代码,rome_json_formatter格式化后的结果应当与 Prettier 的官方快照一致。为此,仓库将 Prettier 官方测试仓库中的 JSON 相关用例(含输入文件与期望输出)抽取出来,转存为本仓库的 Rust 测试数据,并注册为常规的cargo test用例。

这套机制的价值在于:

  • 持续回归:每次修改格式化器实现,都能立即发现与 Prettier 输出的偏差;
  • 差异可视化:不一致时生成 unified diff,指出 Rome 与 Prettier 的具体分歧点;
  • 量化兼容度:通过REPORT_PRETTIER=1环境变量输出整份report.md,统计文件级与行级相似度,用数字追踪兼容性进展。

测试数据的目录组织

Prettier 相关测试数据全部位于crates/rome_json_formatter/tests/specs/prettier/下,按语言特性分子目录。当前仓库中 JSON 侧包含四大类:

目录覆盖场景代表性文件
json/JSON 语法全覆盖pass1.json(JSON Test Pattern)、number.jsonsingle-quote.jsonkey-value.json
json5-as-json-with-trailing-commas/带尾逗号的 JSON5 风格输入nested-quotes.json
range/范围(range)格式化,仅重排指定区间inside-array.json 及大量 issue 用例(issue-4009.jsonissue-7116.jsonissue2297.json
with-comment/块注释与行注释line-comment.json

每个用例目录内同时存在三种文件:

  • .json:输入文件(可能含占位符,见下文);
  • .prettier-snap:Prettier 官方期望输出(由提取脚本生成,测试时作为比对基准);
  • .snap:Rome 自身测试快照,仅在 Rome 输出与 Prettier 不一致时才生成。

此外还有两份支撑文件:prepare_tests.js(JSON 侧提取脚本入口)与 README.md(本文所讲解的说明文档)。

运行 Prettier 兼容性测试

原文档给出的命令是:

cargo test -p rome_js_formatter --test prettier_tests

需要说明的是:该 README 在rome_js_formatterrome_json_formatter两个 crate 中各有一份(见 crates/rome_js_formatter/tests/specs/prettier/README.md),针对 JSON 侧的正确包名应为:

cargo test -p rome_json_formatter --test prettier_tests

测试入口位于 crates/rome_json_formatter/tests/prettier_tests.rs,核心只有两段逻辑:

tests_macros::gen_tests! {"tests/specs/prettier/{json}/**/*.{json}", crate::test_snapshot, ""} fn test_snapshot(input: &'static str, _: &str, _: &str, _: &str) { countme::enable(true); let root_path = Path::new(concat!( env!("CARGO_MANIFEST_DIR"), "/tests/specs/prettier/" )); let test_file = PrettierTestFile::new(input, root_path); let options = JsonFormatOptions::default().with_indent_style(IndentStyle::Space(2)); let language = language::JsonTestFormatLanguage::default(); let snapshot = PrettierSnapshot::new(test_file, language, options); snapshot.test() }

要点解读:

  • tests_macros::gen_tests!是仓库自定义的声明式测试生成宏(实现见 crates/tests_macros/src/lib.rs),按 glob 模式tests/specs/prettier/{json}/**/*.{json}把每个匹配文件展开为一个独立测试用例,无需手写测试函数;
  • 测试固定使用2 空格缩进IndentStyle::Space(2)),与 Prettier 默认配置对齐;
  • crates/rome_json_formatter/tests/language.rs 中的JsonTestFormatLanguage通过parse_json(text, JsonParserOptions::default().with_allow_comments())解析输入——注意这里显式允许注释,这正是with-comment/目录用例能够通过解析的前提。

测试执行链路:从输入文件到差异判定

单条用例的完整执行由 crates/rome_formatter_test/src/test_prettier_snapshot.rs 中的PrettierTestFilePrettierSnapshot两个结构体承担,流程如下:

  1. 读取并预处理输入PrettierTestFile::new读取输入文件,调用strip_prettier_placeholders剥离 Prettier 风格的占位符(光标<|><<<PRETTIER_RANGE_START>>><<<PRETTIER_RANGE_END>>>,见 utils.rs 的StripPlaceholders),并记录它们在原文中的偏移量;随后把prettier-ignore替换为rome-ignore format: prettier ignore,使 Rome 的抑制注释语法与 Prettier 的prettier-ignore语义对齐;
  2. 解析:按parse_input解析出语法树AnyParse
  3. 格式化formatted()根据是否有范围占位符分两条路径——有范围时调用language.format_range只格式化指定TextRange,再替换回原文件;无范围时调用format_node全量格式化;
  4. 幂等性校验:无错误且非范围格式化时,CheckReformat会对输出结果再次解析并格式化,验证"格式化结果再格式化后不变"(实现见 crates/rome_formatter_test/src/check_reformat.rs),这是格式化器稳定性的重要保障;
  5. 与 Prettier 快照比对get_prettier_diff读取同目录下对应的.prettier-snap文件,若 Rome 输出与之一致则判定PrettierDiff::Same,并清理可能残留的.snap/.snap.new文件;不一致则用similar::TextDiff生成 unified diff,头部标注PrettierRome
  6. 写快照:仅当存在差异时,才通过SnapshotBuilder生成.snap文件。

解读一份.snap文件

以 nested-quotes.json.snap 为例,快照依次包含# Input# Prettier differences(unified diff)、# Output(Rome 实际输出)与解析错误信息四段。diff 段直观展示了 Rome 与 Prettier 的分歧:例如 Prettier 会把首行对象展开为多行属性,而当时版本的 Rome 保持单行紧凑;'singleQuote': 'exa"mple'这类 JSON5 单引号与未归一化的键,Rome 也未做重排。这类快照正是追踪兼容性差距、指导后续实现改进的第一手资料。

生成兼容性差异报告:REPORT_PRETTIER

原文档说明:设置环境变量REPORT_PRETTIER=1后运行测试,会输出一份report.md,其中包含 Rome 与 Prettier 输出的穷尽式差异(exhaustive difference)。其实现位于 crates/rome_formatter_test/src/diff_report.rs:

  • DiffReport是一个进程级单例,内部用Mutex<Vec<DiffReportItem>>收集每个用例的 Rome 输出与 Prettier 输出;首次访问时通过libc::atexit注册进程退出回调,确保测试全部结束后统一打印报告;
  • 仅当REPORT_PRETTIER=1时收集数据,is_ignored会过滤掉一批 Prettier 中暂不支持的实验性语法文件名(如partial-applicationpipelinerecordv8intrinsic.js等);
  • 报告输出格式由环境变量控制:
    • REPORT_TYPE=markdown(默认)→ 输出report.md
    • REPORT_TYPE=json→ 输出report.json(结构化数据,便于程序消费);
    • REPORT_FILENAME=<path>→ 自定义报告文件名。

报告内容包含两个核心兼容度指标:

  • 文件级平均相似度file_based_average_prettier_similarity):compatibility_file = 匹配行数 / max(rome 行数, prettier 行数),再对所有文件取平均;
  • 行级平均相似度line_based_average_prettier_similarity):所有文件中匹配行总数 / 双方行数较大者之和。

每个文件还会附带各自的Prettier Similarity百分比与逐行 diff(+/-标注),Markdown 报告按文件名排序并汇总# Overall Metrics章节。这套指标让兼容性工作从"感觉差不多了"变成可量化、可追踪的工程度量。

更新 Prettier 快照:完整操作流程

当 Prettier 官方修复了某个格式问题、新增了用例,或 Rome 需要对齐新的行为时,需要把 Prettier 仓库中的最新快照重新提取进本仓库。原文档给出了完整步骤,结合源码可进一步明确每一步的实质:

  1. 克隆 Prettier 仓库到本地:提取脚本会从该仓库的tests/format目录下遍历用例(见下文PRETTIER_ROOT定义);
  2. 清空本仓库的crates/rome_json_formatter/tests/specs/prettier目录:确保所有过时用例被移除,避免残留文件干扰;
  3. 进入crates/rome_formatter_test/src/prettier目录:该目录是跨 formatter 共享的提取工具所在地,包含 package.json、prepare_tests.jspnpm-lock.yaml
  4. 安装依赖pnpm install:根据 package.json,依赖为prettier@3.0.0,并声明pnpm@^8.0.0引擎约束——提取过程本身需要用 Node 版 Prettier 重新格式化期望输出;
  5. 回到crates/rome_json_formatter/tests/specs/prettier目录,运行:
    node crates/rome_json_formatter/tests/specs/prettier/prepare_tests.js <prettier root directory>

    其中<prettier root directory>是步骤 1 克隆的 Prettier 仓库根目录。

提取脚本的工作原理

入口脚本 crates/rome_json_formatter/tests/specs/prettier/prepare_tests.js 只有数行,真正的工作在共享实现 crates/rome_formatter_test/src/prettier/prepare_tests.js 中:

  • PRETTIER_ROOT = path.resolve(process.argv[2], 'tests/format'),即从 Prettier 仓库的tests/format出发;
  • 递归遍历tests/format/json(JSON 侧通过extractPrettierTests("json", { parser: "json" })指定),跳过以jsfmt.spec开头的 spec 文件
  • 对每个输入文件,定位同级__snapshots__/jsfmt.spec.js.snap快照,按 key`${file} format 1`取出期望输出,再用正则截取=====output==========结尾之间的内容;
  • 关键细节:由于 Rome 与 Prettier 的默认格式化选项不同,提取脚本会用 Node 版 Prettier 以统一配置重新格式化快照内容,再写入.prettier-snap文件。统一配置在脚本顶部定义:
    const defaultConfig = { trailingComma: 'all', tabWidth: 2, printWidth: 80, singleQuote: false, jsxSingleQuote: false, useTabs: false, embeddedLanguageFormatting: 'off' };

    这保证printWidth: 80、双引号、2 空格缩进、全量尾逗号等基准与 Rome 测试侧使用的JsonFormatOptions::default().with_indent_style(IndentStyle::Space(2))对齐;

  • 若快照中找不到对应 key(Prettier 侧无快照),则退而求其次:直接用 Prettier 格式化输入文件本身,把结果作为期望输出写入.prettier-snap
  • 输入文件会原样复制到本仓库对应相对路径(相对tests/format的路径映射为相对tests/specs/prettier的路径),保持目录结构一一对应。

实战解读:从用例到兼容性结论

以 JSON Test Pattern 用例 pass1.json 为例:输入是一段故意写得杂乱无章的 JSON(元素间散布换行与空格、数字写成98.6/1e00、键含转义序列等),对应的 pass1.json.prettier-snap 展示了 Prettier 的规范化结果:数组与对象元素每行一个、数字规整为-9876.543211.23456789e34,空数组/空对象紧凑为[]/{},字符串中的\/归一为/。由于该目录下没有pass1.json.snap,说明当前 Rome 输出与 Prettier 完全一致——这是判断单个用例通过与否的最快捷方式:存在.snap即有差异,不存在即完全对齐。

类似地,range/inside-array.json 通过&lt;&lt;&lt;PRETTIER_RANGE_START&gt;&gt;&gt;&lt;&lt;&lt;PRETTIER_RANGE_END&gt;&gt;&gt;占位符圈定只格式化[2, 3, 4, 5, 6, 7]这一区间,其 期望输出 表明 Prettier 会对整个数组(乃至外层对象)做连贯重排。这类用例专门验证 Romeformat_range的区间处理与 Prettier 的一致性,对应 utils.rs 中占位符剥离与 test_prettier_snapshot.rs 中反向区间的防御性处理(end < start时直接跳过,因为 Rust 侧无法构造反向TextRange)。

注意事项与边界

  • 包名差异:原 README 中命令写的是rome_js_formatter,但该目录属于 JSON 侧,运行时应使用cargo test -p rome_json_formatter --test prettier_tests
  • 忽略列表REPORT_PRETTIER报告会过滤 Prettier 中 Rome 尚未实现/有意跳过的实验性语法(见diff_report.rsis_ignored模式列表),统计时这些文件不参与度量;
  • Node 依赖:更新快照依赖 Node 侧 Prettier 3.0.0 与 pnpm 8,仅在需要同步上游时执行;日常cargo test不依赖 Node 环境;
  • 仓库只读:本仓库为镜像,上述更新流程用于说明机制,实际开发应在可写副本中进行。

通过这套机制,Rome 的 JSON 格式化器得以在数千行真实用例的监督下持续向 Prettier 对齐,任何一次重构都能被立刻暴露、量化并回归验证。

  • 开发工具
  • CLI
  • Lint
  • 格式化
  • 静态分析
  • 代码质量
  • 构建工具

【免费下载链接】tools

Unified developer tools for JavaScript, TypeScript, and the web

项目地址:https://gitcode.com/gh_mirrors/to/tools
点击查看免费下载

相关推荐

上一篇:如何用Hunter快速搭建C/C++项目?5分钟入门教程与实战案例
下一篇:企业级3D人体动作生成实战指南:HumanML3D数据集深度解析与架构设计

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询