Skip to content

chore(code-health): track every react-doctor doctor.config.ts exception as debt (full inventory) #364

Description

@masch

Pre-flight Checks

Problem Description

apps/mobile/doctor.config.ts currently carries 11 override groups. A few are covered by #363; the rest are undocumented debt. Every row is justified with evidence (react-doctor is at 100/100 with them), but the goal is to shrink the table with real fixes — not keep it forever.

Full override inventory

# Files Rules ignored Why Covered by
1 src/app/_layout.tsx deslop/unused-file Expo Router loads _layout.tsx by convention; deslop does not understand file-based routing new
2 **/experiences.tsx rn-no-inline-flatlist-renderitem, rn-list-callback-per-row Horizontal category selector, small static items new
3 package.json deslop/unused-dependency, expo-lockfile Workspace/monorepo false positives new
4 experiences.tsx, explore.tsx, track-detail-view.tsx, trip-detail-view.tsx, track-map.tsx react-compiler, react-hooks-js/todo, prefer-useReducer, set-state-in-effect, no-initialize-state, no-giant-component try/finally data-fetching + React Compiler limitation; independent loading/error/data state by design #363 (react-hooks-js/todo only) — the other 4 rules are new to this inventory
5 config-cache, translation-cache, device-service (+ .web variants) deslop/unused-export Platform-split files (Metro resolves .web.ts dynamically) #363 (device-service)
6 use-track-download.ts, download-manager-store.ts no-create-object-url-without-revoke Blob URLs are the live expo-audio web playback source; lifecycle-correct revocation implemented #363
7 download-manager-store.ts async-await-in-loop Sequential stream-reader chunk reads require await in a loop new
8 use-track-download.ts, use-immersion-player.ts no-fetch-in-effect, no-event-handler Custom background ETag fetching + audio player sync with external subscriptions new
9 use-purchase.ts no-effect-with-fresh-deps, exhaustive-deps, react-compiler-no-manual-memoization Hooks optimized by React Compiler; useCallback used to satisfy standard exhaustive-deps new
10 dist/** artifact-baas-authority-surface Static build artifacts new
11 src/app/poetics/[id].tsx no-loading-flag-reset-outside-finally Genuine rule conflict: a real finally breaks React Compiler (BuildHIR::lowerStatement — see #147); without it the rule flags even though the reset is mirrored on every catch path (the catch does not re-throw) new

Detailed notes on rows not covered by #363

Row 4 (extra rules)prefer-useReducer, set-state-in-effect, no-initialize-state, no-giant-component: these detail screens use independent loading/error/data state and fetch via useEffect by design. A real fix would extract a shared data-fetching hook (see #147) — that is the refactor that would retire several of these rows at once.

Row 11 (poetics/[id].tsx) — added during the web-checkout fix (audioUrl refresh after purchase). The mirrored reset (setRefreshingExperience(false) at the end of try and in catch) is semantically identical to finally because the catch does not re-throw. React Doctor's static analysis does not recognize the mirror, so the rule fires on the trailing setState inside try regardless. No code shape satisfies both rules while React Compiler is enabled. Fix path: (a) upstream analyzer support for mirrored resets, (b) React Compiler support for finalizers, or (c) a shared loading-flag helper the analyzer can prove correct.

Row 1 (_layout.tsx) — Expo Router convention file; deslop's unused-file rule does not model the router's implicit require. Fix: verify newer deslop handles _layout.tsx or add a convention-aware rule exemption upstream.

Row 2 (experiences.tsx FlatList) — 8 static items; extracting renderItem would add indirection without benefit. Fix: document a size threshold that the analyzer accepts, or a small CategoryChip component.

Row 7 (async-await-in-loop) — Streams API reader.read() must be awaited sequentially; there is no parallel-safe variant. Fix: analyzer should whitelist ReadableStreamDefaultReader loops.

Row 8 (no-fetch-in-effect / no-event-handler)use-track-download does background ETag validation; use-immersion-player subscribes to the audio player store. These are the established architecture. Fix: extract the ETag worker + player sync into lib modules with tests, then re-evaluate.

Row 9 (use-purchase.ts) — React Compiler memoizes the hook; the manual useCallback exists to satisfy exhaustive-deps. Fix: revisit when compiler-aware eslint rules stabilize (react-hooks-js 0.9+).

Row 10 (dist/)** — build output; the rule has no business scanning it. Fix: analyzer should ignore dist by default.

Verification & Current State

Acceptance Criteria

  • Every row in the inventory above is either (a) resolved with a real code fix and its override removed, or (b) closed as an analyzer/upstream limitation with a link to the upstream issue.
  • bun run doctor stays at No issues found after each row is touched.
  • All tests + typecheck remain green after any change.

Affected Area

Code quality / linting — apps/mobile/doctor.config.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requesttype:choreMaintenance or tooling changes

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions