Skip to content

feat: make React cleanup first-class - #1624

Open
aidenybai wants to merge 20 commits into
mainfrom
agent/react-cleanup-engine
Open

feat: make React cleanup first-class#1624
aidenybai wants to merge 20 commits into
mainfrom
agent/react-cleanup-engine

Conversation

@aidenybai

@aidenybai aidenybai commented Aug 10, 2026

Copy link
Copy Markdown
Member

Why

React Doctor should make React applications easier to understand, compose, and change. The previous standalone cleanup package split that job across package boundaries, made broad unused-code reports part of the default product, and kept experimental editor surfaces alive beside the CLI and API.

This PR makes React cleanup a native React Doctor capability. React-specific complexity and repeated composition are default signals; whole-project export, file, dependency, and cycle checks remain available only through explicit rule opt-ins.

What changed

  • Move the required project graph analysis into @react-doctor/core; the published CLI and Node API ship their own worker entry instead of consuming a separate cleanup package.
  • Remove the standalone cleanup packages and archive their changelogs.
  • Delete the experimental language server, editor scan API, VS Code extension, Zed extension, LSP command, telemetry, dependencies, builds, CI wiring, and tests.
  • Preserve patch compatibility for the existing deadCode API/config and hidden CLI switches. deadCode: false disables duplicated-JSX analysis only; explicitly enabled graph rules still run.
  • Keep graph rules out of changed/staged scans because they require a complete project graph. Duplicated JSX keeps full-corpus matching while focusing reports to changed paths.
  • In workspaces, duplicate JSX is computed once from the workspace owner. Each child runs graph rules from its own effective config, and ancestor graph analysis excludes nested projects.
  • Keep legacy rule spellings as config aliases only. Diagnostics, metadata, prompts, telemetry, and docs use canonical react-doctor/* identities.
  • Add scan.projectAnalysisRuleCount to the canonical run event so opt-in adoption is measurable without a high-cardinality metric.

Rule inventory

Rule Default Description
react-doctor/no-high-complexity-react-function warning Reports a proven React component or custom hook when cyclomatic or cognitive complexity exceeds 15. Nested callbacks do not inflate the owner.
react-doctor/duplicate-jsx-subtree warning Finds substantial repeated JSX structure across distinct composition roots and links every occurrence for a shared-component decision.
react-doctor/unused-file opt-in Finds source files unreachable from discovered application, package, framework, config, and test entries.
react-doctor/unused-export opt-in Finds value exports with no in-repository consumer after imports and re-exports are traced.
react-doctor/unused-type opt-in Finds type exports with no in-repository consumer.
react-doctor/unused-dependency opt-in Finds production dependencies with no detected use in source, scripts, config, or known framework conventions.
react-doctor/unused-dev-dependency opt-in Finds development dependencies with no detected use in tooling, CI, scripts, source, or config.
react-doctor/circular-dependency opt-in Finds runtime import cycles while excluding type-only and lazy edges.

Category severity changes do not activate opt-in rules. Enable each graph rule directly:

{
  "rules": {
    "react-doctor/unused-file": "warn",
    "react-doctor/unused-export": "warn",
    "react-doctor/unused-type": "warn",
    "react-doctor/unused-dependency": "warn",
    "react-doctor/unused-dev-dependency": "warn",
    "react-doctor/circular-dependency": "warn"
  }
}

The default React cleanup is intended to produce a component boundary, not a mechanical rewrite:

// Before: the same section/header/value composition appears in several cards.
const RevenueCard = () => (
  <section><header><ChartIcon /><h2>Revenue</h2></header><strong>$42k</strong></section>
);

// After: one concept has one component interface.
const MetricCard = ({ title, value }: MetricCardProps) => (
  <section><header><ChartIcon /><h2>{title}</h2></header><strong>{value}</strong></section>
);

Compatibility and rollout

  • Patch changeset for react-doctor and oxlint-plugin-react-doctor.
  • JSON report schema remains v3.
  • The removed editor/LSP packages were private and had no published marketplace or registry artifact to unpublish.
  • No GitHub Action release file changed.
  • Kill or retune a new default diagnostic if confirmed false positives, suppression rates, or rejected refactor recommendations remain high across two patch releases.

Validation

  • Full monorepo test suite: all 12 tasks green.
  • Core: 2,075 tests passing.
  • Public API: 24 tests passing, including an opt-in unused-export scan with deadCode: false.
  • Focused CLI/TUI/cache workspace coverage: 64 tests passing.
  • Oxlint plugin: 26,331 tests passing, including complexity semantics, rule behavior, and liveness.
  • nr typecheck, nr lint, nr format:check, and nr build.
  • nr smoke:json-report and nr check:published-deps.
  • Packed-install smoke: 180 diagnostics, opt-in project analysis exercised, zero forbidden removed packages.
  • Final CI is green on Linux (Node 20, 22, 24, 25, and 26), Windows, and macOS, plus CodeQL, package previews, the interactive PTY recording, and React Doctor at 100/100.
  • Independent blocker review: no remaining blockers in rule selection, workspace ownership, API wiring, or descendant filtering.
  • Most recent complete paired Daytona parity at c54185e3675a0505a94eb203fe4296823fd3f40c: 2,000/2,000 projects compared, zero failures or skipped projects, 46,421 added diagnostics, and zero removed. The exact final-head rerun at 52eb7699e257d9f1fa38adfae54ab7f869ec712c is blocked before scanning by the account’s five-custom-snapshot quota; no unrelated benchmark-harness snapshot was deleted to bypass it.
  • Paired rule totals at c54185e: 19,489 unused-file, 9,861 unused-type, 7,625 unused-export, 4,454 unused-dependency, 2,639 complexity, 1,704 unused-dev-dependency, and 649 runtime cycles.
  • Every cycle received edge-level review: 647 were source-confirmed runtime cycles, zero were false positives, and two had unavailable pinned sources. A 277-finding complexity risk sample produced 274 source-confirmed findings, zero false positives, and three unavailable sources.
  • The original 60,470-diagnostic population was not clean. At least 2,630 false positives were confirmed without extrapolation: 2,180 composition-registry files, 282 convention/config/peer dependency uses, 90 GraphQL Codegen documents, 58 Remix custom-directory findings, 13 optional-chain complexity findings, 6 type-only cycles, and 1 schema loader.
  • Deep cohort revalidation after the analyzer refinements removed 593 confirmed Relax false positives (23 source-confirmed findings retained), 334 Melt UI false positives (3 retained), 275 Base UI false positives (9 retained), and all confirmed Taro unused-file false positives. Treact unused-type findings fell from 855 to 49 after 806 same-file type-consumer false positives were removed.
  • Exact secondary cohorts cleared 35 Base UI namespace re-export false positives, 11 fireyy build-script package false positives, 9 Open Scouts Supabase function false positives, 2 UI-X style-registry false positives, and 1 Parcel Sass false positive.
  • Cross-platform regression coverage now exercises macOS symlink aliases and Windows path/config/glob semantics; the full core suite passes 151 files and 2,075 tests.
  • Post-parity source validation removed the remaining proven Vite-root, import-glob, MDX-demo, Taro-page, build-generator, Sass-host, shell-script, GraphQL-output, and registry-preview false positives. The final targeted corpus has zero analysis errors.
  • The adversarial second pass also corrected overclaims in the audit itself. Every retained finding was traced to exact source/config evidence: UI-X 363/363, TeXlyre 5/5, Relax 23/23, Taro 5/5, and four BillingSDK banner demos are genuine orphaned, deprecated, superseded, or unregistered files. They remain intentionally reported.
  • Canonical GraphQL Codegen signatures now exclude 2,045 reproduced Saleor legacy-SDK generated export findings while authored Apollo-policy near misses remain eligible. A live MDX registry preview clears one BillingSDK source only when its published artifact has matching registry metadata and a structurally equivalent embedded source; stale, fenced, and commented artifacts remain unused.
  • All 671 remaining Saleor export findings were partitioned and reviewed: 604 are removable export modifiers on Codegen-consumed GraphQL declarations, 46 are ordinary fixture exports, two are Playwright data exports, and 19 are individually verified authored exports. None had an external import or framework-required export contract; zero were false positives.
  • Complexity correction cleared 930 function locations and lowered scores for 1,462 retained locations; it introduced zero new function locations. Two alternate cycles surfaced and were verified as real runtime import cycles at the pinned source revision.
  • YAML Codegen discovery cleared 46 Taskcafe files explicitly listed by its pinned frontend/codegen.yml; 33 unused exports remain independently eligible for the opt-in export rule.
  • Generated-code provenance covered 10,008 original findings. These were kept separate from semantic reachability false positives because remediation belongs at the generator or generated-output policy boundary.
  • The evaluator explicitly enables every registry rule, including all six opt-in graph rules. In the intentional all-rules paired sequential workload at 3ccaac6, scan time increased from 7,422,866 ms to 10,108,347 ms (+36.2%, 180 project regressions). This is not the default CLI contract: graph rules do no work unless individually configured.

Note

High Risk
Large architectural rewrite of maintainability/dead-code analysis with new default diagnostics and removal of packages and editor surfaces. Compatibility shims reduce breakage, but analyzer behavior and default findings change substantially.

Overview
Makes React cleanup a native React Doctor capability. Default scans now surface React complexity and repeated JSX composition; whole-project unused file/export/type/dependency and import-cycle checks become explicit opt-in react-doctor/* rules.

Replaces the Deslop-backed DeadCode path with an in-core Maintainability service and project-graph analyzer (checkProjectAnalysis / analyzeProject), shipped via CLI and API worker entries. Removes the standalone deslop-js/deslop-cli packages, experimental language server, editor scan API, and IDE extensions.

Preserves deadCode API/config compatibility: deadCode: false only disables duplicate-JSX analysis; explicitly enabled graph rules still run. Graph rules stay out of changed/staged scans; duplicate JSX still matches the full corpus but focuses reports to changed paths.

Reviewed by Cursor Bugbot for commit 52eb769. Bugbot is set up for automated code reviews on this repo. Configure here.

@pkg-pr-new

pkg-pr-new Bot commented Aug 10, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/eslint-plugin-react-doctor@1624
npm i https://pkg.pr.new/oxlint-plugin-react-doctor@1624
npm i https://pkg.pr.new/react-doctor@1624

commit: 52eb769

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

React Doctor found no new issues. 🎉

Reviewed by React Doctor for commit 52eb769.

@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Interactive terminal E2E

Terminal Control verified the built CLI at 52eb769 in a real PTY:

  • selected a project interactively and observed Scanning... before the three-second Git delay completed
  • waited for the clean result and exercised the compact report
  • opened copy context and the GitHub Actions confirmation, then cancelled safely

Download the edited MP4 and PNG evidence

@aidenybai
aidenybai force-pushed the agent/react-cleanup-engine branch from 1a7f6ca to 792321a Compare August 10, 2026 12:12
@aidenybai
aidenybai marked this pull request as ready for review August 11, 2026 00:31
Comment thread packages/core/src/react-cleanup/detect-duplicate-jsx-subtrees.ts
Comment thread packages/core/src/react-cleanup/detect-duplicate-jsx-subtrees.ts
Comment thread packages/core/src/project-analysis/collect/package-json-entries.ts
Comment thread packages/core/src/project-analysis/utils/is-project-analysis-excluded-path.ts Outdated
Comment thread packages/core/src/project-analysis/analyze-project.ts
Comment thread packages/core/src/project-analysis/collect/entries.ts Outdated

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7ccdd49. Configure here.

Comment thread packages/core/src/project-analysis/report/exports.ts
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants