## Description
This fixes false negatives in the existing React Compiler
`fast_check::is_required` predicate and documents its conservative
skip-gate contract.
Before this PR, `is_required` was a low-false-positive heuristic, but it
was not safe for callers that skip React Compiler when it returns
`false`. On the real v0 corpus it missed anonymous `forwardRef`
callbacks and a module using member hook calls. This PR improves the
existing method rather than adding a second public predicate, following
maintainer feedback.
The new implementation allows false positives but not false negatives:
`false` means compilation cannot change the program. It scans every
function context for JSX, actual hook calls matching React Compiler
HIR's own hook-name rule, member hook calls, and opt-in/dynamic-gating
directives in real directive prologues.
The Node `isReactCompilerRequired{,Sync}` APIs continue to call
`is_required`. Fatal and recovered parse errors return `true`, because
an unsuccessful fixed-syntax parse cannot prove that compilation is
unnecessary.
The package test command enumerates both test files explicitly so it
remains compatible with the existing Windows + Node 18 CI job.
## v0 correctness corpus
I benchmarked a fixed v0 checkout
(`1ab042a47c053bf5ddad5ffb9c6af153ce8e5b56`) on a 16-vCPU / 32-GB Vercel
DevBox:
- 1,816 tracked JS/TS source files
- 10,464,109 source bytes
- zero parser failures
- 257 modules actually transformed by the Rust React Compiler
- 67 compiler diagnostics
The harness parses the corpus once, runs detectors repeatedly, and uses
the actual compiler result as ground truth. It asserts that gated and
ungated transformed paths are identical.
| Detector | Selected files | False negatives |
| --- | ---: | ---: |
| `is_required` before this PR | 279 | 10 / 257 (3.89%) |
| Current Next.js conservative visitor | 315 | 0 / 257 |
| This PR: improved `is_required` | **302** | **0 / 257** |
The old implementation missed nine real `React.forwardRef` UI modules
and one hook module using `React.useState` / `React.useEffect`. The
improved implementation retains all 257 transforms while rejecting 13
more false positives than the current Next.js visitor.
## End-to-end compiler effect
A fresh optimized release run after applying all review feedback used
five interleaved gated/ungated samples on the exact corpus:
| Pipeline | Median | Transformed modules |
| --- | ---: | ---: |
| No gate | 2,371.956 ms | 257 |
| Improved `is_required` gate | **1,906.180 ms** | 257 |
That removes **465.776 ms / 19.64%** from the React Compiler pipeline on
the real v0 corpus without changing compiler output. The detector itself
took 20.624 ms for the full 10.46-MB corpus (mean of 100 passes).
The temporary synthetic CodSpeed benchmark was removed at review
request. The performance evidence remains the real v0 corpus with actual
compiler output as the correctness oracle.
## Review feedback incorporated
- Improved the existing `is_required` API instead of introducing
`may_require`.
- Detect hooks only at call sites and reuse React Compiler's
`is_hook_name` rule (`use` plus uppercase ASCII or a digit).
- Restrict opt-in strings to leading directive prologues using
`Stmt::can_precede_directive`.
- Treat both fatal and recovered parser errors conservatively in the
Node binding.
- Added actual-compiler regressions proving name-only and JSX-free
`React.createElement` wrapper cases do not compile.
- Removed the synthetic benchmark and changed the changeset from a minor
feature to a patch fix.
## Validation
- `cargo fmt --all -- --check`
- `cargo test -p swc_ecma_react_compiler` (107 unit tests + 15 fixtures)
- `cargo test -p binding_react_compiler_node` (14 passed)
- `cargo clippy -p swc_ecma_react_compiler -p
binding_react_compiler_node --all-targets -- -D warnings`
- `pnpm --filter @swc/react-compiler build:dev`
- `pnpm --filter @swc/react-compiler test` (8/8)
- Earlier full-PR validation: `cargo clippy --all --all-targets -- -D
warnings`; `packages/core: pnpm build:dev && pnpm test` (117 passed, 3
skipped)
- Final GPT-5.6 Sol xhigh + Claude Opus 5 xhigh autoreview panel: zero
findings, “patch is correct” (0.99 confidence)
Follow-up to
[vercel/next.js#96820](vercel/next.js#96820).
What?
swc_ecma_react_compiler::fast_check::is_requiredAPI to skip React Compiler work for modules that cannot change in nativeinfermode.annotationandallmodes unconditional.mdxjs-rs-turbopackbranch, with no duplicate SWC 75 stack.This consumes the implementation landed in swc-project/swc#12105, released in
swc_ecma_react_compiler23.0.0. Its contract is conservative: false positives add compiler work, while a negative result guarantees that compilation cannot alter the program.Why?
The native compiler currently runs for every parsed module in infer mode and is configured for both browser and App SSR contexts. This adds avoidable AST conversion/compiler work and compiles hydrated client modules in a server context that the existing Turbopack Babel path excludes.
This matters for large applications such as v0. Their legacy Babel React Compiler path creates a separate machine-sized Node worker pool in addition to PostCSS. Moving to the native path removes that pool; this patch makes the native path cheaper while preserving the established browser-only scope.
Performance
Upstream fast check on the v0 corpus
The final SWC predicate was checked against 1,816 real v0 modules (10.46 MB):
This Next.js patch only
Real v0 homepage, cold
.next, Chromium interaction gate, 16 vCPU / 32 GB, exact Next canary7916855653, exact v0 commit1ab042a47c, three samples per arm. Both arms use the native compiler.All three paired visible, quiet, and CPU deltas favored the patch. The RSS difference is inside noise, so this PR makes no patch-only memory claim.
v0 legacy Babel path to native + this PR
Both arms use
TURBO_ENGINE_EVICT_AFTER_SNAPSHOT=1and remain open for 120 seconds after network quiet.The large memory/process delta comes from switching v0 off the external Babel pool, not from this source diff alone. v0 still needs to enable the native compiler configuration separately.
Every accepted browser run required HTTP 200, a visible nonblank UI with at least ten controls, zero Next overlays/page errors, and a verified prompt interaction. The complete Next process session was sampled every 100 ms.
Validation
cargo fmt --all -- --checkcargo checkswc_ecma_react_compiler; WASI excludes it; noswc_core75 remainsmdxjs-rs-turbopackSWC 76 bump: 188 tests passed