millionco/react-doctor

GitHub: millionco/react-doctor

一款专为 React 项目设计的代码质量诊断工具,通过多维度扫描给出健康评分并输出可操作的诊断结果,尤其擅长捕捉 AI 编程助手生成的反模式代码。

Stars: 7946 | Forks: 252

React Doctor [![版本](https://img.shields.io/npm/v/react-doctor?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/react-doctor) [![下载量](https://img.shields.io/npm/dt/react-doctor.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/react-doctor) 你的 AI 编程助手写了糟糕的 React 代码,这个工具能帮你揪出来。 只需一条命令即可扫描你的代码库,并输出一个 **0 到 100 的健康评分**以及具有可操作性的诊断结果。 支持 Next.js、Vite 和 React Native。 ### [查看实际演示 →](https://react.doctor) ## 安装说明 在你的项目根目录下运行: ``` npx -y react-doctor@latest . ``` 你将获得一个评分(75+ 优秀,50 到 74 需要改进,50 以下 危急)以及涵盖状态与副作用、性能、架构、安全性、可访问性和死代码等方面的问题列表。规则会根据你的框架和 React 版本自动开启。 https://github.com/user-attachments/assets/07cc88d9-9589-44c3-aa73-5d603cb1c570 ## 为你的 AI 编程助手安装 让你的 AI 编程助手学习 React 最佳实践,从源头上避免编写糟糕的代码。 ``` npx -y react-doctor@latest install ``` 系统会提示你选择要为哪些检测到的助手进行安装。传递 `--yes` 以跳过提示。 支持 Claude Code、Cursor、Codex、OpenCode 以及 50 多种其他 AI 编程助手。 ## GitHub Actions 本仓库附带了一个复合操作。将其放入 `.github/workflows/react-doctor.yml` 中: ``` name: React Doctor on: pull_request: push: branches: [main] permissions: contents: read pull-requests: write # required to post PR comments jobs: react-doctor: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 with: fetch-depth: 0 # required for `diff` - uses: millionco/react-doctor@main with: diff: main github-token: ${{ secrets.GITHUB_TOKEN }} ``` 当在 `pull_request` 事件中设置了 `github-token` 时,扫描结果将作为 PR 评论发布(并自动更新)。该操作还会暴露一个 `score` 输出(0–100),你可以在后续步骤中使用它。 **输入项:** `directory`、`verbose`、`project`、`diff`、`github-token`、`fail-on`(`error` / `warning` / `none`)、`offline`、`node-version`。完整说明请参见 [`action.yml`](https://github.com/millionco/react-doctor/blob/main/action.yml)。 不想添加 marketplace 操作?纯 `npx` 形式也可以工作: ``` - run: npx -y react-doctor@latest --fail-on warning ``` ## 配置 在你的项目根目录下创建 `react-doctor.config.json`: ``` { "ignore": { "rules": ["react/no-danger", "jsx-a11y/no-autofocus"], "files": ["src/generated/**"], "overrides": [ { "files": ["components/modules/diff/**"], "rules": ["react-doctor/no-array-index-as-key", "react-doctor/no-render-in-render"] }, { "files": ["components/search/HighlightedSnippet.tsx"], "rules": ["react/no-danger"] } ] } } ``` 三个嵌套键,三个粒度层级——选择最适合你的最窄范围: - **`ignore.rules`** 在整个代码库中忽略某条规则。 - **`ignore.files`** 在匹配的文件上忽略**所有**规则(请谨慎使用——这会导致其他无关规则也失去覆盖)。 - **`ignore.overrides`** 仅在匹配的文件上忽略列出的规则,其他所有规则仍然生效。当单个文件(或 glob 匹配项)确实需要豁免一两规则,但仍应接受所有其他规则的扫描时,这正是你所需要的。 你也可以在 `package.json` 中使用 `"reactDoctor"` 键。CLI 参数始终会覆盖配置文件中的值。 React Doctor 会遵循 `.gitignore`、`.eslintignore`、`.oxlintignore`、`.prettierignore` 以及 `.gitattributes` 中的 `linguist-vendored` / `linguist-generated` 注解。内联的 `// eslint-disable*` 和 `// oxlint-disable*` 注释也同样会被遵循。 如果你有 JSON 格式的 oxlint 或 eslint 配置(`.oxlintrc.json` 或 `.eslintrc.json`),其规则会自动合并到扫描中并计入评分。设置 `adoptExistingLintConfig: false` 可禁用此行为。 #### 可选配套插件 当在被扫描的项目(或在你的 monorepo 中被提升)安装了以下 ESLint 插件时,React Doctor 会将它们的规则纳入同一次扫描中。两者均作为**可选的对等依赖**列出——只需安装你需要的即可。 | 插件 | 新增内容 | 命名空间 | | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | | [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) (v6 或 v7) | React Compiler 前端的正确性规则——在项目中检测到 React Compiler 时触发。 | `react-hooks-js/*` | | [`eslint-plugin-react-you-might-not-need-an-effect`](https://github.com/nickjvandyke/eslint-plugin-react-you-might-not-need-an-effect) (v0.10+) | 补充的“将副作用视为反模式”规则(`no-derived-state`、`no-chain-state-updates`、`no-event-handler`、`no-pass-data-to-parent` 等),它们会与 React Doctor 的原生 State & Effects 规则一起运行。 | `effect/*` | ### 内联抑制 ``` // react-doctor-disable-next-line react-doctor/no-cascading-set-state useEffect(() => { setA(value); setB(value); }, [value]); ``` 当两条规则在同一行触发时,你有两种等效的选择。在单个注释中用逗号分隔规则 ID: ``` // react-doctor-disable-next-line react-doctor/rerender-state-only-in-handlers, react-doctor/no-derived-useState const [localSearch, setLocalSearch] = useState(searchQuery); ``` 或者直接在诊断上方为每个规则堆叠一个注释。只要堆叠注释与目标行之间没有除其他 `react-doctor-disable-next-line` 注释之外的任何内容,堆叠的注释就会生效: ``` // react-doctor-disable-next-line react-doctor/rerender-state-only-in-handlers // react-doctor-disable-next-line react-doctor/no-derived-useState const [localSearch, setLocalSearch] = useState(searchQuery); ``` 代码行穿插在堆叠注释中会破坏链条:只有紧邻诊断上方的那条注释(以及堆叠在其上的连续 `react-doctor-disable-next-line` 注释)才会生效。如果某条注释看起来是相邻的,但规则仍然被触发,请运行 `react-doctor --explain `——它会报告是否找到了附近的抑制、它涵盖了哪些规则,以及为什么它没有生效。 块注释在 JSX 内部有效: ``` {/* react-doctor-disable-next-line react/no-danger */}
``` 对于多行 JSX,将注释紧跟在开始标签的上方即可覆盖整个属性列表(符合 ESLint 规范)。 ## Lint 插件(独立版) 相同的规则集同时作为 oxlint 插件和 ESLint 插件提供,因此你可以将其接入你的项目已在运行的任何 lint 引擎。 在 `.oxlintrc.json` 中配置 **oxlint**: ``` { "jsPlugins": [{ "name": "react-doctor", "specifier": "react-doctor/oxlint-plugin" }], "rules": { "react-doctor/no-fetch-in-effect": "warn", "react-doctor/no-derived-state-effect": "warn", }, } ``` **ESLint** flat 配置: ``` import reactDoctor from "react-doctor/eslint-plugin"; export default [ reactDoctor.configs.recommended, reactDoctor.configs.next, reactDoctor.configs["react-native"], reactDoctor.configs["tanstack-start"], reactDoctor.configs["tanstack-query"], ]; ``` 完整的规则列表位于 [`oxlint-config.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/oxlint-config.ts) 中。 ## CLI 参考 ``` Usage: react-doctor [directory] [options] Options: -v, --version display the version number --no-lint skip linting --no-dead-code skip dead code detection --verbose show every rule and per-file details (default shows top 3 rules) --score output only the score --json output a single structured JSON report -y, --yes skip prompts, scan all workspace projects --full skip prompts, always run a full scan --project select workspace project (comma-separated for multiple) --diff [base] scan only files changed vs base branch --staged scan only staged files (for pre-commit hooks) --offline skip telemetry --fail-on exit with error on diagnostics: error, warning, none --annotations output diagnostics as GitHub Actions annotations --explain diagnose why a rule fired or why a suppression didn't apply --why alias for --explain -h, --help display help ``` 当抑制不起作用时,`--explain `(或其别名 `--why `)会报告扫描器在该位置看到的内容,包括为什么附近的 `react-doctor-disable-next-line` 没有生效。诊断会区分常见的失效模式——针对不同规则的相邻注释(请使用逗号形式)、注释与诊断之间存在代码行(链条被破坏),或者根本没有附近的抑制。对于每个被标记的位置,相同的提示也会通过 `--verbose` 内联显示,并在 `--json` 输出中作为 `diagnostic.suppressionHint` 出现,因此单次扫描即可兼作抑制审查,无需单独的标志。 `--json` 会在标准输出上生成一个可解析的对象,并屏蔽所有人类可读的输出。错误仍然会产生一个带有 `ok: false` 的 JSON 对象,因此标准输出始终是一个有效的文档。 ### 配置键 | 键 | 类型 | 默认值 | | -------------------------- | -------------------------------- | -------- | | `ignore.rules` | `string[]` | `[]` | | `ignore.files` | `string[]` | `[]` | | `ignore.overrides` | `{ files, rules? }[]` | `[]` | | `lint` | `boolean` | `true` | | `deadCode` | `boolean` | `true` | | `verbose` | `boolean` | `false` | | `diff` | `boolean \| string` | | | `failOn` | `"error" \| "warning" \| "none"` | `"none"` | | `customRulesOnly` | `boolean` | `false` | | `share` | `boolean` | `true` | | `textComponents` | `string[]` | `[]` | | `rawTextWrapperComponents` | `string[]` | `[]` | | `respectInlineDisables` | `boolean` | `true` | | `adoptExistingLintConfig` | `boolean` | `true` | `textComponents` 是 `rn-no-raw-text` 的通用应急方案——列出本身行为类似于 React Native 的 `` 的组件(自定义的 `Typography`、`NativeTabs.Trigger.Label` 等),该规则就会将它们视为文本容器,而不管它们的子元素是什么样的。 `rawTextWrapperComponents` 是更窄范围的选项,适用于那些本身不是文本元素,但能安全地仅将字符串子节点通过内部 `` 传递的组件(例如 `heroui-native` 的 `Button`,它会将子节点字符串化并通过 `ButtonLabel` 渲染)。列出的包装器只有在它们的子节点完全可字符串化时才会抑制 `rn-no-raw-text`。带有混合子节点的包装器——例如 ``——仍然会被报告,因为包装器无法安全地与同级 JSX 元素一起路由原始文本。 ## Node.js API ``` import { diagnose, toJsonReport, summarizeDiagnostics } from "react-doctor/api"; const result = await diagnose("./path/to/your/react-project"); console.log(result.score); // { score: 82, label: "Great" } or null console.log(result.diagnostics); // Diagnostic[] console.log(result.project); // detected framework, React version, etc. ``` `diagnose` 接受第二个参数:`{ lint?: boolean, deadCode?: boolean }`。 ``` const report = toJsonReport(result, { version: "1.0.0" }); const counts = summarizeDiagnostics(result.diagnostics); ``` `react-doctor/api` 重新导出了 `JsonReport`、`JsonReportSummary`、`JsonReportProjectEntry`、`JsonReportMode`,以及底层的 `buildJsonReport` 和 `buildJsonReportError` 构建器。完整类型请参见 [`packages/react-doctor/src/api.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/api.ts)。 ## 排行榜 由 React Doctor 扫描的顶级 React 代码库,按得分排名。数据从 [millionco/react-doctor-benchmarks](https://github.com/millionco/react-doctor-benchmarks) 自动更新。 | # | 仓库 | 评分 | | -- | ---- | ----: | | 1 | [executor](https://github.com/RhysSullivan/executor) | 94 | | 2 | [nodejs.org](https://github.com/nodejs/nodejs.org) | 86 | | 3 | [tldraw](https://github.com/tldraw/tldraw) | 70 | | 4 | [t3code](https://github.com/pingdotgg/t3code) | 68 | | 5 | [better-auth](https://github.com/better-auth/better-auth) | 64 | | 6 | [excalidraw](https://github.com/excalidraw/excalidraw) | 63 | | 7 | [mastra](https://github.com/mastra-ai/mastra) | 63 | | 8 | [payload](https://github.com/payloadcms/payload) | 60 | | 9 | [typebot](https://github.com/baptisteArno/typebot.io) | 57 | | 10 | [plane](https://github.com/makeplane/plane) | 56 | 参见[完整排行榜](https://www.react.doctor/leaderboard)。 ## 资源与贡献 想要试用一下?请查看[演示](https://react.doctor)。 想要贡献代码?克隆此仓库,安装依赖,构建项目,然后提交 PR。 ``` git clone https://github.com/millionco/react-doctor cd react-doctor pnpm install pnpm build node packages/react-doctor/bin/react-doctor.js /path/to/your/react-project ``` 发现了 Bug?请前往[问题追踪器](https://github.com/millionco/react-doctor/issues)。 ### 许可证 React Doctor 是基于 MIT 许可证的开源软件。
标签:AI编程助手, Claude Code, Codex, Cursor, GitHub Actions, Linter, MITM代理, npx, pptx, React, React Native, SOC Prime, Syscalls, Vite, 云安全监控, 代码审查, 代码规范, 健康评分, 前端工具, 安全检查, 开发工具, 性能优化, 无障碍访问, 架构分析, 检测绕过, 死代码检测, 自动化攻击, 自动笔记, 静态分析