PureChatDocumentation
开发指南质量检查

Lint 与类型检查脚本

了解 PureChatNext 的代码、样式、文档、依赖循环与类型检查命令。

根目录 package.json 里的质量检查脚本。提交前跑 pnpm lint;其余命令按需单独执行。

怎么用

pnpm lint              # 聚合检查(不改写文件)
pnpm lint:ts           # 仅 ESLint
pnpm lint:docs         # 公开文档结构、链接与索引
pnpm lint:style:fix    # Stylelint 并自动修复 CSS
pnpm prettier          # Prettier 写回
pnpm prettier:check    # Prettier 只检查

pnpm lint 只检查、不格式化。需要改写源码时用带 :fix 的脚本或 pnpm prettier。

异步调用与回调简化

  • 禁止使用一元 void 包装调用。需要 fire-and-forget 时直接写表达式语句;需要处理失败时显式追加 .catch(...)。
  • 事件回调不要写无参数的 () => fn(),直接传递 fn。带参数的回调、延迟执行回调、需要转换返回值的回调,以及需要保留成员方法 this 绑定的包装可以保留。
// ✅
onClick={handleRefresh}
onClose={api.closeWindow}
loadData()

// ❌
onClick={() => handleRefresh()}
onClose={() => void api.closeWindow()}
void loadData()

这些约束由根目录 eslint.config.mjs 中的 no-void 与 purechat/no-useless-event-arrow 提供。void 类型标注(例如 () => void)不受 no-void 影响。

脚本一览

命令职责在 pnpm lint 内改写文件CI
lint并行跑下表「聚合」项,再跑 typecheck—否否
lint:tsESLint(src/、tests/、桌面端、文档站、scripts/)是否否
lint:spa-env-importsSPA/客户端禁止导入服务端 env是否是(独立 job)
lint:styleStylelint 检查 CSS是否否
lint:style:fixStylelint 并 --fix否是否
lint:circular循环依赖(src + packages 并行)是否否
lint:docs公开文档结构、链接、锚点和索引可达性是否否
typecheck根 tsc --noEmit是(并行组之后)否否
lint:console禁止业务 console.log/debug否否否
lint:mdremark 检查 Markdown是否否
lint:md:fixremark --output 写回否是否
lint:unusedknip 未使用文件/导出否否否
prettierPrettier 写回否是否
prettier:checkPrettier 只检查否否否
check:spa-env-leak扫描 SPA 构建产物是否内联密钥否(挂在 build)否是
check:prepushPush 前类型检查(等同 typecheck)否否否(本地 hook)
hooks:install启用 .githooks/pre-push否否(改本地 git config)否
typecheck:packages各 @pure/* 包内 tsc --noEmit否否否

聚合链路

flowchart LR
  subgraph parallel [并行]
    lintTs[lint:ts]
    spaEnv[lint:spa-env-imports]
    style[lint:style]
    circular[lint:circular]
    docs[lint:docs]
    markdown[lint:md]
  end
  typecheck[typecheck]
  parallel --> typecheck

并行由 scripts/run-parallel.mjs 编排:任一子脚本非 0 即失败。typecheck 放在并行组之后,避免与 ESLint / dpdm 抢满 CPU。

lint:circular 内部同样并行 lint:circular:main(src/**/*.{ts,tsx})与 lint:circular:packages(packages/**/src/**/*.{ts,tsx})。

逐项说明

lint:ts

eslint src/ tests/ apps/desktop/src/ apps/docs/src/ scripts/ playwright.config.ts --concurrency=auto

配置:eslint.config.mjs(eslint-config-next + 顶层 import type 规则)。

规则 glob 写了 **/*.{ts,tsx}(含 packages/**),CLI 当前检查主应用、桌面端、文档站和 scripts/;packages/ 上仍有存量 TypeScript ESLint 报错(no-explicit-any、no-unsafe-function-type、ban-ts-comment 等),因此尚未扩扫描范围。

SPA 客户端另有 no-restricted-imports:禁止从 @/envs、@pure/env 导入;禁止从 next/navigation 导入 redirect / notFound 等服务端 API,以及已删除的 @/utils/navigation、@/utils/link 转手层。与下面的专用脚本互补。

lint:spa-env-imports

scripts/web/check-spa-env-imports.mjs 用正则扫客户端目录(src/spa、src/features、src/components 等),包括动态 import()。CI 单独跑这一条,避免被其它 ESLint 噪音挡住导入边界问题。

例外:src/components/Analytics 允许 @/envs/analytics。

lint:style / lint:style:fix

Stylelint,配置:stylelint.config.mjs(standard + Tailwind 4)。扫描 {src,tests}/**/*.css;仓库里实际只有 src/styles/ 与少量 CSS Modules。

pnpm lint 走无 --fix 的 lint:style。需要自动修复时用 pnpm lint:style:fix。

lint:circular

dpdm 检测静态循环依赖。--skip-dynamic-imports circular 会跳过动态 import 形成的环;--exit-code circular:1 发现环则失败。

lint:console(可选)

scripts/check-console-log.mts 扫描 src/ 中的 console.log / console.debug。允许 console.warn / error / info,并跳过测试文件与生成文件。调试日志请用 debug 命名空间,见 .cursor/rules/debug-usage.md。

未纳入 pnpm lint。

lint:docs

scripts/check-docs.mjs 校验公开文档的目录分类、相对链接、标题锚点和索引可达性。它排除 docs/private/,并禁止公开文档使用 locale 后缀或散落在 docs/ 根目录。

已纳入 pnpm lint。

lint:md / lint:md:fix

remark + remark-preset-lint-recommended,配置:remark.config.mjs,忽略列表:.remarkignore。

  • lint:md:--frail 有 warning 即退出 1,不写回
  • lint:md:fix:--output 写回

lint:md 已纳入 pnpm lint。文档目录已从 Prettier 忽略,格式以 remark 为准。

lint:unused(可选)

knip,配置:knip.json。检查未使用文件、导出、类型、enum 成员与重复导出。噪音较大,适合定期跑,不进聚合。

prettier / prettier:check(可选)

配置:.prettierrc.js,忽略:.prettierignore(含 docs/**、构建产物、lockfile)。prettier --write . 会改写匹配文件;CI 或预检用 prettier:check。

check:spa-env-leak

scripts/web/check-spa-env-leak.mjs 扫描 dist/ 与 public/_spa/,防止服务端密钥被 Vite 内联进前端 bundle。

挂在 build / build:docker 之后(需先有 SPA 产物),不是源码 lint。CI 在 build:spa 前注入含 DO-NOT-LEAK 的 canary 密钥,再扫描产物。

typecheck / check:prepush

根目录 tsconfig.json 的 include 为 **/*.ts / **/*.tsx,因此 packages 会一并被检查,compilerOptions 用的是根配置(module: esnext、moduleResolution: bundler 等)。

Vercel 跑 next build 时会做同类类型检查;CI 当前不跑 tsc,所以本地 push 前应拦住。启用一次:

pnpm hooks:install   # git config core.hooksPath .githooks

之后每次 git push 会跑 pnpm check:prepush(即 tsc --noEmit)。紧急跳过:SKIP_PREPUSH=1 git push。

typecheck:packages

pnpm -r --filter "@pure/*" exec tsc --noEmit

只对自带 tsconfig.json 的包有效。目前是:

  • packages/web-crawler
  • packages/chat-adapter
  • packages/model-bank

其余 @pure/* 没有自己的 tsconfig,跑这条会失败。包内配置也可能与根 typecheck 不同(例如 web-crawler 为 CommonJS)。日常以根 typecheck 为准。

CI

.github/workflows/ci.yml 当前只跑:

  1. lint:spa-env-imports
  2. build:spa + build:spa:copy + check:spa-env-leak(canary 密钥)

不跑完整 pnpm lint(ESLint、Stylelint、tsc、dpdm)。本地提交前仍应跑 pnpm lint。

覆盖缺口与剩余优化

本次已做:检查与 --fix 分离;prettier 去掉互相冲突的 -c --write;lint / lint:circular 并行;公开文档结构检查与 remark 纳入聚合。

刻意未做(避免一次暴露大量存量问题):

项说明
lint:ts 扩到 packages/约 40+ 条存量 @typescript-eslint 报错,扩范围会变成清理 PR
lint:ts 扩到 scripts/脚本以 .mjs / .mts 为主,未纳入
CI 接入 pnpm lint目前仅导入边界 + 产物泄漏
lint:console 并入聚合或改成 ESLint no-console可去掉自定义脚本,但需先清存量 console.log
knip 进 CI 或 pnpm lint噪音与未使用项存量较多
给全部 @pure/* 补 tsconfig.json才能让 typecheck:packages 覆盖整个 workspace

本页内容

问问文档

基于公开文档回答