# 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