Conversation
…nd factor those rules into the score (#143) When a project has a JSON-format oxlint or eslint config (`.oxlintrc.json` or `.eslintrc.json`) at the scanned directory or any ancestor up to the nearest project boundary (`.git` directory or monorepo root), react-doctor now folds it into the same scan via oxlint's `extends` field. The user's existing rules fire alongside the curated react-doctor rule set and count toward the 0–100 score — no separate oxlint / eslint invocation needed. Default-on; opt out via `"adoptExistingLintConfig": false`. If oxlint can't load the user's config (broken JSON, missing plugin, unknown rule), react-doctor logs the reason on stderr and retries once without `extends` so the scan still produces a useful score off the curated rule set instead of failing the entire lint pass. Also broadens the diagnostic file-extension filter from `.tsx`/`.jsx` to the full source-file pattern so adopted rules and react-doctor's own JS-performance rules now report on plain `.ts`/`.js` files.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…xlint-config Marks `RuleSeverity`, `NEXTJS_RULES`, `REACT_NATIVE_RULES`, `TANSTACK_START_RULES`, `TANSTACK_QUERY_RULES`, and `GLOBAL_REACT_DOCTOR_RULES` as `export` so downstream consumers (the website's rule-list page, tooling that introspects react-doctor's curated surface) can read the canonical rule sets without re-declaring them. No behavior change for the lint pipeline.
Tracks the 1.62.0 → 1.63.0 oxlint release so we pick up upstream rule fixes and `extends` improvements. Adds a workspace-level `pnpm.overrides` entry pinning `oxlint-tsgolint` to `^0.22.1` to keep its peer-dep range from drifting on lockfile regenerations.
…g plugin (#143) Adds `react-doctor/eslint-plugin` so users on a flat-config ESLint setup can register the same curated rules they'd get from a CLI scan. Wraps each rule from the existing oxlint plugin in an ESLint `{ meta, create }` shape and ships ready-made flat configs: import reactDoctor from "react-doctor/eslint-plugin"; export default [reactDoctor.configs.recommended]; Available configs: - `recommended` — `GLOBAL_REACT_DOCTOR_RULES` - `next` — Next.js rules - `react-native` — React Native rules - `tanstack-start` / `tanstack-query` - `all` — every rule above at its recommended severity Pairs with the same-PR adopt-existing-lint-config feature: that one folds the user's eslint config into a react-doctor scan; this one folds react-doctor's rules into the user's eslint run.
…nt usage
Adds the `./eslint-plugin` subpath to `package.json` exports and a
matching build entry in `vite.config.ts` so the plugin emits as
`dist/eslint-plugin.{js,d.ts}` alongside the existing oxlint plugin.
The `VERSION` env var is threaded into the build env so
`eslintPlugin.meta.version` resolves to the package version at compile
time instead of the `"0.0.0"` fallback.
README split: the existing "Use the oxlint plugin standalone" section
becomes a more general "Use the lint plugin standalone" with two
subsections (oxlint, ESLint flat config), example presets, and a
cherry-pick example for users who only want a few rules.
Documents the new ESLint flat-config plugin alongside the existing adopt-existing-lint-config changeset so the changelog tells both sides of the issue-#143 story: - adopt-existing-lint-config — fold the user's eslint config INTO a react-doctor scan - eslint-plugin — fold react-doctor's rules INTO the user's eslint run Both ship together under a single minor bump.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d50c93c. Configure here.
…r-browser package Removes the entire browser surface that landed in #148: - `react-doctor/browser` and `react-doctor/worker` subpath exports (in-browser diagnostics merge / filter / score pipeline) - `react-doctor browser …` CLI subcommand (`start` / `stop` / `status` / `snapshot` / `screenshot` / `playwright`) - `react-doctor-browser` workspace package (Playwright + CDP + system Chrome launcher + cross-browser cookie extraction) The website never imported them and no other internal package consumed them, so this trims ~9.2k LOC and drops `playwright` + `libsql` and their transitive deps from the install graph. The full removed source is preserved on the `archive/browser` branch for future revival or vendoring. Collateral changes: - Collapse `utils/calculate-score-node.ts` into `utils/calculate-score.ts` now that there is no `-browser` counterpart to disambiguate from. The Node-only `proxyFetch` flow is unchanged. - Drop the constants the browser CLI owned (`CDP_CONNECT_TIMEOUT_MS`, `SNAPSHOT_TIMEOUT_DEFAULT_MS`, viewport defaults, SIGTERM grace, session paths) from `constants.ts`. - Generalize the EPIPE handler comment in `cli.ts` away from the `react-doctor browser snapshot | head` example. - Drop the "Browser API" section from the README. Documentation: - Replace the deleted `.changeset/browser-cli-and-design-rules.md` (which doubled up the browser changelog with 11 still-shipping lint rules) with two focused changesets: - `remove-browser-entrypoints-and-cli.md` — the breaking change above - `new-state-correctness-and-design-rules.md` — preserves the previously-bundled changelog for the 3 state/correctness rules and 8 design-system rules in `react-ui.ts` Tests + tooling: - Add `tests/calculate-score.test.ts` covering `tryScoreFromApi` (network failure, missing fetch, non-2xx, success with `filePath` stripping, malformed payload) and the `calculateScore` orchestration with a stubbed global `fetch`. Restores the API-failure-fallback coverage that was previously only in the deleted `browser.test.ts`. - Drop `tests/fixtures/**` from workspace lint and add `tests/fixtures/.oxlintignore` so direct `vp lint --fix` / `oxlint --fix` invocations against fixture paths can't silently strip the `debugger;` statements and empty blocks the `adoptExistingLintConfig` tests assert on. - Drop dead code uncovered while making the lint pass cleanly: unused `isMemberProperty` import in `nextjs.ts`, unused `fileContainsPattern` helper in `discover-project.ts`, unused locals in two test files.
…lobPattern wrapper The `diagnoseCore` engine in `src/core/` was designed so the deleted browser adapters could share orchestration with the Node `diagnose()` API. With the browser surface gone, the only caller is `src/index.ts::diagnose`, so the dependency-injection shape (`loadUserConfig`, `discoverProjectInfo`, `calculateDiagnosticsScore`, `getExtraDiagnostics`, `createRunners`) is pure indirection. - Inline `diagnoseCore` and `buildDiagnoseTimedResult` directly into `index.ts::diagnose`. Lint / dead-code parallelism, error-isolating `Promise.allSettled`, and reduced-motion environment checks all preserved verbatim. - Move `core/calculate-score-locally.ts` and `core/try-score-from-api.ts` to `utils/` (they were never browser-shared — `proxyFetch` is Node-only) and drop the now-empty `src/core/` directory. - Remove the unused `matchGlobPattern` wrapper from `utils/match-glob-pattern.ts` (production code uses `compileGlobPattern` directly via `is-ignored-file.ts`); rewrite `tests/match-glob-pattern.test.ts` to exercise `compileGlobPattern` with a local helper. Net –116 LOC. `scan.ts` was already independent of `diagnoseCore` and is unchanged. All 443 tests pass; built CLI smoke unaffected.
…int-config # Conflicts: # packages/react-doctor/src/commands/browser/playwright.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Closes #143 from both sides.
Issue #143 asked for react-doctor to integrate with existing ESLint setups so diagnostics flow through "classic hooks / analysis." This PR addresses that two ways:
adoptExistingLintConfig(default-on). When a project has a JSON-format oxlint or eslint config (.oxlintrc.jsonor.eslintrc.json) at the scanned directory or any ancestor up to the nearest project boundary, react-doctor merges it into the same scan via oxlint'sextendsfield. Diagnostics from those rules count toward the 0–100 score.react-doctor/eslint-pluginexport. Drop into a flat config, get the same curated rule set withrecommended/next/react-native/tanstack-start/tanstack-query/allpresets, and diagnostics surface inline through whichever ESLint integration the user already runs.Behavior change on upgrade
Projects with an existing
.oxlintrc.json/.eslintrc.jsonwill see new diagnostics flow into the score on first run; the score may drop. Set"adoptExistingLintConfig": falseto preserve the previous behavior.customRulesOnly: truealso implies opt-out.The PR also broadens the diagnostic file-extension filter from
.tsx/.jsxto the full source-file pattern so adopted rules and react-doctor's own JS-performance rules now report on plain.ts/.jsfiles.Resilience
If oxlint can't load the user's config (broken JSON, missing plugin, unknown rule name), react-doctor logs the reason on stderr (
[react-doctor] could not adopt existing lint config (...); retrying without extends. Set "adoptExistingLintConfig": false to silence.) and retries the scan once withoutextendsso a real score is still produced off the curated rule set.Limitations (documented)
extendscannot evaluate JS or TS, so flat configs (eslint.config.js),.eslintrc.{js,cjs}, andoxlint.config.tsare silently skipped."rules": {...}) flow through. Category-level enables ("categories": {...}) do not — react-doctor's local categories block always wins."plugins": ["unicorn"]+"rules": { "unicorn/no-array-for-each": "error" }works..gitdirectory or monorepo root), never to the user's home directory.Implementation highlights
Adopt-existing-lint-config
detect-user-lint-config.tsthat walks up to a project boundary and returns the first matching config (.oxlintrc.jsonpreferred over.eslintrc.json). Filename list lives inconstants.ts(ADOPTABLE_LINT_CONFIG_FILENAMES) next toKNIP_CONFIG_LOCATIONS.createOxlintConfigacceptsextendsPaths; emitsextendsonly when non-empty.runOxlintwrites the config via a smallwriteOxlintConfighelper (allows in-place rewrite for retry), runs the spawn loop throughspawnLintBatches. On failure with non-empty extends, we rewrite without extends, log to stderr, and retry once.adoptExistingLintConfig?: booleanfield onReactDoctorConfig(defaulttrue), wired throughvalidate-config-types.ts,scan.ts, andindex.ts.PLUGIN_CATEGORY_MAPgains category defaults foreslint,oxc,typescript,unicorn,import,promise,n,node,vitest,jest,nextjsso adopted-rule diagnostics group meaningfully.JSX_FILE_PATTERN→SOURCE_FILE_PATTERNso.ts/.jsfiles aren't dropped post-parse.ESLint plugin
src/eslint-plugin.tsreuses every rule from the existing oxlint plugin (each rule'screate(context) => visitorssignature is already ESLint-compatible), wraps each in the ESLint v9-required{ meta, create }shape, and ships flat configs reusing the exact severity maps the CLI emits to oxlint so the engines stay in lock-step.RuleSeverity,NEXTJS_RULES,REACT_NATIVE_RULES,TANSTACK_START_RULES,TANSTACK_QUERY_RULES, andGLOBAL_REACT_DOCTOR_RULESare nowexported fromoxlint-config.tsso the eslint-plugin module can read the canonical rule sets without re-declaring them../eslint-pluginentry inpackage.jsonexports + a matchingvite.config.tsbuild entry;dist/eslint-plugin.{js,d.ts}ships alongsidereact-doctor-plugin.{js,d.ts}.Commits
87e5cc6— adopt-existing-lint-config (core feature, retry-on-failure, detection walk-up, fixtures, tests)605cc5e—chore: export rule severity / category constants from oxlint-configea988a3—chore: bump oxlint to ^1.63.0 and pin oxlint-tsgolint via override2b59680— eslint-plugin source6acdddc—build: wirereact-doctor/eslint-pluginentry + README docs8eed892— eslint-plugin changesetTest plan
pnpm typecheckcleanpnpm lint0 errors (333 pre-existing warnings unchanged)pnpm test— 461 tests pass (was 456); new tests cover:detect-user-lint-config.test.ts— empty dir, single oxlint/eslint config, first-match priority, dotless / JS variants ignored, walk-up to root, project-boundary stop.run-oxlint.test.ts—adoptExistingLintConfig: default-on merges user rules,.tsfiles reported (not just.tsx), opt-out skips,customRulesOnlyskips, broken-config fallback emits stderr warning and still resolves.validate-config-types.test.ts— passthrough + stringy coercion for the new boolean.pnpm --filter react-doctor buildproducesdist/eslint-plugin.{js,d.ts}(~257 kB / ~55 kB gzipped).react-doctor packages/react-doctor/tests/fixtures/user-oxlint-configreports(2)debuggerviolations acrossapp.tsx+util.ts(was(1),.tsxonly) and folds them into the score (97/100, vs 99/100 withadoptExistingLintConfig: false).react-doctor packages/react-doctor/tests/fixtures/user-oxlint-config-broken(intentionally references unknown plugin) emits the retry warning on stderr and produces a real score (100/100 on the clean fixture) instead of failing the lint pass.Note
Medium Risk
Medium risk because it changes how lint diagnostics are sourced and scored (now optionally extends user configs and includes
.ts/.js), and it removes previously published browser-related entrypoints/packages which may break downstream consumers.Overview
React Doctor now (by default) adopts an existing JSON
.oxlintrc.json/.eslintrc.jsonfound at or above the scan root (up to a project boundary) by wiring it into oxlint viaextends, so user rules run alongside the curated rule set and count toward the health score; failures to load the adopted config are handled by logging and retrying once withoutextends.It also adds a first-class ESLint v9 flat-config plugin export at
react-doctor/eslint-plugin, exposingrecommended/framework presets andallconfigs that mirror the CLI’s severity maps.Separately, the PR drops the browser surface area (the
react-doctor-browser/headless-browserworkspace package and related CLI/exports) and adds a pnpm override foroxlint-tsgolint.Reviewed by Cursor Bugbot for commit 51e8448. Bugbot is set up for automated code reviews on this repo. Configure here.