Do not file on facebook/react or vercel/next.js. Engage existing threads with evidence; a cold issue on the wrong repo risks instant close as duplicate.
Post as a comment on Tooling: Visual HTML reports for React Compiler (Vite plugin) — closest existing thread on per-component visibility.
Sharing real-build evidence on the "how do I know what actually got optimized in
production?" question, since it's adjacent to the tooling here.
I built a small coverage tool + a Next 15.5 harness (webpack and Turbopack, one
Server and one Client component) to test whether per-component optimization status
is recoverable from a real `next build`:
- Minified production builds (default): memo-cache markers are unrecoverable —
`(0,r.c)(N)` calls and names get mangled/inlined — so per-component attribution
isn't possible. The tool returns `coverageAvailable: false` instead of a fake
number.
- Unminified builds: client components are attributable via memo-slot calls +
export names; coverage is accurate. Server Components carry no client memo
markers, so they're excluded from the client denominator rather than scored as
failures.
Repro: `npm run test:harness:turbopack` → https://github.com/Asuddle/react-compiler-coverage
I can see the diagnostics surface is moving fast: the Rust SWC bridge (swc#11917)
forwards diagnostics, swc#11965 adds a `lint`/`lintSync` API to
`@swc/react-compiler`, and oxc#23202 adds a lint-only `react-compiler` rule with
opt-in `reportAllBailouts`. Those cover the violation/bailout side.
The piece coverage tooling still needs — and that I don't see surfaced yet — is the
success side: per-component `CompileSuccess` events (with source locations)
reaching build-tool consumers like `next build`, alongside skip/error. Without the
success signal there's no honest denominator: you can enumerate what failed, not
what was optimized vs. what the compiler silently left alone. A machine-readable
per-build artifact (e.g. sidecar JSON) with the full success/skip/error triple
would let tools drop the bundle-scanning heuristics and report coverage on normal
minified production builds.
Happy to contribute the harness as a repro fixture if useful.
Optional cross-reference: link this comment from the discussion on
swc PR #11965 (lint API) noting
that lint/bailout coverage ≠ CompileSuccess for coverage denominators. Reference
PRs by number and behavior — do not attribute technical direction to named
maintainers in public comments.
Surface per-component CompileSuccess from the SWC/Oxc compiler path to build tooling
Not: "Expose React Compiler diagnostics" — that ask gets deflected by lint APIs already in flight.
Tools that report React Compiler coverage and CI regression gates need per-component signals across three buckets:
| Event | Coverage use |
|---|---|
CompileSuccess |
Denominator + optimized count — what was actually memoized |
CompileError / bailout |
Actionable failures — Rules-of-React violations |
CompileSkip |
Explicit opt-out — 'use no memo', suppression, etc. |
The Babel plugin exposes all three via its logger callback. An isolated Babel
pass with the plugin wired up has full logger access — react-compiler-coverage uses
this today for Babel projects.
Production Next.js builds do not surface that logger to consumers. Current reality (Next 15.5):
- SWC eligibility — Next's SWC layer decides which files need the compiler
(
isReactCompilerRequired), avoiding a full-project Babel pass. - Babel transform — the actual compilation still runs through
babel-plugin-react-compiler, but the logger is not wired into anythingnext buildexposes to tooling. - Rust/SWC-native path in flight — swc #11917
(by magic-akari) adds the
swc_ecma_react_compilerbridge, SWC ↔ React Compiler AST conversion,.swcrcjsc.transform.reactCompiler, and diagnostics forwarding — pending published Rust crates. This is progress, but what is landing upstream today is predominantly the lint/violation side, not the success side coverage tooling needs.
Without CompileSuccess reaching build consumers, downstream tools are forced to
infer optimization by heuristically scanning bundle output (memo cache calls
like (0,r.c)(7), export names, webpack path comments). That works only on
unminified builds and cannot distinguish "silent because nothing to memoize"
from "silent because the compiler gave up."
Recent upstream work covers violations and bailouts — not optimization success:
| Work | What it surfaces |
|---|---|
swc #11965 — lint / lintSync on @swc/react-compiler |
Rules-of-React violations, lint diagnostics |
oxc #23202 — react-compiler lint rule |
Lint-only mode; opt-in reportAllBailouts for bailouts |
| swc #11917 — Rust SWC bridge | Diagnostics forwarding; full compiler integration in progress |
These answer "what failed / bailed out?" Coverage tooling also needs
"what succeeded?" — per-component CompileSuccess with source locations —
or there is no honest denominator.
Repo: react-compiler-coverage
Harness: test/harness/next-app/ (Header = Server Component, ProductCard = 'use client')
| Build | Client component (ProductCard) | Server component (Header) | Tool result |
|---|---|---|---|
| Webpack minified | markers stripped | no client memo slots | coverageAvailable: false |
| Webpack unminified | (0,r.c)(N) + memo slots in client chunk |
no scannable client markers | 100% client (1/1) |
| Turbopack minified | markers stripped | no client memo slots | coverageAvailable: false |
| Turbopack unminified | "ProductCard" export + (0,r.c)(7) (mangled function i) |
excluded from denominator | 100% client (1/1) |
Observations:
- Logger exists in Babel; production build doesn't expose it. Isolated Babel +
plugin = full logger.
next build= no per-component event stream to consumers. Rust bridge adds diagnostics forwarding, scoped to lint/violations today. - Client bundles use SWC memo runtime, not Babel's
_c(— e.g.(0,r.c)(7)withs[0]…s[6]slots. - Server Components often have no client memo markers in RSC output — excluded from client-component coverage denominator, not scored as silent failures.
- Webpack vs Turbopack attribution differs — webpack emits
;// ./components/X.tsxcomments; Turbopack preserves export strings but mangles function names. Both require unminified output for bundle-scanning heuristics.
Reproduce:
git clone https://github.com/Asuddle/react-compiler-coverage.git
cd react-compiler-coverage
npm install && npm run build
npm run test:harness:turbopackAs the Rust/SWC-native (and Oxc) compiler path matures, surface the full logger event triple to build-tool consumers — not just lint diagnostics:
type CompileEvent =
| { kind: 'CompileSuccess'; fnLoc: SourceLocation; memoBlocks?: number; /* … */ }
| { kind: 'CompileError'; fnLoc: SourceLocation; detail: DiagnosticDetail }
| { kind: 'CompileSkip'; fnLoc: SourceLocation; reason: string };The differentiator of this ask is CompileSuccess reaching next build (and
similar bundlers) — skip/error alone cannot support coverage denominators.
Possible delivery (open to discussion):
- Sidecar JSON per build (e.g.
.next/react-compiler-events.json) - Transform/lint API returning upstream
CompileResultlogger JSON (swc #11965 discussion already points at returning serializable upstream types rather than parallel SWC models — extend that to success events in build mode) - Bundler forwarding layer (Next/Turbopack) passing events to CI tooling
Requirements:
- Per-function source locations (file + line) mapping to original TS/JSX
CompileSuccessin addition to skip/error — required for coverage- Available in
next buildon minified production output (no specialminimize: falseCI step)
- Replace bundle-scanning heuristics with authoritative per-component status
- Report client-component coverage on normal minified production builds
- Distinguish Server vs Client compile outcomes using compiler knowledge
- Ship honest CI gates without
coverageAvailable: falseworkarounds
| Venue | Action |
|---|---|
| reactwg/react-compiler #79 | Primary — comment with harness evidence (see above) |
| swc PR #11965 | Cross-reference: lint API ≠ CompileSuccess for coverage |
| swc #11697 | Parity work — logger event types already in crate surface |
| swc #11751 | Closed — landed in #11917; don't re-file |
Do not open fresh issues on facebook/react or vercel/next.js for this ask.
The upstream comment is cheap and worth posting. The differentiator that makes people care beyond a number — silent-fine vs. silent-should-optimize — is entirely in-repo:
- Isolated Babel logger →
CompileSuccess/CompileError/CompileSkip reportAllBailouts/ eslintreact-hooks/todo→ bailout reasons for silent components- Reconcile the two → actionable "this component should have optimized but didn't"
See silent-diagnosis design (TBD) — that path is under our control today.