项目尚无统一工程规范,AI 助手与研发各自发挥导致返工风险。建立 总纲 + 细则两层结构:AGENTS.md 只写硬约束与索引(14 条红线、技术栈 基线、仓库结构、命令契约),细则拆为 10 份文档覆盖 Git、代码风格、 格式化、API 与路由、数据库、测试与 CI、Review、日志、密钥、资产命名。 影响面:仅文档。约束力从本轮起对新建代码生效;与现状冲突处以文档为准。 回滚方式:git revert 本提交。
142 lines
5.8 KiB
Markdown
142 lines
5.8 KiB
Markdown
# 03 · 格式化与 Lint
|
||
|
||
| 项 | 内容 |
|
||
| --- | --- |
|
||
| 文档版本 | V1.0(2026-11-02) |
|
||
| 依据 | 技术架构 §2.2(前端)、§10.1(工程约定)、§4.6(CI/CD) |
|
||
| 适用范围 | 全仓库可执行文件 |
|
||
| 强制级别 | 强制(CI 门禁,不允许「先合并后修」) |
|
||
|
||
---
|
||
|
||
## 1 一句话原则
|
||
|
||
**格式交给工具,评审只看逻辑。** 任何格式争论都应由配置解决,禁止在 Review 中讨论空格与换行。
|
||
|
||
---
|
||
|
||
## 2 Prettier(唯一格式化器)
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
| --- | --- | --- |
|
||
| `printWidth` | `100` | 与技术架构示例行宽一致 |
|
||
| `tabWidth` | `2` | |
|
||
| `useTabs` | `false` | |
|
||
| `semi` | `true` | |
|
||
| `singleQuote` | `true` | |
|
||
| `trailingComma` | `all` | |
|
||
| `arrowParens` | `always` | |
|
||
| `endOfLine` | `lf` | 跨平台一致 |
|
||
| `plugins` | `prettier-plugin-tailwindcss`(仅 `apps/web`) | 若引入 Tailwind;未引入则不加 |
|
||
|
||
规则:
|
||
|
||
- `FMT-01` 只允许一个 Prettier 版本、一个根配置;子包**不得**覆盖。
|
||
- `FMT-02` 禁止在文件中写 `prettier-ignore`,除非是生成代码或大段 markdown 表格,并注明原因。
|
||
- `FMT-03` CI 执行 `pnpm format:check`,失败即阻断。
|
||
- `FMT-04` 禁止把格式化产生的无关改动混进功能 PR(会产生冲突与 Review 噪音)。
|
||
|
||
---
|
||
|
||
## 3 ESLint(ESLint 9 flat config)
|
||
|
||
| 项 | 约定 |
|
||
| --- | --- |
|
||
| 配置位置 | 根 `eslint.config.mjs`,按 `apps/*`、`packages/*` 分层 export |
|
||
| 基础 | `@eslint/js` recommended + `typescript-eslint` 的 `strictTypeChecked` + `stylisticTypeChecked` |
|
||
| 前端附加 | `eslint-plugin-react-hooks`、`eslint-plugin-react-refresh`、`jsx-a11y` |
|
||
| 导入排序 | `eslint-plugin-simple-import-sort` |
|
||
| 命名检查 | `@typescript-eslint/naming-convention`(对应 [02](02-代码风格.md) §1) |
|
||
|
||
### 3.1 必须为 `error` 的规则
|
||
|
||
| 规则 | 目的 |
|
||
| --- | --- |
|
||
| `@typescript-eslint/no-explicit-any` | 禁 `any` |
|
||
| `@typescript-eslint/no-floating-promises` | 禁未处理 Promise |
|
||
| `@typescript-eslint/no-misused-promises` | 禁 async 传入非异步回调位 |
|
||
| `@typescript-eslint/consistent-type-imports` | 类型导入用 `import type` |
|
||
| `@typescript-eslint/no-unnecessary-condition` | 消除冗余判断(strict 下有效) |
|
||
| `no-restricted-imports` | **UI / 图标白名单**(见 §3.2) |
|
||
| `no-console` | 业务代码禁用,`scripts/**` 与 `worker` 的 logger 除外 |
|
||
| `import/no-cycle` | 禁循环依赖 |
|
||
| `react-hooks/rules-of-hooks` / `exhaustive-deps` | Hooks 正确性 |
|
||
| `jsx-a11y/*` 关键项 | 可访问性底线 |
|
||
|
||
### 3.2 白名单(`no-restricted-imports` 强制)
|
||
|
||
**禁止导入**(含子路径):
|
||
|
||
```
|
||
@douyinfe/semi-ui ← 只能用 @douyinfe/semi-ui-19
|
||
antd @ant-design/* @arco-design/*
|
||
@douyinfe/semi-icons @ant-design/icons react-icons lucide-react @heroicons/*
|
||
element-plus naive-ui @mui/* @chakra-ui/*
|
||
```
|
||
|
||
**规则**:
|
||
|
||
- `FMT-05` 图标只能 `import { IconXxx } from '@lingxi/ui/icons'`;禁止 `import * as Icons`、禁止直接引 `remixicon/icons/...`。
|
||
- `FMT-06` SVG 内联仅允许品牌 Logo(`packages/ui/brand/`),其他一律走图标出口。
|
||
- `FMT-07` 颜色字面量(`#rrggbb`、`rgb(`)在 `apps/web` 内禁用,除非在 `packages/ui/theme/` 的 token 定义文件。
|
||
|
||
### 3.3 允许 `warn` 的规则
|
||
|
||
`@typescript-eslint/no-non-null-assertion`(须注释证明非空)、`complexity`(> 15 警告)、`max-lines-per-function`(> 50 警告)。
|
||
|
||
---
|
||
|
||
## 4 提交钩子(本地)
|
||
|
||
| 钩子 | 工具 | 行为 |
|
||
| --- | --- | --- |
|
||
| `pre-commit` | husky + lint-staged | 仅对暂存文件跑 `eslint --fix` + `prettier --write` |
|
||
| `commit-msg` | commitlint | 校验 Conventional Commits(见 [01](01-Git规范.md) §2) |
|
||
| `pre-push` | husky | 跑 `pnpm typecheck`(本地拦截类型错误,避免浪费 CI) |
|
||
|
||
- `FMT-08` `lint-staged` 只处理暂存文件,**禁止**在钩子里跑全量 lint / 全量测试(太慢会诱发 `--no-verify`)。
|
||
- `FMT-09` 禁止 `git commit --no-verify`;如确因钩子故障,须在 PR 描述说明并附 CI 结果。
|
||
|
||
---
|
||
|
||
## 5 CI 门禁(阻断项)
|
||
|
||
| 阶段 | 命令 | 失败后果 |
|
||
| --- | --- | --- |
|
||
| 安装 | `pnpm install --frozen-lockfile` | 阻断(lock 不一致直接失败) |
|
||
| 格式 | `pnpm format:check` | 阻断 |
|
||
| Lint | `pnpm lint --max-warnings=0` | 阻断 |
|
||
| 类型 | `pnpm typecheck` | 阻断 |
|
||
| 单测 | `pnpm test -- --coverage` | 阻断(覆盖率不得低于基线) |
|
||
| 迁移 | `prisma migrate diff` 校验 schema 与迁移一致 | 阻断 |
|
||
| 构建 | `pnpm build` | 阻断 |
|
||
| 安全 | 依赖审计 + 镜像高危漏洞 | 阻断(高危 0) |
|
||
|
||
- `FMT-10` CI 与本地必须使用**同一份配置**,禁止 CI 单独放宽规则。
|
||
- `FMT-11` 覆盖率基线只升不降;下调须在 PR 说明理由并由技术负责人批准。
|
||
|
||
---
|
||
|
||
## 6 例外与豁免
|
||
|
||
| 场景 | 做法 |
|
||
| --- | --- |
|
||
| 生成代码(Prisma Client、OpenAPI 类型) | 在 `eslint.config.mjs` 中按路径 `ignores`,禁止在生成文件内加注释 |
|
||
| 第三方类型缺陷 | 文件级 `/* eslint-disable rule -- 原因 */`,必须写原因并在 PR 说明 |
|
||
| 单行特殊语法 | 行内 `// eslint-disable-next-line rule -- 原因` |
|
||
| 全文件 disable | **禁止**;确需则改为局部 disable 或调整规则配置 |
|
||
|
||
- `FMT-12` 任何 `eslint-disable` 都必须写明原因与到期处理方式(升级依赖 / 提 issue)。
|
||
- `FMT-13` 规则调整(新增 / 降级 / 关闭)必须单独 PR,说明影响文件数与整改计划。
|
||
|
||
---
|
||
|
||
## 7 检查清单
|
||
|
||
- [ ] `pnpm format:check` 通过,无格式化噪音混入
|
||
- [ ] `pnpm lint --max-warnings=0` 通过
|
||
- [ ] `pnpm typecheck` 通过
|
||
- [ ] 无新增 `eslint-disable`;已有的都有原因
|
||
- [ ] 未引入禁用包 / 禁用图标库
|
||
- [ ] 无散落色值,样式走 token
|