react-doctor@0.7.0
Patch Changes
-
#1060
ced746fThanks @rayhanadev! - The whole cache stack now survives CI's fresh checkouts, so the GitHub Action's persistedREACT_DOCTOR_CACHE_DIRactually warms every layer instead of only the content-addressed ones. The whole-repo scan-result cache moves under the shared per-project cache root (honoringREACT_DOCTOR_CACHE_DIRlike the lint, sidecar, dead-code, and supply-chain caches — previously it silently escaped the action's cache intonode_modules/.cacheor the OS temp dir), and its key drops every stat-based fingerprint that a re-clone rotates: config files and gitignored dotenv files are now content-hashed, and the toolchain is keyed by package versions (matching the lint ruleset hash) rather than install mtimes. The stat-fingerprinted dead-code caches gain mtime repair (the ninja/restat pattern): entries carry a content-hash witness, and a stat mismatch over identical bytes — every file after a fresh checkout — re-hashes once, accepts the entry, and persists the refreshed stat so the cost is paid once per checkout, not per run. This covers core's whole-project dead-code result cache (per-file(mtime, size, hash)records replacing the stats-in-key fingerprint) and deslop-js's incremental summary store (parse summaries, package-reference facts, and the manifest/bundler-config fingerprints feeding the collect/resolution hashes). Expired supply-chain score entries are also pruned past their TTL so restored cache directories stop accumulating dead purls. Everything stays fail-open and byte-identical to an uncached scan; on a re-cloned repo with one changed file, a warm scan now replays ~100% of lint, sidecar, and parse work instead of starting cold. -
#1056
20d81f6Thanks @rayhanadev! - Hardened the dead-code result cache key against two silent-staleness classes. The fingerprinted extension and manifest name lists are now imported fromdeslop-js/analyzed-inputs— a new dependency-free subpath export assembled from the same constants deslop's own readers consume — so a deslop upgrade that widens its walk can never under-invalidate the cache. And the key now includes@react-doctor/core's own version, so upgrading react-doctor re-analyzes instead of replaying cached diagnostics shaped by an older core's post-processing. The cache schema-version constant remains for cache-format changes only. -
#1053
e257a5eThanks @rayhanadev! - Rescans now skip the dead-code analysis entirely when nothing it reads has changed. The pass persists its diagnostics keyed by a fingerprint over the analyzed source tree (stat-based, so additions, deletions, and edits all invalidate), the project's manifests, tsconfigs, lockfiles, knip/entry/ignore configuration, and the analyzer version — on an unchanged-input rerun the stored result replays instead of re-walking the whole import graph, cutting several seconds off warm rescans of large repos. Only complete, successful passes are stored;REACT_DOCTOR_NO_CACHE(or the granularREACT_DOCTOR_NO_DEAD_CODE_CACHE) disables it. -
#1057
ce49250Thanks @rayhanadev! - Dead-code analysis is now incremental across scans. deslop-js gains an opt-inincrementalCachePathconfig field: one stat walk per run (following directory symlinks, like the glob scans it stands in for) validates four independently keyed layers — per-file parse summaries (mtime+size), the collected file list, the module-resolution map (dropped whenever the file set or a bundler/tsconfig-like config changes, since resolution is file-set-dependent), and the per-file package-reference facts behind the unused-dependency content scans. The same walk also answers the stale-package file-discovery globs (config/docs/rescue/package.json/nx/tsconfig scans, verified byte-identical against fast-glob), so a cached run never re-walks the tree for them. Entry resolution deliberately stays live every run (it reads config/doc/sibling-source content no fingerprint can validate); on cached runs it moves to a dedicated worker thread so its filesystem work overlaps the main-thread analysis instead of serializing after it. Every layer fails open — corrupt, truncated, or version/config-mismatched stores degrade to a fresh computation, never a wrong result — writes are atomic and skipped when clean, and results are byte-identical to an uncached run. Unused-export detection also indexes re-export edges by source module, dropping an O(entry points × edges) scan that dominated the detector on large repos.react-doctor points the analysis worker at a per-project summary store next to the existing whole-result dead-code cache, so a changed-files rescan of a large repo re-parses only what changed (sentry, 9k files: ~8.0s → ~3.2s with 0-10 files edited; ~4.0s after adding a file; the cache-fill run costs the same as an uncached scan). The worker also stops running the discarded DRY-pattern redundancy detectors (
reportRedundancy: false), which shrinks the summary store by dropping fields only those detectors read, and reports the cache outcome asdeadCode.summaryCacheHits/deadCode.summaryCacheMissesin anonymized telemetry (absent whenever no analysis consulted the store).REACT_DOCTOR_NO_CACHE(or the granularREACT_DOCTOR_NO_DEAD_CODE_CACHE) disables it. -
#1054
8c004f0Thanks @rayhanadev! - Instant reruns now also work with uncommitted changes: the whole-repo scan-result cache no longer requires a clean worktree — it keys on the exact dirty state (every modified, staged, renamed, deleted, and untracked path plus a hash of its content), so rescanning the same work-in-progress tree hits the cache while any edit still invalidates it. -
#1052
1571119Thanks @rayhanadev! - Restore instant reruns on large repos: raiserunGit's output cap so the whole-repo scan-result cache works past ~15k tracked files.The cache's clean-worktree gates shell out to git through a helper that used Node's default 1 MiB
maxBuffer. On repos with roughly 15-25k tracked files (getsentry/sentry: 20,343),git ls-files -valone exceeds that,execFileSyncthrows ENOBUFS, the helper swallows it intonull, and the gates readnullas "hidden tracked state" — so the cache silently never stored or served a scan on exactly the repos where the instant-rerun path saves the most time. The helper now runs with an explicit 64 MiB cap, which clears monorepos with hundreds of thousands of files while still bounding a pathological child process. -
#1058
ea9a775Thanks @rayhanadev! - Warm rescans now skip the cross-file lint sidecar for files whose dependencies are unchanged. Previously the per-file lint cache replayed most rules but re-linted EVERY file with the cross-file ruleset (no-barrel-import, the Next.js metadata/Suspense rules,no-mutating-reducer-state, the React Native text rules) on every scan, because a sibling edit can flip an unchanged file's verdict. Each file now carries a dependency fingerprint — the exact set of filesystem probes (resolved import targets, barrel chains, ancestor layouts, nearest package.json/tsconfig, and the negative resolution candidates that catch shadowing) its cross-file rules consulted — and the sidecar replays the stored diagnostics when every probe still answers the same, re-linting only files whose dependency set changed. Every entry fails open (corrupt store, unparseable file, partial lint → fresh re-lint), a cross-file rule without a registered dependency collector re-lints everywhere, andREACT_DOCTOR_NO_CACHE(or the granularREACT_DOCTOR_NO_SIDECAR_CACHE) disables it. -
#1059
bdf8074Thanks @rayhanadev! - Added cache-temperature telemetry to the per-scan Sentry wide event, so cache effectiveness is queryable at a glance instead of being inferred from per-subsystem dims.cache.temperatureclassifies every scan asturbo(whole-repo scan-result replay — now marked by an explicitwholeRepoCacheHitflag on the replay path, never inferred from absent dims),warm(any incremental reuse across the per-file lint, sidecar, or dead-code caches),cold(caches on, zero reuse), ordisabled(the globalREACT_DOCTOR_NO_CACHEoff-switch).cache.warmthis the numeric headline magnitude in [0, 1] — the plain mean of the known subsystem reuse fractions, skipping subsystems that never consulted a cache. The existing per-subsystem dims (lint.cacheHitRatio,lint.sidecarReplayRatio,deadCode.cacheHit,deadCode.summaryCacheHits/Misses) are unchanged. Telemetry-only: no new counters, no JSON-report or cache-schema changes. -
Updated dependencies [
ced746f,20d81f6,ce49250]:- deslop-js@0.7.0
- oxlint-plugin-react-doctor@0.7.0