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"] }
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 的通用配置项(printWidth、tabWidth、useTabs 等),也可以声明自定义选项。自定义选项需要在插件的 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 的插件机制提供了扩展的可能性,但"可能性"不等于"必要性"。

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