花了一天我写了这样一个 Prettier 插件

作者:起个名字好难2025.10.10 19:52浏览量:1

简介:本文记录作者如何用一天时间开发一个Prettier插件,解决特定代码格式化需求,适合对代码格式化工具开发感兴趣的开发者。

引言:为什么需要自定义 Prettier 插件?

Prettier 作为目前最流行的代码格式化工具之一,凭借其“零配置”理念和开箱即用的特性,已成为前端开发者的标配工具。然而,在实际项目中,我们常常会遇到一些特殊需求:比如团队内部约定使用特定的注释格式、需要保留某些手动调整的代码结构,或者需要支持某些小众语法。这些需求往往无法通过现有的 Prettier 规则完全满足。

最近,我在一个项目中遇到了这样的场景:团队要求所有 SQL 语句必须使用特定的模板格式,而现有的 Prettier 插件要么不支持 SQL,要么无法满足我们的格式化要求。面对这个问题,我决定自己动手开发一个 Prettier 插件。令人惊喜的是,从开始构思到最终完成,我只花了一天时间。接下来,我将详细分享这个过程。

一、理解 Prettier 插件机制

在开始开发之前,首先需要理解 Prettier 的工作原理和插件机制。Prettier 的核心思想是通过解析代码生成抽象语法树(AST),然后根据配置规则重新生成格式化后的代码。插件的作用就是扩展 Prettier 的解析和打印能力,使其能够处理更多类型的代码。

Prettier 插件主要需要实现两个部分:

  1. 解析器(Parser):将源代码转换为 AST
  2. 打印机(Printer):将 AST 转换回格式化后的代码

Prettier 本身已经内置了对 JavaScript、TypeScript、HTML、CSS 等语言的支持,但可以通过插件机制添加对其他语言的支持,或者修改现有语言的处理方式。

二、开发环境准备

为了高效开发,我首先搭建了开发环境:

  1. 使用 Node.js 16+ 环境
  2. 初始化项目:npm init -y
  3. 安装必要依赖:
    1. npm install prettier @types/prettier typescript --save-dev
  4. 配置 TypeScript(可选,但推荐):
    1. {
    2. "compilerOptions": {
    3. "target": "ES2020",
    4. "module": "CommonJS",
    5. "outDir": "./dist",
    6. "esModuleInterop": true,
    7. "forceConsistentCasingInFileNames": true,
    8. "strict": true
    9. },
    10. "include": ["src/**/*"]
    11. }

三、插件核心实现

1. 解析器实现

Prettier 插件需要实现一个解析器函数,接收源代码和解析选项,返回 AST。对于我的 SQL 插件,我选择使用现有的 SQL 解析器(如 sql-parser)来生成 AST。

  1. import { parse as sqlParse } from "sql-parser";
  2. const parsers = {
  3. sql: {
  4. parse(text: string, parsers: any, options: any) {
  5. try {
  6. return sqlParse(text);
  7. } catch (error) {
  8. console.error("SQL parsing error:", error);
  9. return null;
  10. }
  11. },
  12. astFormat: "sql",
  13. },
  14. };
  15. export default parsers;

2. 打印机实现

打印机负责将 AST 转换回格式化后的代码。这是插件的核心部分,需要处理各种格式化细节。

  1. const printers = {
  2. sql: {
  3. print(path: any, options: any, print: any) {
  4. const node = path.getValue();
  5. // 处理 SELECT 语句
  6. if (node.type === "select") {
  7. const columns = node.columns.map(c => print(c).parts.join("")).join(", ");
  8. const from = print(node.from).parts.join("");
  9. const where = node.where ? ` WHERE ${print(node.where).parts.join("")}` : "";
  10. return `SELECT ${columns}${from}${where}`;
  11. }
  12. // 其他 SQL 语句类型的处理...
  13. return "";
  14. }
  15. }
  16. };
  17. export default printers;

3. 插件入口文件

插件需要一个入口文件,将解析器和打印机组合起来,并导出必要的接口。

  1. import parsers from "./parsers";
  2. import printers from "./printers";
  3. export default {
  4. parsers,
  5. printers,
  6. languages: [
  7. {
  8. name: "SQL",
  9. parsers: ["sql"],
  10. extensions: [".sql"],
  11. vscodeLanguageIds: ["sql"],
  12. },
  13. ],
  14. };

四、测试与调试

开发过程中,测试和调试至关重要。我采用了以下方法:

  1. 单元测试:使用 Jest 编写测试用例,验证解析器和打印机的行为

    1. import plugin from "../src";
    2. import prettier from "prettier";
    3. test("formats simple SELECT statement", async () => {
    4. const input = "SELECT id,name FROM users WHERE id=1";
    5. const formatted = await prettier.format(input, {
    6. parser: "sql",
    7. plugins: [plugin],
    8. });
    9. expect(formatted).toBe("SELECT id, name FROM users WHERE id = 1");
    10. });
  2. 手动测试:创建一个测试文件,使用 Prettier CLI 进行格式化

    1. npx prettier --write test.sql --plugin ./dist/index.js
  3. 调试技巧:在解析器和打印机中添加 console.log,观察 AST 结构和格式化过程

五、优化与完善

在基本功能实现后,我进行了以下优化:

  1. 性能优化:缓存解析结果,避免重复解析
  2. 错误处理:增强对错误 SQL 语法的容错能力
  3. 配置选项:添加插件特定的配置选项,如缩进大小、换行规则等
  4. 文档编写:编写 README.md,说明插件的安装和使用方法

六、发布与使用

完成开发后,我将插件发布到 npm:

  1. 创建 .npmignore 文件,排除不必要的文件
  2. 配置 package.json
    1. {
    2. "name": "prettier-plugin-sql-custom",
    3. "version": "1.0.0",
    4. "main": "dist/index.js",
    5. "scripts": {
    6. "build": "tsc",
    7. "prepare": "npm run build"
    8. }
    9. }
  3. 执行 npm publish

其他开发者可以通过以下方式使用:

  1. npm install prettier-plugin-sql-custom --save-dev

然后在 .prettierrc 中配置:

  1. {
  2. "plugins": ["prettier-plugin-sql-custom"],
  3. "sqlPluginOptions": {
  4. "indent": 2,
  5. "maxLineLength": 80
  6. }
  7. }

七、一天开发的启示

回顾这一天的开发过程,我有以下几点体会:

  1. Prettier 插件机制设计优秀:清晰的接口定义和模块化的设计使得插件开发变得简单
  2. TypeScript 的优势:类型检查大大减少了开发过程中的错误
  3. 现有工具的利用:复用现有的 SQL 解析器节省了大量时间
  4. 测试的重要性:充分的测试确保了插件的稳定性

八、对开发者的建议

如果你也有开发 Prettier 插件的需求,以下建议可能对你有帮助:

  1. 从简单需求开始:第一次开发可以选择处理某种特定语法结构,而不是整个语言
  2. 充分利用现有资源:查找是否有现成的解析器可以使用
  3. 遵循 Prettier 哲学:保持插件的配置简单,避免引入过多选项
  4. 编写详细文档:清晰的文档能降低用户的使用门槛
  5. 考虑性能影响:格式化操作可能频繁执行,需要注意性能优化

九、未来展望

这次一天开发 Prettier 插件的经历让我看到,通过合理利用现有工具和框架,开发者可以在短时间内实现有价值的功能。未来,我计划:

  1. 扩展插件支持更多 SQL 语法
  2. 添加对其他数据库方言的支持
  3. 开发可视化配置工具,降低插件配置难度
  4. 探索 Prettier 插件与其他工具的集成

结语

用一天时间开发一个 Prettier 插件,不仅解决了实际项目中的问题,也让我对代码格式化工具的内部机制有了更深入的理解。这个过程证明,只要掌握了正确的方法和工具,开发者可以在短时间内创造出有价值的成果。希望我的经验能为其他开发者提供启发和参考,鼓励更多人尝试开发自己的工具和插件。