9.1 Monorepo 里的三种布局
- A. 根只放一份 biome.json
- 最简单——在仓库根跑
biome ci .管全部。适合包之间风格一致、且包数量不多(< 20)。 - B. 根一份基线 + 各包按需 extends
packages/config-biome导出基线;每个需要定制的包自己 biome.json 里extends它。风格一致性 + 细粒度调整兼得,是 2026 年主流做法。- C. 完全独立
- 每包各自 biome.json,互不 extends。只在特殊情况(不同语言、遗留系统共存)采用。
9.2 推荐布局(方案 B)
my-monorepo/
├─ biome.json # 根:组织全局、一键全仓 lint
├─ packages/
│ ├─ config-biome/ # 共享基线包
│ │ ├─ package.json
│ │ └─ base.json # ← 真正的基线
│ ├─ ui/
│ │ ├─ biome.json # extends 基线 + React 规则
│ │ └─ src/...
│ └─ api/
│ ├─ biome.json # extends 基线 + Node 规则
│ └─ src/...
└─ apps/
├─ web/biome.json
└─ docs/biome.json
packages/config-biome/base.json
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true },
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100
},
"linter": {
"enabled": true,
"rules": { "recommended": true }
},
"organizeImports": { "enabled": true },
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "always",
"trailingCommas": "all"
}
}
}
packages/ui/biome.json
{
"extends": ["@repo/config-biome/base.json"],
"linter": {
"rules": {
"a11y": { "recommended": true },
"style": { "useSelfClosingElements": "error" }
}
}
}
extends 的字符串可以是相对路径或包名(走 Node 解析)——把 base.json 作为 package.json 的 exports 暴露即可。
packages/config-biome/package.json
{
"name": "@repo/config-biome",
"version": "0.0.0",
"private": true,
"exports": {
"./base.json": "./base.json",
"./react.json": "./react.json",
"./node.json": "./node.json"
}
}
9.3 根 biome.json 的作用
虽然每包有自己的 biome.json,仍建议留一份根配置,写 files.ignore 屏蔽 dist/build 产物,并允许跑 biome check . 做全仓扫描。
{
"$schema": "https://biomejs.dev/schemas/2.0.0/schema.json",
"extends": ["./packages/config-biome/base.json"],
"files": {
"ignore": [
"**/dist/**",
"**/build/**",
"**/.next/**",
"**/.turbo/**",
"**/coverage/**"
]
}
}
Biome 自动寻找最近 biome.json
在 packages/ui/src/foo.ts 上执行的格式化会读 packages/ui/biome.json;未命中则向上找,最终落到根。这保证 IDE 打开任何文件都能找到正确配置。
9.4 Turborepo 下的任务定义
// turbo.json
{
"tasks": {
"lint": {
"inputs": ["src/**", "biome.json", "../../biome.json"],
"outputs": []
}
}
}
要点:inputs 里把 biome.json 都列上——任一改动才会使缓存失效。
每包 package.json
{
"scripts": {
"lint": "biome ci ."
}
}
根 package.json
{
"scripts": {
"lint": "turbo run lint",
"lint:root": "biome ci .",
"lint:affected": "turbo run lint --filter=[origin/main]"
}
}
9.5 pnpm 工作区
无 Turborepo 时用 pnpm 的 --filter + 并发:
pnpm -r --parallel exec biome ci .
或直接在根跑一次全仓 ci——对 Biome 性能友好(单进程 AST 缓存优于多进程并发):
biome ci .
9.6 包内 override 的常见场景
// packages/api/biome.json
{
"extends": ["@repo/config-biome/base.json"],
"overrides": [
{
"include": ["src/generated/**"],
"linter": { "enabled": false },
"formatter": { "enabled": false }
},
{
"include": ["src/scripts/**"],
"linter": {
"rules": { "suspicious": { "noConsoleLog": "off" } }
}
}
]
}
9.7 渐进推广:大仓库分阶段启用
老 monorepo 引入 Biome 常会遇到几千个历史 warning。分阶段:
- 第 1 阶段:只启用 formatter 全仓;lint 每包 enabled=false
- 第 2 阶段:每周选一个 package 开启 recommended lint、修完错误
- 第 3 阶段:开根配置 recommended,遗留特例写进 overrides
- 第 4 阶段:删除 ESLint,关闭旧规则残留
9.8 多团队协作:谁能改 base?
- 把
packages/config-biome/加到 GitHub CODEOWNERS,由平台/DevEx 团队 approve - 修改 base.json 的 PR 强制 run 全仓 CI,让改动影响可见
- 保留每包可 override,但尽量"先提议 base 改、再包内临时 override"
小结
Monorepo 里 Biome 的范式:一个 base 包 + 每包 extends + 少量 overrides。Turborepo 缓存要把 biome.json 列入 inputs。推广期可先 format、再 lint 分阶段开。下一章进入进阶主题——Biome v2 的 domain 规则组与 GritQL。