mirror of
https://github.com/millionco/react-doctor.git
synced 2026-09-14 20:00:24 +08:00
codex/fix-latest-issues
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
aa61efae28 |
refactor: collapse @react-doctor/types and @react-doctor/project-info into core (#440)
* refactor: collapse @react-doctor/types and @react-doctor/project-info into core Eliminates two private workspace packages whose only role was to namespace shared TS types and project-discovery helpers — neither was a real consumer boundary, and the indirection cost a `workspace:*` edge per leaf package without saving any code. **Layout** - `packages/types/` → `packages/core/src/types/` (10 files, pure TS types) - `packages/project-info/` → `packages/core/src/project-info/` (27 files, discoverProject + framework detection + monorepo helpers) - `packages/types/src/react-native-dependency-names.ts` → `packages/oxlint-plugin-react-doctor/src/react-native-dependency-names.ts` (the canonical RN-aware-package list lives next to its heaviest consumer — the rule gate — and is re-exported from core to avoid the rule-plugin ↔ core dependency cycle the previous types package was specifically designed to break). The two packages are deleted from the workspace; every consumer (`react-doctor`, `api`, `oxlint-plugin-react-doctor`, `actions/review`, +130 source/test files) now imports from `@react-doctor/core` directly. **Errors cleanup** Dropped the legacy plain-class `ReactDoctorError` and `isReactDoctorError` from `project-info`. The four narrow errors (`ProjectNotFoundError`, `NoReactDependencyError`, `PackageJsonNotFoundError`, `AmbiguousProjectError`) now extend `Error` directly — the only valid hierarchy is the tagged `Schema.TaggedErrorClass` `ReactDoctorError` from `core/errors.ts`, which has been the canonical one since the Effect v4 refactor. Added `isProjectDiscoveryError` as a narrow type guard for the four leaves. The discovery errors are thrown synchronously BEFORE the Effect runtime takes over (at `diagnose()` / CLI entry points); inside the runtime, the Project service's `translateProjectInfoError` converts each one to its tagged-reason equivalent. **Schema vs type name collisions** `schemas.ts` exports `Diagnostic` / `JsonReport` / `JsonReportMode` / … as Schema.Class values, while `types/` exports the same names as TS types. The core barrel re-exports the TS-type versions (broad-strokes API) and exposes the Schema classes via a new `@react-doctor/core/schemas` subpath export for the two callers that need them (`Reporter` service, smoke validator, schema tests). **Validation** - `pnpm typecheck` ✓ - `pnpm lint` ✓ - `pnpm format` ✓ - `pnpm test` ✓ (1449 passing, 3 skipped — no regressions) - `pnpm smoke:json-report` ✓ Co-authored-by: Cursor <cursoragent@cursor.com> * fix(core): keep RN-dependency constants out of types barrel to avoid pulling oxlint plugin into discovery Bugbot caught (medium severity): re-exporting `isReactNativeDependencyName` from `oxlint-plugin-react-doctor` via the core types barrel meant every consumer of `@react-doctor/core` (including `discoverProject` / `discoverReactSubprojects`) would load the entire 286-rule plugin at module-init. Adds a tiny standalone leaf at `core/src/project-info/internal-rn-dependency-names.ts` (zero imports, just the constants the workspace-discovery gate needs) and switches `is-package-json-react-native-aware.ts` to use it. The types barrel no longer re-exports the plugin-side constants — rule consumers import them directly from `oxlint-plugin-react-doctor`. The duplication is intentional and documented at both files; the two lists are short enough that a future regression test in the plugin can observe the union. Validated: typecheck + test (1449 pass) + lint + format all green. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
bfaf9c9530 | fix: avoid React 19 rule false positives on React 18 (#254) | ||
|
|
d23137e177 | test: add fixed issue regression coverage (#258) | ||
|
|
f0198e2f2d | refactor: extract @react-doctor/{types,project-info,core} internal packages + cleanup (#249) | ||
|
|
4d622970b5 |
Revert "pub"
This reverts commit
|
||
|
|
4d9d1a5fcf | pub | ||
|
|
7169551251 |
refactor: tier 1 cleanup — drop dead fields, flatten core/, inline single-use utils (#244)
* refactor: tier 1 cleanup — drop dead fields, flatten core/, inline single-use utils Five structural simplifications in one PR, all zero-behavior-change (verified: byte-identical 262-line diagnostic snapshot vs `main`). ## 1. Drop `examples` field on Rule The `examples: RuleExample[]` slot was authored across all 178 rules in PR #234 but nothing ever consumed it. Pure storage / maintenance burden. Removed the field from the interface, deleted `RuleExample` type, and stripped the field from every rule via codemod. ## 2. Derive `framework` from bucket directory (no per-rule field) The `framework` field was always 1:1 with the bucket directory: - `nextjs/*.ts` → `framework: "nextjs"` - `react-native/*.ts` → `framework: "react-native"` - `tanstack-start/*.ts` → `framework: "tanstack-start"` - `tanstack-query/*.ts` → `framework: "tanstack-query"` - everything else → `framework: "global"` Pure redundancy. Updated the codegen (`scripts/generate-rule-registry.mjs`) to compute it from the bucket directory and inject it into the registry entry. Rule files no longer carry the field at all. 178 lines removed across rule files; codegen gained the bucket→framework table. ## 3. Default `category` from bucket, preserve overrides Same shape as #2 but with overrides — most rules in a bucket share a category, but a few (e.g. `tanstack-start-no-secrets-in-loader`, `nextjs-no-side-effect-in-get-handler`) override to `"Security"`. Codegen now provides a bucket→default-category table, and rules only ship the `category` field when overriding. Codemod stripped ~170 of the 178 `category:` declarations (left 4 overrides intact, verified by diagnostic snapshot). ## 4. Inline single-use plugin utils Helpers in `plugin/utils/` and `plugin/rules/<bucket>/utils/` with only one importer got inlined back into their consumer. Pure file-count reduction — utilities shared by 2+ rules stay. - `plugin/utils/`: ~50 → 40 files - `plugin/rules/*/utils/`: ~30 → 27 files ## 5. Flatten `core/` subdirectories The 5 subdirectories (`core/config/`, `core/detection/`, `core/diagnostics/`, `core/runners/`, `core/scoring/`) collapsed into a flat `core/` directory. Subdirs weren't pulling weight — consumers always imported by specific file name, never as a group. Now everything's one level deep. (Exception: `core/runners/oxlint/` stays nested — those 6 files form a cohesive oxlint-config subcluster.) 200+ import statements updated across the codebase. `git mv` used so history is preserved. ## Verification - `pnpm typecheck` — 0 errors - `pnpm test` — 80 test files / 734 / 734 pass - `pnpm lint` — 0 warnings, 0 errors - `pnpm format` — clean - Diagnostic-snapshot diff vs `origin/main` — byte-identical 262 lines ## Stats - 330 files changed, **+650 / -6312 net** (5662 line reduction) - 18 files deleted (dead utils + the now-pointless RuleExample type) - 12 renamed (the core/ flatten) - 54 renamed-with-modifications (core/ flatten + import updates) After this PR, the largest source-of-truth files are the 178 rule files themselves; everything else is supporting infrastructure that's now smaller and flatter. * fix(codegen): emit multi-line registry entries so prettier doesn't re-wrap them CI's `format:check` reformatted long single-line registry entries to multi-line, which would have caused the codegen-overwrite-on-build to loop forever. Pre-emit the multi-line shape so the file is byte-stable across `pnpm gen → pnpm format → pnpm gen`. Verified: MD5 unchanged across gen → format → gen cycle. |
||
|
|
8bb54c9823 |
refactor: split src/utils/ into src/core/{config,detection,diagnostics,scoring,runners}/ + src/cli/ (#224)
* refactor: split src/utils/ (68 flat files) into src/core/{config,detection,diagnostics,scoring,runners}/ + src/cli/
Final big organizational refactor in the series (#218 plugin/, #220 cli/,
#221 scan/, #222 drop browser-poc, #223 dead-code). Carves the 68-file
src/utils/ dumping ground into purpose-built directories.
New layout:
src/cli/ presentation (26 files)
+ prompts.ts, highlighter.ts, spinner.ts, logger.ts
+ colorize-by-score.ts, format-error-chain.ts
+ indent-multiline-text.ts, wrap-indented-text.ts
+ build-hidden-diagnostics-summary.ts
+ should-auto-select-current-choice.ts, should-select-all-choices.ts
+ detect-agents.ts, to-display-name.ts
+ (existing cli/ files: index.ts, render-*.ts, etc.)
src/core/ core logic (7 files at root + 5 subdirs)
build-json-report.ts shared JSON reporting (CLI + public API)
build-json-report-error.ts shared JSON error envelope
group-by.ts, is-file.ts generic primitives
is-plain-object.ts, to-relative-path.ts
read-file-lines-node.ts
config/ (11) config loading, root-dir resolution, ignore patterns,
glob matching, gitattributes parsing, validation
detection/ (6) project discovery, react/tailwind major-version parsing,
package.json reading, monorepo root detection
diagnostics/ (12) combine/filter/suppress/merge, jsx-opener-span
lookup, disable-directive walks
scoring/ (6) local + remote score calc, score breakdown,
reduced-motion check, proxy fetch
runners/ (13) oxlint + knip + their config builders, include-path
computation, Node-version compat, diff-files
src/plugin/ (unchanged from #218)
scan.ts, index.ts, errors.ts, types.ts, constants.ts,
oxlint-config.ts, eslint-plugin.ts, install-skill.ts top-level
Driven by a one-shot codemod that performed the moves via `git mv` (so
all 68 file renames preserve blame/log history) and rewrote every
relative import in the package + tests (98 importer files touched) to
the new paths.
One non-mechanical fix needed after the move:
src/core/runners/run-oxlint.ts resolved its built plugin via
`../../dist/react-doctor-plugin.js` -- with 1 extra level of nesting
that needs to be `../../../dist/react-doctor-plugin.js`.
One test fixed:
tests/regressions/scan-resilience.test.ts read a source file by
hardcoded path (`src/utils/calculate-score-locally.ts`) to assert no
`fetch(` reference -- updated to the new location
(`src/core/scoring/calculate-score-locally.ts`).
Verification:
- 128 files changed, +237 / -222 (almost entirely import-path edits)
- typecheck/lint/format clean
- 734/734 tests pass
- build produces identical dist/cli.js, dist/index.js, etc.
- No public API change
- src/utils/ no longer exists
* refactor: move orchestrators into tiers + rename scan -> inspect (#225)
* refactor: move install-skill, scan, oxlint-config out of src/ root into their tiers
Top-level src/ was still holding three orchestrator-grade files that
logically belong inside the existing tier directories. Move them so the
src/ root only contains public-API entry points and cross-cutting types.
Moves (via git mv, history preserved):
src/install-skill.ts -> src/cli/install-skill.ts CLI subcommand
(already imported
6 things from cli/)
src/scan.ts -> src/core/scan.ts Orchestrator
(lives with the
logic it calls)
src/oxlint-config.ts -> src/core/runners/oxlint-config.ts Sibling of
run-oxlint.ts
(config + runner)
After: top-level src/ contains only the entry points and shared types:
src/
├── cli/ presentation
├── core/ logic
├── plugin/ rules
├── constants.ts
├── errors.ts
├── eslint-plugin.ts public-API entry
├── index.ts public-API entry
├── knip.d.ts type declaration
└── types.ts
Imports rewritten via a small codemod (11 files):
- 3 moved files' internal imports recomputed for new depth
- 4 src/ consumers of the moved files updated
- 4 test files updated
Verification:
- 11 files changed, +39/-39 (all import path edits)
- typecheck/lint/format clean
- 734/734 tests pass
- build produces identical dist/* output
- run-oxlint.ts's relative path to dist/react-doctor-plugin.js
(../../../) is unchanged -- run-oxlint.ts itself didn't move
Stacks on top of #224 (utils->core reorg). Once #224 merges, this PR's
base auto-rebases to main.
* refactor: rename scan -> inspect (function, file, types, test)
"scan" was a misnomer -- the function does much more than scan files: it
runs lint via oxlint, runs dead-code via knip, calculates a score,
merges/filters diagnostics, and renders CLI output. "inspect" describes
the role accurately and matches v2's chosen name.
Renames:
src/core/scan.ts -> src/core/inspect.ts (git mv)
tests/scan.test.ts -> tests/inspect.test.ts (git mv)
scan() function -> inspect() function
runScan() -> runInspect()
mergeScanOptions() -> mergeInspectOptions()
ResolvedScanOptions -> ResolvedInspectOptions
ScanResult -> InspectResult (src/types.ts)
ScanOptions -> InspectOptions (src/types.ts)
describe("scan", ...) -> describe("inspect", ...) (test)
Importers updated:
src/cli/index.ts 4 sites
src/core/build-json-report.ts 3 sites
tests/inspect.test.ts 5 sites
tests/regressions/cli-and-output.test.ts 4 sites
tests/build-json-report.test.ts 2 sites
Kept as English prose (not identifiers):
- "full scan", "scan output", "scan only files", "scan banner" in
comments, CLI option descriptions, and prompt text -- these describe
the action of scanning the codebase, not references to the function.
- constants.ts JSX_OPENER_SCAN_MAX_LINES (about lexical JSX scanning,
unrelated to inspect()).
Verification:
- 7 files changed, +54/-54 (all renames)
- typecheck/lint/format clean
- 734/734 tests pass
- No public API change (scan/ScanResult/ScanOptions weren't exported
from src/index.ts -- the public API still calls the underlying
function diagnose() which is unchanged)
Stacks on #225 (which moved scan.ts to src/core/). Once #225 merges,
this PR's base auto-rebases to main.
* fix: restore help-text example paths corrupted by the import codemod
The utils->core import-path codemod's regex matched `from '...'`
substrings inside string literals -- specifically the EXAMPLE code
shown to users in oxlint diagnostic help text. Three messages in
HELP_TEXT_MAP had their example paths mangled into the codemod's
target form (`../../utils/...`) even though they were supposed to
illustrate paths in the USER'S code, not paths internal to this
package.
Restored to the original example paths:
no-barrel-import:
`import { Button } from '../../utils/components/Button.js'`
-> `import { Button } from './components/Button'`
no-dynamic-import-path:
`import('../../utils/feature/heavy.js')`
-> `import('./feature/heavy.js')`
nextjs-no-css-link:
`import styles from '../../utils/Button.module.css.js'`
-> `import styles from './Button.module.css'`
Verified no other string literals were corrupted (grepped for
`["'`]../../(utils|core|cli)/` across src/ -- all remaining hits are
legitimate plugin/utils imports from the per-rule split in #218, which
is its own unrelated `utils/` directory).
Closes the Bugbot finding on PR #224.
* refactor: move format-error-chain / logger / highlighter to core/ (fix layering)
Addresses Bugbot's "Core modules depend backwards on CLI layer" finding
on PR #224. Several core/ files were importing from cli/, undermining
the layered structure the refactor was introducing:
core/build-json-report-error.ts -> cli/format-error-chain
core/runners/extract-failed-plugin-name -> cli/format-error-chain
core/config/load-config -> cli/logger
core/config/read-ignore-file -> cli/logger
core/config/resolve-config-root-dir -> cli/logger
The 3 utilities in question are all pure abstractions (no CLI-specific
behavior beyond using console.* for output), so they belong in core/
where both core AND cli consumers can import them without inversion.
Moves (git mv, history preserved):
cli/format-error-chain.ts -> core/format-error-chain.ts (Error.cause walker)
cli/logger.ts -> core/logger.ts (silent-mode logger)
cli/highlighter.ts -> core/highlighter.ts (picocolors wrapper)
22 files changed, +29/-29 -- all relative-import path rewrites driven
by a small codemod with a strict `^import ... from "..."` regex
(line-anchored to avoid the help-text-literal corruption bug from the
previous codemod, which triggered the bigger Bugbot follow-up).
After this:
- core/ no longer imports from cli/ for utilities -- only
core/inspect.ts still does (legitimately: it's the CLI orchestrator
that calls render-*.ts + resolve-oxlint-node prompt + spinner)
- cli/ files import the moved utilities via `../core/<name>.js`
Verification:
- typecheck/lint/format clean
- 734/734 tests pass
- `rg "from .+cli/" src/core` shows only inspect.ts's legitimate
imports of render-*, spinner, resolve-oxlint-node (all of which
are CLI presentation/interaction, not utilities)
|
||
|
|
8f100985fe |
feat(react-doctor): 4 new React 18→19 migration rules (#152)
Co-authored-by: Aiden Bai <aidenybai@users.noreply.github.com> |