Squashes work that closes#143 plus follow-on cleanup of the browser
surface and dead-code.
**`adoptExistingLintConfig` (default-on)** — when a project has a
JSON-format `.oxlintrc.json` or `.eslintrc.json` at the scanned
directory or any ancestor up to the nearest project boundary, that
config is folded into the same scan via oxlint's `extends` field.
Diagnostics from the user's existing rules count toward the 0–100
health score alongside the curated react-doctor rule set; if oxlint
can't load the user config, react-doctor logs the reason on stderr
and retries the scan once without `extends`. JS / TS configs are
silently skipped (oxlint's `extends` can't evaluate them). Coverage
broadened to `.ts` and `.js` files (previously the parser dropped
non-JSX). Closes#143.
**`react-doctor/eslint-plugin` flat-config export** — the same
react-doctor rule set is shipped as an ESLint flat-config plugin so
projects that already run ESLint can wire it up without depending on
the CLI. Includes presets (`recommended`, `next`, `react-native`,
`tanstack-start`, `tanstack-query`, `all`) plus cherry-picking.
**Removed browser entrypoints, browser CLI, and `react-doctor-browser`
package** — the in-browser diagnostics pipeline (`react-doctor/browser`
+ `/worker`), the `react-doctor browser …` CLI subcommand
(`start` / `stop` / `status` / `snapshot` / `screenshot` /
`playwright`), and the standalone `react-doctor-browser` workspace
package (Playwright + CDP + cookie extraction) are gone. Nothing in
the monorepo consumed them and they pulled in a heavy dep footprint
(`playwright`, `libsql`). Source is preserved on the
`archive/browser` branch.
**`diagnose-core` engine inlined into `index.ts::diagnose`** —
the dependency-injection shape was designed for browser sharing.
Only one caller now, so collapsed into a flat orchestrator. Score
helpers moved from `src/core/` to `src/utils/`; `src/core/` directory
removed.
**Smaller cleanups** — drop unused `matchGlobPattern` wrapper,
`isMemberProperty` import in nextjs rule, `fileContainsPattern`
helper, and various dead test locals; ignore `tests/fixtures/**`
from workspace lint and add `.oxlintignore` so `lint --fix` against
fixture paths can't strip the `debugger;` statements that
`adoptExistingLintConfig` tests assert on.
Tests: 452 / 452 pass on Node 22 + 24. Build smoke green.
- Added `.turbo` to `.gitignore`.
- Removed obsolete `.oxlintrc.json` configuration file.
- Updated `package.json` to use `turbo` for script commands and added new `typecheck` and `check` scripts.
- Updated `devDependencies` to include `@voidzero-dev/vite-plus-core` and `turbo`, while removing `oxlint` and `tsdown`.
- Adjusted `pnpm-lock.yaml` to reflect new dependencies and overrides for `vite` and `vitest`.
- Removed `tsdown.config.ts` and `vitest.config.ts` files from `react-doctor` package.
- Refactored interfaces in `src/types.ts`, `src/core`, and `src/plugin` files for consistency.
- Cleaned up unused imports and code in various utility files.
- Deleted unused `react-doctor-icon.tsx` component from the website package.
* fix: detect namespace hook calls (React.useState, React.useEffect) in all rules
Fixes#117: isHookCall now handles MemberExpression callee types
(e.g. React.useEffect, React.useState) in addition to plain Identifier
callee types. This fixes silent misses in no-derived-state-effect,
no-fetch-in-effect, no-cascading-set-state, no-effect-event-handler,
rerender-functional-setstate, rendering-hydration-no-flicker, and
the countSetStateCalls helper.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: remove unsupported jsx-a11y/no-noninteractive-element-interactions rule
Fixes#113, #119: This rule does not exist in the bundled oxlint
binary, causing 'Failed to parse oxlint configuration file' errors
and score suppression on all scans.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* feat: add --no flag to skip prompts and always run full scan
Fixes#106: Adds -n/--no flag that skips all interactive prompts
(like --yes) but specifically declines the diff-only scan prompt,
ensuring a full project scan runs. Useful for CI agents that cannot
interact with TTY prompts.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* test: add namespace hook detection tests (React.useEffect, React.useState, etc.)
Adds 13 tests verifying that all rules correctly detect namespace-style
hook calls (import * as React; React.useEffect, React.useState, etc.).
Covers: no-derived-state-effect, no-fetch-in-effect, no-cascading-set-state,
no-effect-event-handler, no-derived-useState, rerender-lazy-state-init,
rerender-functional-setstate, rerender-dependencies, rendering-hydration-no-flicker,
no-usememo-simple-expression, prefer-useReducer, plus a regression guard for
direct-import fixtures.
Relates to #117.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* test: add edge case tests for project discovery and catalog resolution
Adds tests for:
- peerDependencies-only with catalog reference resolution
- Catalog reference to nonexistent catalog name (fallback to default)
- Empty pnpm-workspace.yaml handling
- Malformed package.json in workspace subdirectories
- Nonexistent root directory for discoverReactSubprojects
- File entries mixed with directory entries in project root
Relates to #101, #87, #105, #71.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: handle malformed JSON in workspace package.json files gracefully
readPackageJson now catches SyntaxError from malformed JSON and returns
an empty object instead of crashing. This prevents workspace discovery
from aborting when a subdirectory has an invalid package.json.
Previously, a broken package.json in any workspace subdirectory would
crash the entire scan with an unhandled SyntaxError.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* feat: add customRulesOnly config to disable builtin react/jsx-a11y rules
Fixes#109: Teams with existing ESLint setups can now set
customRulesOnly: true in react-doctor.config.json to only run
react-doctor/* custom rules, skipping the bundled react/, jsx-a11y/,
react-perf, and react-compiler rules.
This avoids rule duplication for projects that already lint with
eslint-plugin-react, eslint-plugin-jsx-a11y, etc.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* feat: inherit config from ancestor directories for monorepo support
Fixes#73: loadConfig now walks up the directory tree to find
react-doctor.config.json or package.json reactDoctor key from
ancestor directories. This means a single config at the monorepo
root applies to all workspace packages without needing to duplicate
the config in each package.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* feat: add --annotations flag for GitHub Actions annotation output
Fixes#81: The --annotations flag outputs diagnostics in GitHub
Actions annotation format (::error / ::warning) so they appear
as inline annotations on PR diffs. Example:
::error file=src/App.tsx,line=42,title=react-doctor/no-fetch-in-effect::fetch() inside useEffect
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* refactor: remove Ami integration
Removes all Ami-related code:
- --ami flag, --fix flag, fix subcommand, install-ami subcommand
- Ami detection, installation, deeplinks, and open-URL helpers
- Fix prompt (maybePromptFix) with Ami banners
- Skill installation prompt (maybePromptSkillInstall)
- fetchEstimatedScore and EstimatedScoreResult
- AMI_WEBSITE_URL, AMI_INSTALL_URL, AMI_RELEASES_URL, OPEN_BASE_URL,
ESTIMATE_SCORE_API_URL, ERROR_ESTIMATED_FIX_RATE, WARNING_ESTIMATED_FIX_RATE
- skill-prompt.ts utility
CLI bundle reduced by ~39KB (324KB → 285KB).
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* chore: remove .ami directory and all remaining Ami references
- Delete .ami/ directory (42 skill/config files)
- Delete packages/website/src/app/open/ (Ami redirect page)
- Remove Ami references from README.md, share page, install-skill
route, llms.txt, and install-skill.sh
- Zero Ami references remain in the codebase
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* chore: move .ami/skills to .agents/skills
Relocates all skill files from the deleted .ami/ directory into
.agents/skills/ and removes .agents from .gitignore so they are
tracked in version control.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: recognize custom text components in rn-no-raw-text rule
Fixes#93: The rn-no-raw-text rule now recognizes a much wider set of
text-handling components:
1. Expanded built-in names: Typography, Paragraph, Span, H1-H6
2. Keyword matching instead of suffix-only: any component whose name
contains Text, Title, Label, Heading, Caption, Subtitle, Typography,
Paragraph, Description, or Body is treated as text-handling
(e.g. StyledText, BodyText, CustomLabel, MyTypography)
3. New textComponents config option: users can specify arbitrary
component names in react-doctor.config.json that should be treated
as text-handling:
{
"textComponents": ["MyCustomComp", "NativeWindText"]
}
Diagnostics inside these components are filtered out post-lint.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* chore: upgrade dependencies
- oxlint: 1.47.0 → 1.59.0 (fixes SIGABRT crashes on large projects #84/#77)
- oxfmt: 0.32.0 → 0.44.0
- typescript: 5.9.3 → 6.0.2
- vitest: 4.0.18 → 4.1.4
- tsdown: 0.20.3 → 0.21.7
- knip: 5.83.1 → 6.3.1
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: add file count limit per oxlint batch to prevent OOM crashes
Mitigates #84/#77: Adds OXLINT_MAX_FILES_PER_BATCH (500) to cap the
number of files per oxlint invocation. Combined with the oxlint upgrade
from 1.47 to 1.59, this prevents SIGABRT/OOM crashes on large projects
with 100+ source files.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* feat: add --staged flag for pre-commit hook scanning
Fixes#115: The --staged flag scans only files in the git index
(staged with git add). It materializes staged content to a temp
directory so oxlint reads the exact index version, not the working
tree version. This is critical for pre-commit hooks where the working
tree may differ from what's being committed.
Usage:
npx react-doctor . --staged --fail-on error
For lint-staged:
"*.{tsx,jsx}": "npx react-doctor . --staged --fail-on error"
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* refactor: code review fixes per AGENTS.md rules
- DRY: Extract shared createFileLinesCache in filter-diagnostics.ts
- DRY: Extract resolveFailOnLevel in cli.ts (was duplicated in
staged and normal paths)
- DRY: Reuse exported getCalleeName from helpers.ts instead of
duplicating in state-and-effects.ts and performance.ts
- Magic number: Move maxBuffer 10MB to GIT_SHOW_MAX_BUFFER_BYTES constant
- Variable naming: prevLine → previousLine, tagName → fullTagName,
leafName → leafTagName, configFiles → projectConfigFilenames
- Remove .pop()! non-null assertion in favor of .at(-1) with fallback
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: prevent shell injection in staged file content reading
Replace execSync with string interpolation with spawnSync using an
args array. File paths with shell metacharacters (quotes, semicolons,
etc.) in their names are now passed safely without shell interpretation.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
* fix: pass resolved config to staged scan to preserve ancestor inheritance
The staged scan materializes files to /tmp, where loadConfig's ancestor
traversal would never find a monorepo root's config. Now the CLI resolves
config from the real project directory first and passes it via the new
configOverride ScanOption, so customRulesOnly, share, ignore rules,
and textComponents all apply correctly during --staged scans.
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com>
- Added homepage and bugs fields to package.json for better project visibility.
- Introduced new constants for React Native deprecated modules and legacy Expo package replacements in constants.ts.
- Updated index.ts to include new rules for React Native diagnostics.
Scaffolds the react-doctor CLI with project discovery, oxlint spawning,
and categorized diagnostic output. Bundles 36 rules across 5 plugins
(react, jsx-a11y, react-perf, import, typescript).
Generated with [Ami](https://ami.dev)
Co-Authored-By: Ami <noreply@ami.dev>