4.4插件系统扩展语言


4.4 插件系统扩展语言

Prettier 的语言扩展能力

Prettier 核心只内置了 JavaScript、TypeScript、CSS、JSON、HTML、Markdown、GraphQL 和 YAML 的支持。但它的架构设计支持通过插件扩展到其他语言。

Prettier 的插件机制基于一个简洁的接口:一个插件只需要提供三样东西——一个解析器(把代码变成 AST)、一个 AST-to-Doc 转换器(把 AST 变成 Doc IR)、以及一些元信息(支持的文件类型、扩展名等)。打印算法是通用的,不需要插件提供。

这种设计的好处是:要支持一种新语言,你只需要写特定于那个语言的解析和转换逻辑,不需要重新实现整个格式化管线。

官方维护的插件

Prettier 官方维护了几个重要的语言插件:

@prettier/plugin-php:PHP 格式化支持。这是 Prettier 插件中比较成熟的官方插件之一,支持 PHP 的各种语法结构。

@prettier/plugin-ruby:Ruby 格式化支持。

@prettier/plugin-java:Java 格式化支持。

@prettier/plugin-xml:XML 格式化支持,也覆盖 HTML 的某些边缘情况。

这些插件通过 npm install 安装,然后通过 Prettier 的插件发现机制自动加载:

npm install --save-dev @prettier/plugin-php

安装后,Prettier 会自动检测 node_modules 中的插件并加载。不需要额外的配置来注册——插件通过 prettier 关键字声明自己是 Prettier 插件,Prettier 在启动时扫描 node_modules 发现它们。

社区插件

除了官方插件,社区贡献了大量第三方插件,覆盖更广泛的语言:

插件 语言 说明
prettier-plugin-toml TOML Cargo.toml 等配置文件
prettier-plugin-sql SQL 多种 SQL 方言
prettier-plugin-sh Shell/Bash Shell 脚本格式化
prettier-plugin-pug Pug Pug 模板引擎
prettier-plugin-astro Astro Astro 框架的 .astro 文件
prettier-plugin-twig Twig Twig 模板
prettier-plugin-ejs EJS EJS 模板

社区插件的质量参差不齐——有些非常成熟,有些在特定语法上还有 bug。在正式项目中使用社区插件前,建议先在非关键文件上试用,确认格式化结果符合预期。

插件的安装与配置

插件安装后,Prettier 会自动加载。但如果你需要指定插件的加载路径(比如插件不在 node_modules 中),可以用 plugins 配置项:

{ "plugins": ["@prettier/plugin-php"] }

如果插件安装在项目外部(比如全局安装的插件),需要指定完整路径:

{ "plugins": ["/usr/local/lib/node_modules/prettier-plugin-sh"] }

图 4-5:Prettier 插件系统工作原理

插件的局限

Prettier 插件机制有一些固有的局限需要了解。

插件不能改变核心布局算法。插件只提供解析器和 AST-to-Doc 转换器,最终的布局决策由 Prettier 核心的打印算法完成。如果某种语言的排版需求跟打印算法不兼容(比如需要表格对齐这种 Prettier 核心不支持的功能),插件很难实现。

插件版本必须跟 Prettier 主版本兼容。Prettier 的插件 API 在主版本之间可能变化。如果 Prettier 升级到 v4(假设),现有的 v3 插件可能需要更新才能兼容。升级 Prettier 版本时,需要同时检查插件的兼容性。

插件增加启动时间。每个插件都需要被加载和初始化。如果你安装了大量插件但大部分文件类型都不需要它们,每次 Prettier 启动都会被拖慢。建议只安装项目实际需要的插件。

自己写一个最小插件

理解插件机制最快的方式是写一个最小的。假设你想支持一种极简的伪语言,语法是 KEY: VALUE 行。一个插件需要提供 languages 元信息、parsers(把文本变成 AST)和 printers(把 AST 变成 Doc):

// prettier-plugin-mini.js module.exports = { languages: [ { name: "MiniLang", parsers: ["mini-parser"], extensions: [".mini"], }, ], parsers: { "mini-parser": { parse: (text) => { // 极简解析:按行拆成 { key, value } 节点 return text .trim() .split("\n") .map((line) => { const [key, value] = line.split(":").map((s) => s.trim()); return { type: "pair", key, value }; }); }, astFormat: "mini-ast", locStart: () => 0, locEnd: () => 0, }, }, printers: { "mini-ast": { print: (path) => { const node = path.getValue(); if (node.type === "pair") { return [node.key, ": ", node.value]; } return ""; }, }, }, };

这个插件没有处理复杂语法,但展示了最小骨架:languages 声明文件类型,parsers 把文本变成 AST,printers 把 AST 变成 Doc。注册方式是在配置里加一行:

{ "plugins": ["./prettier-plugin-mini.js"] }

写真实插件时,解析器通常是复用现成的语言解析器(比如 @babel/parser),而不是从零写语法分析。

插件与配置的配合

插件可以读取并应用 Prettier 的通用配置项(printWidthtabWidthuseTabs 等),也可以声明自定义选项。自定义选项需要在插件的 options 字段里定义,之后就能在配置文件里像普通选项一样使用。

选择插件时的判断顺序:官方插件优先 → 活跃维护的社区插件 → 需要小范围定制时自己写。自己写的插件要特别注意维护成本——格式化器的输出一旦变化,会影响全仓库代码,因此建议先在独立分支验证并跑完整测试。

不用插件的替代方案

对于 Prettier 不直接支持且没有可靠插件的语言,有几种替代策略:

通过管道调用其他格式化工具。很多语言有自己的格式化工具(Python 的 Black、Go 的 gofmt、Rust 的 rustfmt)。在编辑器中可以为不同文件类型配置不同的格式化器,不一定非要让 Prettier 统一处理所有语言。

在 CI 中分别检查。CI 流水线可以为不同语言运行不同的格式检查:

# CI 中分别检查 npx prettier --check "**/*.{js,ts,css,json,md}" black --check "**/*.py" gofmt -l .

Prettier 的核心价值在于 JavaScript/TypeScript 生态——这是它的主战场,也是它做得最好的领域。对于其他语言,使用该语言的专用格式化工具通常是更好的选择。Prettier 的插件机制提供了扩展的可能性,但"可能性"不等于"必要性"。

04-04-fig01

图 4-6:Prettier 语言支持范围总览

插件系统让 Prettier 的格式化能力超越了 JavaScript 生态的边界。但在使用插件时保持审慎——选择成熟度高的插件,明确它的局限,不要因为"统一用 Prettier"的执念而使用质量不够可靠的插件来处理关键语言的代码。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U