Residue from #29 (the React analyzer itself shipped in 75d40d6; this is the one bucket that never fills).
Problem
The React Health page has a Compiler Readiness section whose blurb promises "React Compiler diagnostics reported by the official React lint rules" (app/src/monitor/views/reactHealth.logic.ts:85-89). On a repo that has the current eslint-plugin-react-hooks configured and is reporting compiler diagnostics, that section reads 0 issues. The findings are all filed under Hooks instead, so the page cannot answer the question it advertises: is this codebase ready for the React Compiler?
Where it is
src/runners/react.ts:55-66:
function categoryForRule(rule: string | undefined): ReactCategoryId {
if (!rule) return "component-structure";
if (rule === "react-hooks/exhaustive-deps") return "effects";
if (rule.startsWith("react-hooks/")) return "hooks"; // :58 — swallows everything
if (rule.startsWith("react-refresh/")) return "fast-refresh";
if (rule.startsWith("jsx-a11y/")) return "accessibility";
if (rule === "react/jsx-key" || /(^|\/)(jsx-key|no-array-index-key|key)/.test(rule)) return "rendering";
if (/compiler/i.test(rule)) return "compiler-readiness"; // :62 — unreachable for these
...
The compiler test at :62 matches on the string "compiler" appearing in the rule id. No current rule id contains it.
Why :62 never fires
Verified against the installed plugin, [email protected] (app/node_modules/eslint-plugin-react-hooks). Its recommended-latest config enables 17 rules, all reported by ESLint under the react-hooks/ prefix:
react-hooks/rules-of-hooks react-hooks/immutability
react-hooks/exhaustive-deps react-hooks/globals
react-hooks/static-components react-hooks/refs
react-hooks/use-memo react-hooks/set-state-in-effect
react-hooks/void-use-memo react-hooks/error-boundaries
react-hooks/preserve-manual-memoization react-hooks/purity
react-hooks/incompatible-library react-hooks/set-state-in-render
react-hooks/unsupported-syntax react-hooks/config
react-hooks/gating
These are the React Compiler diagnostics — the plugin ships them as the compiler-powered lint rules (meta.docs.url points at react.dev/reference/eslint-plugin-react-hooks/lints/<rule>). Not one of them contains "compiler". They all match :58 first, so every one is bucketed hooks.
:62 is reachable only for the deprecated standalone eslint-plugin-react-compiler (rule id react-compiler/react-compiler), which was folded into eslint-plugin-react-hooks at v6. So on any project using the current plugin, compiler-readiness is structurally always 0.
Two smaller mis-bucketings fall out of the same line:
- Effects. Only
exhaustive-deps is routed to effects (:57). set-state-in-effect, no-deriving-state-in-effects, exhaustive-effect-dependencies and memoized-effect-dependencies are effect rules and land in hooks.
- Error boundaries.
react-hooks/error-boundaries lands in hooks, not error-boundary.
What to do
- Cheapest, ships alone: add an explicit
react-hooks/<rule> → bucket table consulted before the :58 prefix fallback, and keep :58 as the fallback for rules the table does not know (forward-compatible with plugin releases that add rules). Proposed mapping, judgment calls flagged below:
compiler-readiness: immutability, purity, globals, refs, set-state-in-render, preserve-manual-memoization, incompatible-library, unsupported-syntax, config, gating
effects: set-state-in-effect, no-deriving-state-in-effects, exhaustive-effect-dependencies, memoized-effect-dependencies, plus the existing exhaustive-deps
error-boundary: error-boundaries
hooks: rules-of-hooks, hooks, capitalized-calls, component-hook-factories
component-structure: static-components
use-memo / void-use-memo / memo-dependencies: see open question
- Keep the
/compiler/i test as a trailing fallback so the legacy react-compiler/react-compiler rule still lands correctly.
- Optional, once 1 lands: surface the detected plugin version in
details.tooling, so a report can be read years later without guessing which rule set produced it.
Alternatives considered and rejected
- Read the bucket off the rule's ESLint
meta. Rejected: there is no category there. Checked purity, immutability, use-memo, set-state-in-effect, error-boundaries, rules-of-hooks — every one is { type: "problem", docs: { description, recommended, url } }. Nothing machine-readable says "this is a compiler diagnostic". A static map is the only option.
- Just reorder
:58 and :62. Rejected: it changes nothing. /compiler/i does not match any of these rule ids in either order.
- Match on the
meta.docs.url path (/lints/) to mean "compiler diagnostic". Rejected: it would require loading the target repo's node_modules at scan time, and the analyzer only ever sees ESLint's JSON output, not the plugin object.
- Drop the
compiler-readiness bucket. Rejected: the React Compiler question is exactly the differentiated thing this analyzer is for, and the app already has a section built for it.
Acceptance criteria
- A unit test feeds
parseReactEslintIssues output containing react-hooks/purity, react-hooks/immutability and react-hooks/set-state-in-render and asserts they land in compiler-readiness, not hooks.
- A test asserts
react-hooks/set-state-in-effect lands in effects and react-hooks/error-boundaries in error-boundary.
react-hooks/rules-of-hooks still lands in hooks; react-hooks/exhaustive-deps still lands in effects.
- An unknown future
react-hooks/whatever still lands in hooks rather than crashing or defaulting to component-structure.
src/runners/react.test.ts:201 (expect.objectContaining({ id: "compiler-readiness", issues: 0 })) is re-examined — it currently pins the empty bucket as expected on a fixture with no ESLint at all, which is fine, but it should no longer be the only assertion about that bucket.
Constraints
- Version skew. Projects on
eslint-plugin-react-hooks v5 only ever emit rules-of-hooks and exhaustive-deps; the table must leave those repos exactly as they are today (empty compiler-readiness is the honest answer there).
- Lockstep with the app.
app/src/monitor/views/reactHealth.logic.ts:235-247 is a verbatim copy of this same function. The app prefers the CLI's details.categories when present (:377), so fixing the CLI fixes the section counts — but buildReactFixQueue (:443) still calls the local copy, so fix-queue rows keep the wrong category label until the app is updated too. Tracked in the companion app issue linked below.
Open question for the maintainer
use-memo, void-use-memo, memo-dependencies, preserve-manual-memoization and static-components are compiler diagnostics by origin but read as memoization/structure advice to a user. Do they belong in compiler-readiness (origin) or in component-structure (what the developer would go fix)? Pick one and the table is unambiguous.
Related
Residue from #29 (the React analyzer itself shipped in
75d40d6; this is the one bucket that never fills).Problem
The React Health page has a Compiler Readiness section whose blurb promises "React Compiler diagnostics reported by the official React lint rules" (
app/src/monitor/views/reactHealth.logic.ts:85-89). On a repo that has the currenteslint-plugin-react-hooksconfigured and is reporting compiler diagnostics, that section reads 0 issues. The findings are all filed under Hooks instead, so the page cannot answer the question it advertises: is this codebase ready for the React Compiler?Where it is
src/runners/react.ts:55-66:The compiler test at
:62matches on the string "compiler" appearing in the rule id. No current rule id contains it.Why
:62never firesVerified against the installed plugin,
[email protected](app/node_modules/eslint-plugin-react-hooks). Itsrecommended-latestconfig enables 17 rules, all reported by ESLint under thereact-hooks/prefix:These are the React Compiler diagnostics — the plugin ships them as the compiler-powered lint rules (
meta.docs.urlpoints atreact.dev/reference/eslint-plugin-react-hooks/lints/<rule>). Not one of them contains "compiler". They all match:58first, so every one is bucketedhooks.:62is reachable only for the deprecated standaloneeslint-plugin-react-compiler(rule idreact-compiler/react-compiler), which was folded intoeslint-plugin-react-hooksat v6. So on any project using the current plugin,compiler-readinessis structurally always 0.Two smaller mis-bucketings fall out of the same line:
exhaustive-depsis routed toeffects(:57).set-state-in-effect,no-deriving-state-in-effects,exhaustive-effect-dependenciesandmemoized-effect-dependenciesare effect rules and land inhooks.react-hooks/error-boundarieslands inhooks, noterror-boundary.What to do
react-hooks/<rule>→ bucket table consulted before the:58prefix fallback, and keep:58as the fallback for rules the table does not know (forward-compatible with plugin releases that add rules). Proposed mapping, judgment calls flagged below:compiler-readiness:immutability,purity,globals,refs,set-state-in-render,preserve-manual-memoization,incompatible-library,unsupported-syntax,config,gatingeffects:set-state-in-effect,no-deriving-state-in-effects,exhaustive-effect-dependencies,memoized-effect-dependencies, plus the existingexhaustive-depserror-boundary:error-boundarieshooks:rules-of-hooks,hooks,capitalized-calls,component-hook-factoriescomponent-structure:static-componentsuse-memo/void-use-memo/memo-dependencies: see open question/compiler/itest as a trailing fallback so the legacyreact-compiler/react-compilerrule still lands correctly.details.tooling, so a report can be read years later without guessing which rule set produced it.Alternatives considered and rejected
meta. Rejected: there is no category there. Checkedpurity,immutability,use-memo,set-state-in-effect,error-boundaries,rules-of-hooks— every one is{ type: "problem", docs: { description, recommended, url } }. Nothing machine-readable says "this is a compiler diagnostic". A static map is the only option.:58and:62. Rejected: it changes nothing./compiler/idoes not match any of these rule ids in either order.meta.docs.urlpath (/lints/) to mean "compiler diagnostic". Rejected: it would require loading the target repo'snode_modulesat scan time, and the analyzer only ever sees ESLint's JSON output, not the plugin object.compiler-readinessbucket. Rejected: the React Compiler question is exactly the differentiated thing this analyzer is for, and the app already has a section built for it.Acceptance criteria
parseReactEslintIssuesoutput containingreact-hooks/purity,react-hooks/immutabilityandreact-hooks/set-state-in-renderand asserts they land incompiler-readiness, nothooks.react-hooks/set-state-in-effectlands ineffectsandreact-hooks/error-boundariesinerror-boundary.react-hooks/rules-of-hooksstill lands inhooks;react-hooks/exhaustive-depsstill lands ineffects.react-hooks/whateverstill lands inhooksrather than crashing or defaulting tocomponent-structure.src/runners/react.test.ts:201(expect.objectContaining({ id: "compiler-readiness", issues: 0 })) is re-examined — it currently pins the empty bucket as expected on a fixture with no ESLint at all, which is fine, but it should no longer be the only assertion about that bucket.Constraints
eslint-plugin-react-hooksv5 only ever emitrules-of-hooksandexhaustive-deps; the table must leave those repos exactly as they are today (emptycompiler-readinessis the honest answer there).app/src/monitor/views/reactHealth.logic.ts:235-247is a verbatim copy of this same function. The app prefers the CLI'sdetails.categorieswhen present (:377), so fixing the CLI fixes the section counts — butbuildReactFixQueue(:443) still calls the local copy, so fix-queue rows keep the wrong category label until the app is updated too. Tracked in the companion app issue linked below.Open question for the maintainer
use-memo,void-use-memo,memo-dependencies,preserve-manual-memoizationandstatic-componentsare compiler diagnostics by origin but read as memoization/structure advice to a user. Do they belong incompiler-readiness(origin) or incomponent-structure(what the developer would go fix)? Pick one and the table is unambiguous.Related