react-doctor 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -22
- package/dist/cli.js +253 -94
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.d.ts +48 -1
- package/dist/index.js +209 -21
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -41,17 +41,42 @@ Works with Claude Code, Cursor, Codex, OpenCode, and 50+ other agents.
|
|
|
41
41
|
|
|
42
42
|
## GitHub Actions
|
|
43
43
|
|
|
44
|
+
A composite action ships with this repository. Drop it into `.github/workflows/react-doctor.yml`:
|
|
45
|
+
|
|
44
46
|
```yaml
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
name: React Doctor
|
|
48
|
+
|
|
49
|
+
on:
|
|
50
|
+
pull_request:
|
|
51
|
+
push:
|
|
52
|
+
branches: [main]
|
|
53
|
+
|
|
54
|
+
permissions:
|
|
55
|
+
contents: read
|
|
56
|
+
pull-requests: write # required to post PR comments
|
|
57
|
+
|
|
58
|
+
jobs:
|
|
59
|
+
react-doctor:
|
|
60
|
+
runs-on: ubuntu-latest
|
|
61
|
+
steps:
|
|
62
|
+
- uses: actions/checkout@v5
|
|
63
|
+
with:
|
|
64
|
+
fetch-depth: 0 # required for `diff`
|
|
65
|
+
- uses: millionco/react-doctor@main
|
|
66
|
+
with:
|
|
67
|
+
diff: main
|
|
68
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
52
69
|
```
|
|
53
70
|
|
|
54
|
-
When `github-token` is set on `pull_request` events, findings are posted as a PR comment. The action also
|
|
71
|
+
When `github-token` is set on `pull_request` events, findings are posted (and updated) as a PR comment. The action also exposes a `score` output (0–100) you can use in subsequent steps.
|
|
72
|
+
|
|
73
|
+
**Inputs:** `directory`, `verbose`, `project`, `diff`, `github-token`, `fail-on` (`error` / `warning` / `none`), `offline`, `node-version`. See [`action.yml`](https://github.com/millionco/react-doctor/blob/main/action.yml) for full descriptions.
|
|
74
|
+
|
|
75
|
+
Prefer not to add a marketplace action? The bare `npx` form works too:
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
- run: npx -y react-doctor@latest --fail-on warning
|
|
79
|
+
```
|
|
55
80
|
|
|
56
81
|
## Configuration
|
|
57
82
|
|
|
@@ -64,20 +89,39 @@ Create a `react-doctor.config.json` in your project root:
|
|
|
64
89
|
"files": ["src/generated/**"],
|
|
65
90
|
"overrides": [
|
|
66
91
|
{
|
|
67
|
-
"files": ["components/diff/**"],
|
|
68
|
-
"rules": ["react-doctor/no-array-index-as-key"]
|
|
92
|
+
"files": ["components/modules/diff/**"],
|
|
93
|
+
"rules": ["react-doctor/no-array-index-as-key", "react-doctor/no-render-in-render"]
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"files": ["components/search/HighlightedSnippet.tsx"],
|
|
97
|
+
"rules": ["react/no-danger"]
|
|
69
98
|
}
|
|
70
99
|
]
|
|
71
100
|
}
|
|
72
101
|
}
|
|
73
102
|
```
|
|
74
103
|
|
|
75
|
-
|
|
104
|
+
Three nested keys, three layers of granularity — pick the narrowest one that fits:
|
|
105
|
+
|
|
106
|
+
- **`ignore.rules`** silences a rule across the whole codebase.
|
|
107
|
+
- **`ignore.files`** silences **every** rule on the matched files (use sparingly — it loses coverage for unrelated rules).
|
|
108
|
+
- **`ignore.overrides`** silences only the listed rules on the matched files, leaving every other rule active. This is what you want when a single file (or glob) legitimately needs an exemption from one or two rules but should still be scanned for everything else.
|
|
109
|
+
|
|
110
|
+
You can also use the `"reactDoctor"` key in `package.json`. CLI flags always override config values.
|
|
76
111
|
|
|
77
112
|
React Doctor respects `.gitignore`, `.eslintignore`, `.oxlintignore`, `.prettierignore`, and `linguist-vendored` / `linguist-generated` annotations in `.gitattributes`. Inline `// eslint-disable*` and `// oxlint-disable*` comments are honored too.
|
|
78
113
|
|
|
79
114
|
If you have a JSON oxlint or eslint config (`.oxlintrc.json` or `.eslintrc.json`), its rules get merged into the scan automatically and count toward the score. Set `adoptExistingLintConfig: false` to opt out.
|
|
80
115
|
|
|
116
|
+
#### Optional companion plugins
|
|
117
|
+
|
|
118
|
+
When the following ESLint plugins are installed in the scanned project (or hoisted in your monorepo), React Doctor folds their rules into the same scan. Both are listed as **optional peer dependencies** — install only what you want.
|
|
119
|
+
|
|
120
|
+
| Plugin | Adds | Namespace |
|
|
121
|
+
| ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ |
|
|
122
|
+
| [`eslint-plugin-react-hooks`](https://www.npmjs.com/package/eslint-plugin-react-hooks) (v6 or v7) | The React Compiler frontend's correctness rules — fired when a React Compiler is detected in the project. | `react-hooks-js/*` |
|
|
123
|
+
| [`eslint-plugin-react-you-might-not-need-an-effect`](https://github.com/nickjvandyke/eslint-plugin-react-you-might-not-need-an-effect) (v0.10+) | Complementary effects-as-anti-pattern rules (`no-derived-state`, `no-chain-state-updates`, `no-event-handler`, `no-pass-data-to-parent`, …) that run alongside React Doctor's native State & Effects rules. | `effect/*` |
|
|
124
|
+
|
|
81
125
|
### Inline suppressions
|
|
82
126
|
|
|
83
127
|
```tsx
|
|
@@ -88,7 +132,24 @@ useEffect(() => {
|
|
|
88
132
|
}, [value]);
|
|
89
133
|
```
|
|
90
134
|
|
|
91
|
-
When two rules fire on the same line,
|
|
135
|
+
When two rules fire on the same line, you have two equivalent options. Comma-separate the rule ids on a single comment:
|
|
136
|
+
|
|
137
|
+
```tsx
|
|
138
|
+
// react-doctor-disable-next-line react-doctor/rerender-state-only-in-handlers, react-doctor/no-derived-useState
|
|
139
|
+
const [localSearch, setLocalSearch] = useState(searchQuery);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Or stack one comment per rule directly above the diagnostic. Stacked comments are honored as long as nothing but other `react-doctor-disable-next-line` comments sits between them and the target line:
|
|
143
|
+
|
|
144
|
+
```tsx
|
|
145
|
+
// react-doctor-disable-next-line react-doctor/rerender-state-only-in-handlers
|
|
146
|
+
// react-doctor-disable-next-line react-doctor/no-derived-useState
|
|
147
|
+
const [localSearch, setLocalSearch] = useState(searchQuery);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A code line between stacked comments breaks the chain: only the comment immediately above the diagnostic (and any contiguous `react-doctor-disable-next-line` comments stacked on top of it) is honored. If a comment looks adjacent but the rule still fires, run `react-doctor --explain <file:line>` — it reports whether a nearby suppression was found, what rules it covers, and why it didn't apply.
|
|
151
|
+
|
|
152
|
+
Block comments work inside JSX:
|
|
92
153
|
|
|
93
154
|
<!-- prettier-ignore -->
|
|
94
155
|
```tsx
|
|
@@ -151,10 +212,11 @@ Options:
|
|
|
151
212
|
--fail-on <level> exit with error on diagnostics: error, warning, none
|
|
152
213
|
--annotations output diagnostics as GitHub Actions annotations
|
|
153
214
|
--explain <file:line> diagnose why a rule fired or why a suppression didn't apply
|
|
215
|
+
--why <file:line> alias for --explain
|
|
154
216
|
-h, --help display help
|
|
155
217
|
```
|
|
156
218
|
|
|
157
|
-
When a suppression isn't working, `--explain <file:line>` reports what the scanner sees at that location, including why a nearby `react-doctor-disable-next-line` didn't apply. The same hint surfaces inline with `--verbose` and in `--json` output as `diagnostic.suppressionHint
|
|
219
|
+
When a suppression isn't working, `--explain <file:line>` (or its alias `--why <file:line>`) reports what the scanner sees at that location, including why a nearby `react-doctor-disable-next-line` didn't apply. The diagnosis distinguishes the common failure modes — adjacent comment for a different rule (use the comma form), a code line between the comment and the diagnostic (the chain is broken), or no nearby suppression at all. The same hint surfaces inline with `--verbose` for every flagged site, and in `--json` output as `diagnostic.suppressionHint`, so a single scan doubles as a suppression audit without a separate flag.
|
|
158
220
|
|
|
159
221
|
`--json` produces a parsable object on stdout with all human-readable output suppressed. Errors still produce a JSON object with `ok: false`, so stdout is always a valid document.
|
|
160
222
|
|
|
@@ -211,15 +273,15 @@ Top React codebases scanned by React Doctor, ranked by score. Updated automatica
|
|
|
211
273
|
| # | Repo | Score |
|
|
212
274
|
| -- | ---- | ----: |
|
|
213
275
|
| 1 | [executor](https://github.com/RhysSullivan/executor) | 96 |
|
|
214
|
-
| 2 | [nodejs.org](https://github.com/nodejs/nodejs.org) |
|
|
215
|
-
| 3 | [tldraw](https://github.com/tldraw/tldraw) |
|
|
216
|
-
| 4 | [t3code](https://github.com/pingdotgg/t3code) |
|
|
217
|
-
| 5 | [
|
|
218
|
-
| 6 | [excalidraw](https://github.com/excalidraw/excalidraw) |
|
|
219
|
-
| 7 | [
|
|
220
|
-
| 8 | [
|
|
221
|
-
| 9 | [
|
|
222
|
-
| 10 | [
|
|
276
|
+
| 2 | [nodejs.org](https://github.com/nodejs/nodejs.org) | 86 |
|
|
277
|
+
| 3 | [tldraw](https://github.com/tldraw/tldraw) | 70 |
|
|
278
|
+
| 4 | [t3code](https://github.com/pingdotgg/t3code) | 68 |
|
|
279
|
+
| 5 | [better-auth](https://github.com/better-auth/better-auth) | 64 |
|
|
280
|
+
| 6 | [excalidraw](https://github.com/excalidraw/excalidraw) | 63 |
|
|
281
|
+
| 7 | [mastra](https://github.com/mastra-ai/mastra) | 63 |
|
|
282
|
+
| 8 | [payload](https://github.com/payloadcms/payload) | 60 |
|
|
283
|
+
| 9 | [typebot](https://github.com/baptisteArno/typebot.io) | 57 |
|
|
284
|
+
| 10 | [plane](https://github.com/makeplane/plane) | 56 |
|
|
223
285
|
|
|
224
286
|
<!-- LEADERBOARD:END -->
|
|
225
287
|
|