
Formbricks 共享 Prettier 预设解析formbricks/config-prettier的配置、消费方式与导入排序实践【免费下载链接】formbricksOpen Source Qualtrics Alternative项目地址: https://gitcode.com/GitHub_Trending/fo/formbricksFormbricks开源 Qualtrics 替代方案是一个使用 pnpm workspace Turborepo 管理的大型 TypeScript 单体仓库monorepo跨apps/与packages/两个顶层目录包含数十个应用与包。为了保证数百个包在代码格式上完全一致仓库把格式化基线收敛为独立包formbricks/config-prettier位于 packages/config-prettier。本文以该包为核心逐一拆解其预设内容、被根目录与子包消费的机制、插件体系以及它在 Husky、lint-staged、VS Code 中的落地用法帮助你在自己的多包仓库里复刻一套配置收敛 局部覆盖的格式化治理方案。一、包定位一行 README 背后的工程决策packages/config-prettier/README.md 用一句话界定了这个包的全部职责Shared Prettier preset for the Formbricks monorepo (110-char width, semicolons, double quotes, import-order groups).即这是 Formbricks 全仓库共享的 Prettier 预设核心风格约定是行宽 110 字符、强制分号、双引号、分组式导入排序。整个包只有三个文件prettier-preset.js预设本体CommonJS 导出package.json包元信息与插件依赖README.md消费方式说明包本身不包含任何业务代码它的价值在于单一事实来源single source of truth——全仓库的格式风格只在一处定义任何子包都不再各自维护一套 Prettier 选项。其package.json还揭示了一个关键工程选择包被标记为private: true且版本为1.0.0说明它不发布到 npm registry仅在仓库内通过 workspace 使用同时它把三个格式化插件固定为自身devDependencies{ name: formbricks/config-prettier, version: 1.0.0, private: true, license: MIT, devDependencies: { trivago/prettier-plugin-sort-imports: catalog:, prettier: catalog:, prettier-plugin-tailwindcss: catalog:, prettier-plugin-sort-json: catalog: } }注意这里使用了 pnpmcatalog:协议版本统一收口在 pnpm-workspace 的 catalog 中插件版本全仓库只维护一份从根上避免各包插件版本漂移。二、预设本体prettier-preset.js逐项拆解prettier-preset.js 是这份预设的核心实现完整内容如下module.exports { bracketSpacing: true, bracketSameLine: true, singleQuote: false, jsxSingleQuote: false, trailingComma: es5, semi: true, printWidth: 110, arrowParens: always, importOrder: [ // Mocks must be at the top as they contain vi.mock calls (.*)/__mocks__/(.*), server-only, THIRD_PARTY_MODULES, ^formbricks/(.*)$, ^~/(.*)$, ^/(.*)$, ^[./], ], importOrderSeparation: false, importOrderSortSpecifiers: true, };其中纯 Prettier 核心选项可以归纳为一张参数表选项值效果与默认值的差异printWidth110单行最大宽度 110 字符超宽自动换行默认 80放宽以适配较长的 TS 类型与链式调用semitrue语句结尾强制分号同默认值显式声明以锁定风格singleQuotefalse字符串一律使用双引号与很多默认单引号的工程相反是 Formbricks 的显式风格决策jsxSingleQuotefalseJSX 属性也使用双引号同默认值显式锁定trailingCommaes5仅在 ES5 合法处对象、数组添加尾逗号函数参数不加默认即es5显式声明arrowParensalways箭头函数参数永远带括号(x) x即使是单参数默认always显式锁定bracketSpacingtrue对象字面量花括号内留空格{ foo: 1 }同默认值bracketSameLinetrue多行 JSX/HTML 的与开始标签同行默认false独立换行这里刻意对齐 JSX 写法除核心选项外预设还注入了importOrder相关配置importOrderSeparation: false表示组间不插入空行、importOrderSortSpecifiers: true表示对同一组内的具名导入按字母排序。这些选项本身来自trivago/prettier-plugin-sort-imports后面会详细展开。三、消费机制为什么用相对路径 require而不是包名README 里有一个容易被忽略、但对复刻工程极有指导意义的细节The preset lives inprettier-preset.jsand is consumed byrelative path, not by package name — Prettier resolvesrequires from the config files location.即预设通过相对路径被引入而不是通过formbricks/config-prettier包名。原因是 Prettier 在加载.prettierrc.js时会从配置文件所在目录解析其中的require。如果写成require(formbricks/config-prettier)在 pnpm 的符号链接symlinknode_modules 布局下存在解析不确定性而相对路径是确定性的、不依赖安装器行为。仓库内共有三个消费点构成了根预设 → 局部覆盖的层级3.1 根目录.prettierrc.js全仓库默认配置.prettierrc.js 是根级配置通过相对路径./packages/config-prettier/prettier-preset引入预设并叠加插件const baseConfig require(./packages/config-prettier/prettier-preset); module.exports { ...baseConfig, plugins: [ trivago/prettier-plugin-sort-imports, prettier-plugin-tailwindcss, prettier-plugin-sort-json, ], jsonRecursiveSort: true, };这里展示了预设的纯数据设计它只导出可展开的选项对象不声明插件。是否启用插件、启用哪些插件由每个消费方根据自己的语言栈决定。根配置一口气启用了三个插件并把jsonRecursiveSort: true打开让 JSON 文件递归排序这正对应根package.json中把三个插件以catalog:形式列入devDependencies的做法。3.2packages/js-core/.prettierrc.cjs局部覆盖导入顺序packages/js-core/.prettierrc.cjs 是继承预设、再局部覆盖的典型例子。js-core 是浏览器端核心 SDK对依赖注入和 mock 有特殊要求因此它只启用 sort-imports 一个插件并整体重写了 importOrder// packages/js-core/.prettierrc.cjs // eslint-disable-next-line typescript-eslint/no-require-imports -- CJS config file const base require(../config-prettier/prettier-preset); module.exports { ...base, plugins: [trivago/prettier-plugin-sort-imports], importOrder: [ ^vitest$, // 1️⃣ vitest first THIRD_PARTY_MODULES, // 2️⃣ then other externals ^/.*$, // 3️⃣ then anything under / ^\\.\\/__mocks__\\/.*$, // 4️⃣ then anything under ./__mocks__/ ^[./], // 5️⃣ finally all relative imports ], importOrderSortSpecifiers: true, };可以看到文件头部注释特别说明了require的使用原因CJS 配置文件并在注释里通过eslint-disable-next-line与 ESLint 规则协调。注意这里importOrder是整体替换而非合并——这正是共享预设 局部覆盖模式的关键心智基础风格引号、分号、行宽全局统一导入分组策略允许按包裁剪。3.3apps/web/.prettierrc.js继承根配置并补 Tailwind 样式表apps/web/.prettierrc.js 展示了第三层用法——子应用直接继承根配置并注入 Tailwind 插件所需的上下文const baseConfig require(../../.prettierrc.js); module.exports { ...baseConfig, tailwindStylesheet: ./modules/ui/globals.css, };tailwindStylesheet是prettier-plugin-tailwindcss的配置项让插件读取指定的全局 CSS 来识别自定义 Tailwind 类名排序class 顺序以modules/ui/globals.css中的类定义为准。这里没有重复声明任何 Prettier 核心选项完全透传根配置只在需要差异化的地方叠加一层。四、导入排序规则七段分组的深层动机预设中最有工程味道的部分是importOrder。结合根配置它定义了七段固定顺序importOrder: [ (.*)/__mocks__/(.*), // 1️⃣ mock 模块最先 server-only, // 2️⃣ Next.js 服务端专用标记 THIRD_PARTY_MODULES, // 3️⃣ 第三方依赖 ^formbricks/(.*)$, // 4️⃣ 仓库内 formbricks/* 包 ^~/(.*)$, // 5️⃣ ~/ 别名 ^/(.*)$, // 6️⃣ / 别名 ^[./], // 7️⃣ 相对导入 ],每组模式的含义与动机(.*)/__mocks__/(.*)代码注释解释了原因——Vitest mock 文件里包含vi.mock调用必须最先执行才能正确拦截依赖。如果 mock 排在普通导入之后模块加载顺序会导致 mock 不生效。这一行是以测试正确性为第一优先级的显式工程决策。server-onlyNext.js 的server-only包用于标记仅服务端可导入把它单独排在第三方之前、紧跟 mock保证服务端边界语义不被其他导入干扰。THIRD_PARTY_MODULESsort-imports 插件的内置占位符代表所有未匹配其余规则的第三方包。^formbricks/(.*)$monorepo 内部包如formbricks/database、formbricks/surveys排在第三方之后区分外部依赖与内部依赖。^~/(.*)$与^/(.*)$仓库内使用的两类路径别名。^[./]所有相对导入兜底排在最后。配合importOrderSortSpecifiers: true同一组内具名导入会按字母排序而importOrderSeparation: false表示分组之间不插入空行保持代码紧凑。这套排序规则与仓库的代码规范文档 docs/development/standards/practices/code-formatting.mdx 中列出的导入顺序完全一致Mocks → Server-only → 第三方 →formbricks/*→ 别名 → 相对导入说明配置即文档、文档即配置的双向一致是 Formbricks 格式化治理的一部分。五、插件体系sort-imports / tailwindcss / sort-json 三者分工预设本身不装插件但根配置一次性启用了三个它们分别治理三种文件类型插件治理对象核心作用trivago/prettier-plugin-sort-imports.ts/.tsx/.js/.jsx按importOrder分组并排序导入语句prettier-plugin-tailwindcssTSX 中的 className按 Tailwind 语义排序 class需配合tailwindStylesheet读取全局 CSSprettier-plugin-sort-json.json对 JSON 键递归排序jsonRecursiveSort: true值得注意的是这三个插件都由formbricks/config-prettier自身固定版本而根package.json也以catalog:列出了相同的三个插件。也就是说预设包声明了配套插件的兼容版本根配置再实际加载它们——版本约束就近放在预设里避免消费方各自猜测该配哪个版本。六、落地使用脚本、Pre-commit 与编辑器6.1 一键格式化与校验根 package.json 暴露了两个全局脚本format: prettier --write ., format:check: prettier --check .pnpm format对全仓库执行prettier --write自动修复格式pnpm format:check只校验不改写CI 中用它阻止未格式化代码合入。配合prepare: husky install仓库在安装依赖时自动激活 Husky git 钩子。6.2 Pre-commitlint-staged 只格式化暂存文件根据 code-formatting.mdx 的记录仓库的lint-staged配置按文件类型分流{ lint-staged: { (apps|packages)/**/*.{js,ts,jsx,tsx}: [prettier --write], *.json: [prettier --write], packages/database/schema.prisma: [prisma format] } }TS/JS 与 JSON 走 Prettier会自动命中上面的共享预设Prisma schema 走prisma format。文档还特别强调pre-commit 钩子只做格式化不做 ESLint——ESLint 检查交给pnpm lint与 CI 的 Run Linters 任务从而把提交耗时控制住。这与根package.json中pre-commit: lint-staged的脚本定义一致。6.3 VS Code保存即格式化同一份规范文档给出了推荐的编辑器集成方式这也是格式化基线能在开发者本机生效的关键{ editor.formatOnSave: true, editor.defaultFormatter: esbenp.prettier-vscode, editor.codeActionsOnSave: { source.fixAll.eslint: true }, typescript.tsdk: node_modules/typescript/lib }安装esbenp.prettier-vscode、dbaeumer.vscode-eslint、bradlc.vscode-tailwindcss三个扩展后编辑器会读取工作区内的.prettierrc.js从而间接使用formbricks/config-prettier预设实现保存即格式化让格式统一在编码阶段而非评审阶段完成。七、可复刻的治理模式总结从formbricks/config-prettier可以提炼出一套可直接迁移到任何多包仓库的格式化治理方案单一预设包把 Prettier 核心风格行宽、引号、分号、尾逗号、箭头括号收敛进一个private包用module.exports导出纯选项对象不混入插件声明相对路径消费消费方通过require(../../packages/config-prettier/prettier-preset)引入规避 pnpm symlink 布局下的解析不确定性局部覆盖需要差异化时如 js-core 重写 importOrder、web 补充 tailwindStylesheet用展开运算符覆盖子集其余继承插件就近固定版本配套插件以catalog:协议固定在预设包的devDependencies中消除版本漂移配置与文档双向一致预设里的importOrder与代码规范文档的导入顺序一一对应评审时可以相互印证三层执行防线编辑器 formatOnSave开发时→ lint-staged pre-commit提交时→ CIformat:check合入时。这套定义一处、分层消费、多防线执行的模式正是 Formbricks 这个数百文件规模仓库能够长期保持代码风格一致性的底层机制想要在自己的 monorepo 中落地相同的工程实践从复制这份三文件结构的 config-prettier 包开始即可。【免费下载链接】formbricksOpen Source Qualtrics Alternative项目地址: https://gitcode.com/GitHub_Trending/fo/formbricks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考