news 2026/9/24 13:29:50

Hermes 引擎的 Prettier 插件 prettier-plugin-hermes-parser 演进解析:Flow 前沿语法格式化与版本兼容全指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Hermes 引擎的 Prettier 插件 prettier-plugin-hermes-parser 演进解析:Flow 前沿语法格式化与版本兼容全指南
  • 语言运行时
  • 编译器
  • 移动开发

【免费下载链接】hermes

A JavaScript engine optimized for running React Native.

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

导读

本文以 Hermes 仓库内 tools/hermes-parser/js/prettier-plugin-hermes-parser/CHANGELOG.md 为主线,系统讲解prettier-plugin-hermes-parser(当前仓库版本 0.37.0)的设计定位、安装配置、底层打印原理,以及它随版本迭代逐步支持 React 组件/ Hook 声明、Flow 新型 variance、keyof、match 模式、Records、opaque type 上下界等前沿语法的完整过程。读完本文,你将掌握该插件的接入方式、版本选择依据,并能从源码与测试层面理解它如何让 Prettier 持续兼容 Hermes 解析器产出的最新 ESTree 结构。

一、插件定位:让 Prettier 认识 Hermes 的 AST

prettier-plugin-hermes-parser是 Hermes 生态中连接「解析」与「格式化」的桥梁。Hermes 的 JavaScript 解析器由 C++ 实现并编译为 WASM,能完整解析 Flow、JSX 以及大量尚未进入 Prettier 官方插件的实验性语法。Prettier 官方自带 Babel 等解析器插件,但无法第一时间覆盖 Hermes 解析器支持的这些「超集」语法节点;该插件的职责就是把 Hermes 解析器产出的 AST(以estree-hermes格式标识)接入 Prettier 的打印管线,从而让这些新语法也能获得一致的格式化输出。

需要特别说明定位:仓库 README.md 明确写道——「Unless you want to be on the bleeding edge, you should use the official@prettier/plugin-hermesinstead」。也就是说,追求稳定环境的普通用户应使用官方发布的@prettier/plugin-hermes,而本插件是 Hermes 仓库内维护的「前沿(bleeding edge)」版本,用于更快跟进 Hermes 解析器新增的语法;在 0.31.1 版本中,插件正是在@prettier/plugin-hermes基础上重建("Rebuild based on@prettier/plugin-hermes. There should be no formatting differences, but it will be less buggy"),由此继承了其打印逻辑,同时以独立版本线继续演进。

从 package.json 的元数据(package.json)可以看到:

  • 包名prettier-plugin-hermes-parser,版本 0.37.0,MIT 许可;
  • 入口通过exports字段暴露./index.mjs(ESM 模块);
  • peerDependencies声明prettier: ^3.0.0——插件仅支持 Prettier v3,这与 0.31.0 版本移除 Prettier v2 支持的决策一致(见 tools/hermes-parser/js/CHANGELOG.md 0.31.0 条目)。

二、安装与配置:将 hermes 设为解析器

2.1 基础配置

插件的使用方式非常直接:安装插件后,在 Prettier 配置中把它加入plugins列表,并对需要走 Hermes 解析器的文件通过overrides指定parser: "hermes"。仓库 README 给出了完整的 .prettierrc 示例:

{ "plugins": ["prettier-plugin-hermes-parser"], "overrides": [ { "files": ["*.js", "*.jsx", "*.flow"], "options": { "parser": "hermes" } } ] }

其中overridesfiles可扩展至你项目中所有使用 Flow/JSX 的文件类型;parser: "hermes"是插件向 Prettier 注册的解析器名。之所以用overrides而非全局parser,是为了让.js/.jsx/.flow之外的文件继续走 Prettier 默认解析器。

2.2 在测试中的实际调用方式

仓库的单元测试展示了编程式调用方式,可视为配置的「代码形态」。以 prettier-plugin-hermes-parser-test.js 为例:

function getOptions() { return { ...prettierConfig, parser: 'hermes', requirePragma: false, plugins: [require.resolve('../index.mjs')], }; } const output = await prettier.format(code, getOptions());

要点包括:parser: 'hermes'与 CLI/配置文件中的写法完全一致;plugins直接引用本地index.mjs的解析路径;requirePragma: false表示不需要@prettier类 pragma 注释也会格式化。测试还额外验证了格式化的稳定性(idempotency)——对输出结果二次格式化必须与首次结果逐字节一致,这是 Prettier 插件质量的关键指标。

三、入口实现:index.mjs 与 avoidAstMutation

理解插件能力边界,最好的切入点是它的入口文件 index.mjs。整个文件非常精简:

import hermesPlugin from './index.generated.mjs'; const HERMES_AST_FORMAT = 'estree-hermes'; const hermesPrinter = hermesPlugin.printers[HERMES_AST_FORMAT]; const printers = { ...hermesPlugin.printers, [HERMES_AST_FORMAT]: { ...hermesPrinter, experimentalFeatures: { ...hermesPrinter.experimentalFeatures, avoidAstMutation: true, }, features: { ...hermesPrinter.features, experimental_avoidAstMutation: true, }, }, }; export const languages = hermesPlugin.languages; export const options = hermesPlugin.options; export const parsers = hermesPlugin.parsers; export {printers};

从源码结构可以提炼出三点实现事实:

  1. 核心实现来自生成文件:真正的解析器与打印器逻辑位于index.generated.mjs(由scripts/build-prettier.sh从 Prettier fork 构建产出,见下文「贡献与构建」一节),入口文件只是在其上做轻量包装。
  2. estree-hermes打印格式:插件注册的 AST 格式标识为estree-hermes,Prettier 根据该标识找到对应的 printer 来序列化 Hermes 解析器输出的节点。
  3. avoidAstMutation(避免 AST 变异):这是本插件相对 Prettier 默认行为的重要差异——它显式开启了实验性特性experimental_avoidAstMutation,即打印过程中不修改传入的 AST。这对hermes-transformflow-api-translator等依赖 AST 多次复用的工具链尤为关键:保证打印不会破坏原始节点结构(相关背景见 tools/hermes-parser/js/CHANGELOG.md 0.15.0 中print缓存键唯一性的修复,以及 0.13.0 中「Printer 始终使用本插件以支持最新 Flow 语法」的决策)。

四、版本演进全景:0.31.1 → 0.37.0 的功能时间线

CHANGELOG 将 0.31.1 之前的历史指向 tools/hermes-parser/js/CHANGELOG.md(hermes-parser 系列的主 changelog),因此本文聚焦 0.31.1 之后独立维护的条目。以下按功能类别梳理:

4.1 对 Prettier 版本的兼容与同步(选型必读)

  • 0.37.0:修复与 Prettier 3.7+ 的兼容性问题(Fix compatibility with Prettier 3.7+)。这是当前最新版本,如果你的工程升级到了 Prettier 3.7 及以上,应使用该版本。
  • 0.33.2:将内置基础 Prettier 版本回退到 3.6.2,但保留其他变更(Reverts base Prettier version back to 3.6.2, but keeps other changes)。这提醒我们:插件内部「基于某个 Prettier 版本 fork」与「对宿主 Prettier 版本的兼容」是两条独立的维度,基础版本的升降不影响插件在宿主环境中的工作。
  • 0.33.1:包含直至 Prettier 3.7.4 的变更(Includes changes up to Prettier 3.7.4)。
  • 0.31.1:基于@prettier/plugin-hermes重建,格式输出不变但更少 bug。

这些条目的实践含义是:插件版本与宿主 Prettier 版本需要匹配。若宿主 Prettier 较新(≥3.7),应优先使用 0.37.0;若被 0.37.0 的兼容修复影响,可结合 0.33.x 的兼容策略评估回退路径。

4.2 新语法格式化支持(核心能力)

这是插件的核心竞争力——让 Prettier 能够打印 Hermes 解析器支持的各类 Flow 实验语法:

  • React 组件与 Hook 声明(0.36.1):支持async componentasync hook声明的格式化。对应解析器侧在 hermes-parser 0.34.0 就加入了async hook/async component语法支持,0.36.1 修复了async component函数体内await不被识别的问题,随后插件跟进打印支持。
  • Flow variance 新语法(0.36.0、0.35.0):支持新的 Flow variance 语法;并将readonly作为等价于+(协变/只读)的 variance 注解加以支持,可作用于对象类型属性、索引器、类属性乃至元组标记元素。hermes-estree 侧在 0.36.0 为 variance 类型新增了writeonlyinout,0.35.0 新增readonly,插件随之同步。
  • keyof运算符(0.34.0):支持 Flow 新的keyof运算符(用于替代$Keys)。
  • match 实例模式与 Flow Records(0.33.1):支持 Flow match 实例模式(match语句/表达式)与 Records(记录字面量{}语法)。
  • opaque type 上下界(0.32.0):支持同时带下界与上界的 opaque type,即superextends语法(对应解析器 0.30.0 的解析能力)。

上述每一项都有对应的专项测试文件佐证,例如 component-declaration-test.js(组件声明)、readonly-variance-test.js(readonly variance)、keyof-test.js(keyof)、match-test.js、record-test.js、opaque-type-test.js,以及 const 类型参数(const-type-param-test.js)与declare component/declare hook(declare-component-test.js、declare-hook-test.js)等。

4.3 Bug 修复(稳定性保障)

  • 0.34.1:修复自动格式化器意外删除空格、导致 Prettier 补出多余空行的问题("a white space was accidentally removed by autoformatter causing prettier to add unnecessary new lines")。这一条说明插件同时维护自己的生成代码风格,生成过程中的空白处理也是质量保障的一部分。

4.4 其他语法细节

  • 0.36.0:支持DeclareVariable声明的数组形式("SupportDeclareVariabledeclarations array"),对应 Flow 中declare var声明数组的打印。

五、格式化效果示例:从测试快照看插件行为

专项测试中的 inline snapshot 直接展示了插件的真实输出,是理解其格式化行为的最佳素材。以下从 component-declaration-test.js 选取典型场景:

组件声明(含导出、泛型约束、可选参数、默认值):

component MyComponent() {} component MyComponent() renders SomeComponent {} export component MyComponent() {} component MyComponent<T>() {} component MyComponent(bar: string) {} component MyComponent(bar?: string) {} component MyComponent(bar: string = '') {}

超长泛型约束与长参数列表自动换行(体现 Prettier 折行策略与 Flow 尾逗号约定):

component MyComponent< T: Fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo, >() {} component MyComponent( bar: string, baz: $ReadOnly<{k: string}>, realllllllllllllllllllyLong: string, ) {}

rest 参数、注释保留(块注释、行尾注释、前置注释均被稳定保留并正确对齐):

component MyComponent(...restProps: $ReadOnly<{k: string}>) {} component MyComponent( /** * Commet block */ bar: string, // Trailing comment // preceding comment 'data-baz' as baz: $ReadOnly<{k: string}>, ) {}

JSX 稳定性(prettier-plugin-hermes-parser-test.js):测试验证了 JSX 变量声明「一次格式化后再次格式化结果完全一致」,并确保嵌套 JSX 中不会在开标签后/闭标签前出现多余空行:

function Foo() { return ( <View style={styles.root}> <View style={styles.content}> <Text>Hello</Text> <Button onClick={handleClick} /> </View> <Separator /> </View> ); }

内嵌标签模板(graphql / css)与 prettier-ignore 注释也在同一测试中验证可正常工作,说明插件保留了 Prettier 的完整嵌入式格式化能力:

const styles = { content: css` column-gap: 8px; display: grid; grid-template-columns: 1fr 3fr; `, };

六、在 Hermes 工具链中的位置:为 transform 与 translator 提供打印能力

本插件并不仅服务于命令行格式化,它还是 Hermes 代码变换工具链的「打印后端」。从 tools/hermes-parser/js/CHANGELOG.md 可以还原这条依赖链:

  • 0.13.0hermes-transformflow-api-translator的 Printer「始终使用prettier-plugin-hermes-parser以确保支持最新 Flow 语法」——即 AST 变换/翻译后的代码统一交给本插件序列化;
  • 0.15.0hermes-transform增加对本插件的peerDependency,并保证print缓存键在存在多实例时唯一;
  • 0.32.0hermes-transform在安装了本插件时,使用它来打印变换后的代码;
  • 0.37.0(最新):hermes-transform改为使用 Prettier 内置的 Flow 与 TypeScript 插件打印,不再条件加载本插件——这是对当前主 changelog 中该工具链策略的一次重要调整,说明本插件的职责正逐步回归「面向使用者的独立格式化插件」角色。

因此,如果你在研究hermes-transform(AST 变换后重写源码)或flow-api-translator(Flow 转 TypeScript 定义),本插件的历史版本行为与之深度耦合,理解这份 CHANGELOG 有助于判断打印行为差异的来源。

七、贡献与构建:基于 Prettier fork 的二次开发

仓库内 CONTRIBUTION.md 描述了插件(本质上是一份「修改版 Prettier v3」)的构建流程,对想深入贡献的开发者很有价值:

  1. hermes-parser/js下运行./scripts/build-prettier.sh执行 yarn 构建脚本;
  2. 对位于hermes-parser/js/prettier-hermes-flow-fork的 Prettier fork 仓库做修改;
  3. 修改完成后运行yarn build-prettier重新构建;
  4. 完成后在 fork 仓库内提交,并可将相关改动向上游提交 PR。

手动流程则是:检出 Prettier 的 flow fork,执行yarn build --package=@prettier/plugin-hermes,将构建产物dist/plugin-hermes及全部 JS 文件拷贝到本目录。这也解释了入口文件为何是index.mjs引用index.generated.mjs——后者即构建生成的、体积较大的完整插件实现。

八、总结与版本选择建议

prettier-plugin-hermes-parser的演进史是 Hermes 解析器语法扩展的缩影:每当 hermes-parser 新增语法解析能力,插件在下一版本即跟进打印支持,同时持续同步 Prettier 上游并修复空白、换行等细节问题。选择建议如下:

  • 稳定场景:优先使用官方@prettier/plugin-hermes(见 README.md);
  • 前沿场景:需要格式化component/hook声明、Flow 新 variance、keyof、match 模式、Records、opaque type 上下界等实验语法时,使用本插件;
  • 版本匹配:宿主 Prettier ≥ 3.7 时使用 0.37.0;更早版本可参考 0.33.x 的兼容与回退策略;peerDependency 约束为 Prettier ^3.0.0,不支持 v2;
  • 验证手段:所有格式化行为均可通过仓库内tests目录下的专项测试(含稳定性与快照断言)复核,便于在接入前评估对自身代码库的影响。
  • 语言运行时
  • 编译器
  • 移动开发

【免费下载链接】hermes

A JavaScript engine optimized for running React Native.

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

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/24 13:29:50

Django实现xAdmin更新表单自动同步到其他模型

在内容管理系统(CMS)中,通常会遇到这样一个需求:编辑一个模块的内容后,能够自动同步更新到另一个模块,但这些内容可能需要根据不同的规则进行调整或删减。这样可以使得相同的内容在不同模块中得到不同的用途和展示效果。 在本教程中,将详细介绍如何通过Django的adminx机…

作者头像 李华
网站建设 2026/9/24 13:29:40

Django重写User模型修改明文密码加密方法

在Django项目中,使用默认的用户管理系统时,可能会遇到用户密码以明文方式存储的问题。这不仅影响用户体验,也对安全性产生了较大的隐患。因此,在开发过程中,需要对Django的用户管理机制进行调整,确保用户密码在创建时能够被正确加密存储,避免以明文形式显示。 本文将通过…

作者头像 李华
网站建设 2026/9/24 13:29:19

示波器实测RS485:从波形看懂差分信号与串口调试实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 13:29:10

离线语音AI芯片选型指南:蜂鸟系列在IoT家居的落地实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 13:28:33

SD NAND 深度解析:嵌入式存储选型、SPI/SDIO 驱动与 ECC 机制

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/24 13:26:30

Polar SI9000阻抗仿真原理与PCB高速设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华