花了一天我写了这样一个 Prettier 插件
2025.10.10 19:52浏览量:3简介:本文记录作者如何用一天时间开发一个Prettier插件,解决特定代码格式化需求,适合对代码格式化工具开发感兴趣的开发者。
引言:为什么需要自定义 Prettier 插件?
Prettier 作为目前最流行的代码格式化工具之一,凭借其“零配置”理念和开箱即用的特性,已成为前端开发者的标配工具。然而,在实际项目中,我们常常会遇到一些特殊需求:比如团队内部约定使用特定的注释格式、需要保留某些手动调整的代码结构,或者需要支持某些小众语法。这些需求往往无法通过现有的 Prettier 规则完全满足。
最近,我在一个项目中遇到了这样的场景:团队要求所有 SQL 语句必须使用特定的模板格式,而现有的 Prettier 插件要么不支持 SQL,要么无法满足我们的格式化要求。面对这个问题,我决定自己动手开发一个 Prettier 插件。令人惊喜的是,从开始构思到最终完成,我只花了一天时间。接下来,我将详细分享这个过程。
一、理解 Prettier 插件机制
在开始开发之前,首先需要理解 Prettier 的工作原理和插件机制。Prettier 的核心思想是通过解析代码生成抽象语法树(AST),然后根据配置规则重新生成格式化后的代码。插件的作用就是扩展 Prettier 的解析和打印能力,使其能够处理更多类型的代码。
Prettier 插件主要需要实现两个部分:
- 解析器(Parser):将源代码转换为 AST
- 打印机(Printer):将 AST 转换回格式化后的代码
Prettier 本身已经内置了对 JavaScript、TypeScript、HTML、CSS 等语言的支持,但可以通过插件机制添加对其他语言的支持,或者修改现有语言的处理方式。
二、开发环境准备
为了高效开发,我首先搭建了开发环境:
- 使用 Node.js 16+ 环境
- 初始化项目:
npm init -y - 安装必要依赖:
npm install prettier @types/prettier typescript --save-dev
- 配置 TypeScript(可选,但推荐):
{"compilerOptions": {"target": "ES2020","module": "CommonJS","outDir": "./dist","esModuleInterop": true,"forceConsistentCasingInFileNames": true,"strict": true},"include": ["src/**/*"]}
三、插件核心实现
1. 解析器实现
Prettier 插件需要实现一个解析器函数,接收源代码和解析选项,返回 AST。对于我的 SQL 插件,我选择使用现有的 SQL 解析器(如 sql-parser)来生成 AST。
import { parse as sqlParse } from "sql-parser";const parsers = {sql: {parse(text: string, parsers: any, options: any) {try {return sqlParse(text);} catch (error) {console.error("SQL parsing error:", error);return null;}},astFormat: "sql",},};export default parsers;
2. 打印机实现
打印机负责将 AST 转换回格式化后的代码。这是插件的核心部分,需要处理各种格式化细节。
const printers = {sql: {print(path: any, options: any, print: any) {const node = path.getValue();// 处理 SELECT 语句if (node.type === "select") {const columns = node.columns.map(c => print(c).parts.join("")).join(", ");const from = print(node.from).parts.join("");const where = node.where ? ` WHERE ${print(node.where).parts.join("")}` : "";return `SELECT ${columns}${from}${where}`;}// 其他 SQL 语句类型的处理...return "";}}};export default printers;
3. 插件入口文件
插件需要一个入口文件,将解析器和打印机组合起来,并导出必要的接口。
import parsers from "./parsers";import printers from "./printers";export default {parsers,printers,languages: [{name: "SQL",parsers: ["sql"],extensions: [".sql"],vscodeLanguageIds: ["sql"],},],};
四、测试与调试
开发过程中,测试和调试至关重要。我采用了以下方法:
单元测试:使用 Jest 编写测试用例,验证解析器和打印机的行为
import plugin from "../src";import prettier from "prettier";test("formats simple SELECT statement", async () => {const input = "SELECT id,name FROM users WHERE id=1";const formatted = await prettier.format(input, {parser: "sql",plugins: [plugin],});expect(formatted).toBe("SELECT id, name FROM users WHERE id = 1");});
手动测试:创建一个测试文件,使用 Prettier CLI 进行格式化
npx prettier --write test.sql --plugin ./dist/index.js
调试技巧:在解析器和打印机中添加
console.log,观察 AST 结构和格式化过程
五、优化与完善
在基本功能实现后,我进行了以下优化:
- 性能优化:缓存解析结果,避免重复解析
- 错误处理:增强对错误 SQL 语法的容错能力
- 配置选项:添加插件特定的配置选项,如缩进大小、换行规则等
- 文档编写:编写 README.md,说明插件的安装和使用方法
六、发布与使用
完成开发后,我将插件发布到 npm:
- 创建
.npmignore文件,排除不必要的文件 - 配置
package.json:{"name": "prettier-plugin-sql-custom","version": "1.0.0","main": "dist/index.js","scripts": {"build": "tsc","prepare": "npm run build"}}
- 执行
npm publish
其他开发者可以通过以下方式使用:
npm install prettier-plugin-sql-custom --save-dev
然后在 .prettierrc 中配置:
{"plugins": ["prettier-plugin-sql-custom"],"sqlPluginOptions": {"indent": 2,"maxLineLength": 80}}
七、一天开发的启示
回顾这一天的开发过程,我有以下几点体会:
- Prettier 插件机制设计优秀:清晰的接口定义和模块化的设计使得插件开发变得简单
- TypeScript 的优势:类型检查大大减少了开发过程中的错误
- 现有工具的利用:复用现有的 SQL 解析器节省了大量时间
- 测试的重要性:充分的测试确保了插件的稳定性
八、对开发者的建议
如果你也有开发 Prettier 插件的需求,以下建议可能对你有帮助:
- 从简单需求开始:第一次开发可以选择处理某种特定语法结构,而不是整个语言
- 充分利用现有资源:查找是否有现成的解析器可以使用
- 遵循 Prettier 哲学:保持插件的配置简单,避免引入过多选项
- 编写详细文档:清晰的文档能降低用户的使用门槛
- 考虑性能影响:格式化操作可能频繁执行,需要注意性能优化
九、未来展望
这次一天开发 Prettier 插件的经历让我看到,通过合理利用现有工具和框架,开发者可以在短时间内实现有价值的功能。未来,我计划:
- 扩展插件支持更多 SQL 语法
- 添加对其他数据库方言的支持
- 开发可视化配置工具,降低插件配置难度
- 探索 Prettier 插件与其他工具的集成
结语
用一天时间开发一个 Prettier 插件,不仅解决了实际项目中的问题,也让我对代码格式化工具的内部机制有了更深入的理解。这个过程证明,只要掌握了正确的方法和工具,开发者可以在短时间内创造出有价值的成果。希望我的经验能为其他开发者提供启发和参考,鼓励更多人尝试开发自己的工具和插件。

发表评论
登录后可评论,请前往 登录 或 注册