Match upstream's catchable missing-identifier invariant in `PruneNonEscapingScopes`, preventing a later misleading `PreserveManualMemo` diagnostic. Adds the original-shaped named function expression regression.
AI-assisted and reviewed by the contributor.
Support for (;;) by lowering the omitted test to true and omitting the synthetic HIR initializer during codegen.
AI assistance was used to implement and verify this change.
Separate React Compiler emission from lint analysis so oxlint can dead-strip compiler codegen.
This reduces the stripped oxlint binary by 145 KiB (1.21%).
AI-assisted with Codex.
Improve `react/no-deriving-state-in-effects` diagnostics with labeled state updates, named reactive dependencies, and actionable render guidance. Adds fixture coverage for single and multiple dependencies.
AI assistance was used for this change.
BigInt object keys fell through generic unsupported-key handling. Lower
them through the exact parser-provided decimal string, preserving
alternate bases and values beyond Number precision.
Validated with `just ready`.
AI-assisted.
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
React Compiler HIR already preserves both raw and cooked tagged-template
values, but lowering rejected templates whenever they differed. Remove
the stale bailout and cover escaped and invalid escape sequences.
Validated with `just ready`.
AI-assisted.
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
`known_incompatible` is already checked before both aliasing and legacy
signature handling, making the legacy-path TODO stale. Add
aliasing-signature coverage to guard the shared behavior.
Validated with `just ready`.
AI-assisted.
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Improve React Compiler diagnostics with actionable help and cause-oriented labels. Preserve-memoization and recursive-initialization diagnostics previously spanned entire callbacks; they now keep codeframes compact while refs and `try`/`finally` show related locations.
Validated with React Compiler and React linter suites, Clippy, `just ready`, and the 24-rule VS Code ecosystem audit.
AI-assisted: Codex was used to implement and validate this change. I reviewed and take responsibility for the result.
Bail out when JSX `ref` values are loaded through properties, matching React Compiler and preserving getter and live-binding evaluation semantics.
The validator previously stopped temporary tracking for every property result inferred as ref-like instead of only stopping for `ref.current`.
Validation: `cargo test -p oxc_react_compiler` and `just ready`.
AI assistance: Codex was used to investigate and prepare this change; the contributor remains responsible for reviewing it before merge.
Queue JSX-outlined components back through the normal `process_fn` pipeline, matching Babel's `Program.ts`. Oxc rebuilds immutable semantics once per FIFO generation and preserves Babel's outlined declaration order.
Validated against the React Compiler fixture suite, Babel output, and `just ready`.
AI-assisted; reviewed and validated by the contributor.
Preserve named function expression names when the private name is unreferenced. This matches React Compiler lowering and avoids changing observable `Function.name` values.
Validated with `just ready`.
AI-assisted.
Improve React Compiler diagnostics with focused primary spans and related labels for declarations, dependencies, effects, nested functions, dynamic Hooks, and transitive ref access. This avoids full-function highlights and fixes zero-width arrow labels when lowered parameter spans are missing.
Validation: `env -u NO_COLOR just ready`.
AI-generated with Codex; reviewed and validated by the contributor.
Fix destructured function and rest parameters that become context variables by lowering them through temporaries before `StoreContext`. This avoids inconsistent `Destructure`/context HIR and compiles supported components correctly.
Validated with `just ready` and runtime equivalence checks against React 19.2.1's compiler runtime.
AI usage: Implemented with assistance from OpenAI Codex.
Splits the experimental `react/react-compiler` rule into 24 category-specific rules matching `eslint-plugin-react-hooks` v7.
- Runs React Compiler once per file and routes structured diagnostics to each enabled rule.
- Removes `react/react-compiler` and `reportAllBailouts`, with rules categorized by user-facing lint intent.
- Adds per-rule documentation and deduplicates shared React lint URLs.
- Preserves standardized compiler diagnostics while reporting each finding under its Oxlint rule name.
Breaking change: configurations using `react/react-compiler` must migrate to the category-specific rules.
Written with Claude Code and Codex.
Standardize React Compiler diagnostics with concise titles, primary labels, actionable help, notes, structured categories, and official guidance links.
Preserve React guidance when diagnostics are surfaced through Oxlint, and refresh affected snapshots.
AI-assisted; reviewed and validated by the contributor.
Centralizes React compiler diagnostics in explicit `#[cold]` functions and removes the `[ReactCompiler]` prefix, keeping diagnostic formatting off hot paths. Updates related NAPI expectations.
AI assistance was used.
Preserve tagged-template call, aliasing, and mutation effects instead of assuming no-substitution tags are primitive or pure.
Execute opaque or effectful tags unconditionally while retaining memoization only when a configured signature proves a read-only pure call and safe return.
AI-assisted investigation and implementation; reviewed and understood by the contributor.
Preserve inline TypeScript enums as opaque pass-through HIR nodes, matching upstream React Compiler behavior. Apply Oxc-specific raw AST reference renaming, and skip compilation when passthrough would hide nested captures or flatten block-local bindings.
AI-assisted: Codex was used to implement and validate this change.
Match the public validation defaults from babel-plugin-react-compiler 1.0 while retaining explicit opt-in for newer validations.
Validated with fixture snapshots and `just ready`.
AI-assisted investigation and implementation; reviewed and understood by the contributor.
React Compiler's JSX reconstruction preserved the HIR identifier expression kind, causing compiler-generated lowercase aliases such as `navTitle` to lower as variable references. Classify reconstructed tags by their emitted JSX spelling while preserving underscore-prefixed component references.
Validation: `cargo test -p oxc_react_compiler`, `cargo clippy -p oxc_react_compiler --all-targets -- -D warnings`, and the transform-react NAPI suite.
AI assistance: OpenAI Codex was used to investigate, implement, and validate this change.
Removes synthetic expression statements emitted after captured compound assignments when their value is unused. The lowering-created context load is tagged so analysis retains it, while codegen can omit it after the lvalue is pruned. Consumed assignment results are preserved.
Validated with `env -u NO_COLOR just ready`.
AI assistance: Codex was used to investigate, implement, and validate this change.
Retain exception-handler edges for value-block stores so caught throws do not commit the input/result memo entry. This preserves retries for identical inputs after a transient throw.
Adds a sequential-render evaluator fixture covering throw-once behavior.
Validated with `env -u NO_COLOR just ready` and a Sprout runtime comparison.
AI assistance was used to investigate and implement this change.
Preserve potentially throwing operations inside try/catch when dead-code elimination would otherwise remove their unused results. Share the throw classifier with try-edge pruning, retain caught destructuring, and use safe root dependencies without hoisting throwing operations outside their handlers.
This preserves source runtime behavior for property and computed reads, coercions, destructuring, computed keys, and spreads. Latest React Compiler currently has the same DCE divergence, so this follows JavaScript semantics rather than its incorrect output.
Validated with `just ready` and React Compiler’s Sprout evaluator.
AI assistance was used to investigate and implement this change.
Model `Symbol()` as identity-bearing so escaping symbols are memoized, matching Babel. The regression came from registering `Symbol` with copy-like primitive globals while allowing capitalized built-in calls.
Validation: `cargo test -p oxc_react_compiler`; Babel output comparison; runtime identity check. `just ready` is blocked by three unrelated ANSI-sensitive Oxlint snapshot mismatches in this environment.
AI-assisted: Codex was used to investigate, implement, verify, and prepare this PR.
Scopes `forwardRef`/`memo` discovery context to the direct callback so nested helpers are not incorrectly compiled with stale caches. Adds regression coverage for arrow and function callbacks.
Validated with `just ready`.
AI-assisted.
Closes#25536
## Problem
With `enableFunctionOutlining` (default on), the compiler outlines function expressions whose lowered **context** is empty. Context only counts variables captured from scopes inside the function being compiled — a free variable of the whole compiled function (e.g. a binding of an *enclosing* function) is modeled as a plain global load/store in HIR, so a callback referencing one still gets outlined. Correctness then depends entirely on where the outlined declaration is inserted: upstream uses Babel `originalFn.insertAfter(...)`, which splices into the statement list containing the original at **any nesting depth**, preserving the invariant that every name visible to the original is visible at its sibling position.
`ox_insert_outlined_after` searched only `program.body`. For a component declared inside another function, the search missed and the fallback appended the outlined declaration at module scope:
```js
export function makeProbe() {
let mounts = 0;
function MountCounter() {
useEffect(() => { mounts += 1; }, []);
return null;
}
return { MountCounter, read: () => mounts };
}
```
compiled to
```js
export function makeProbe() { /* ... useEffect(_temp, t0) ... */ }
function _temp() {
mounts = mounts + 1; // ReferenceError: mounts is not defined
}
```
with no diagnostic — a silent miscompilation that only fails when the effect fires.
A second instance of the same shape: gating ran before outlined insertion. The gating rewrite replaces the declaration statement with a `const`, destroying the scope-id anchor, so gated components' outlined functions always fell to the end-of-program fallback (upstream inserts them during the compile-queue loop, before any replacement, so they land right after the gated `const`).
## Fix
- `ox_insert_outlined_after` locates the declaring statement through a new `InsertOutlinedAfter` mode on the shared `OxcVisitor`, scanning each statement list before descending into it. Top-level originals (the common case) cost the same as before — the target is found in the first list scanned with no descent — and the walk prunes itself once insertion happens. Matching by `scope_id` cell stays sound because `ox_replace_function` mutates originals in place and discovery skips compiled bodies, so the anchor statement is always original AST. The top-level append survives only as a fallback for sloppy-mode annex-B positions (`if (c) function F() {}`).
- Outlined insertion now runs before `ox_apply_gated_conditional`, restoring upstream's sequencing.
Arrow/function-expression originals still append at module scope: upstream does the same (`program.pushContainer('body', ...)`) — including for nested originals, where upstream's own output breaks the same way — and the port reproduces upstream behavior rather than diverging (now documented in the placement comment).
Placement in all three added fixtures (nested declaration = the issue repro, nested block, gating + outlining) is byte-identical to `babel-plugin-react-compiler` built from react @ 22e4f993c7 with matching options; the existing 1310 fixture snapshots are unchanged.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Treat a named function expression’s private name as a function-entry binding through HIR, SSA, and alias analysis. This prevents self-references from reaching mutation inference uninitialized and adds fixture coverage.
Closes#23752.
Validated with targeted compiler and linter checks, workspace checking, and linting.
AI-assisted.
Fix tagged template hooks so they are validated and treated like hook calls, including unconditional execution across renders.
Closes#24473.
AI-assisted: Codex was used to investigate and implement this change. I reviewed and take responsibility for it.
Fixes#25049.
Register `DeclareContext` bindings as locals before analyzing nested functions so local self-references are not treated as missing memo dependencies. Skip exhaustive dependency validation when both modes are disabled.
Validation: `just ready`
AI assistance was used to investigate and implement this change.
Prevent JSX callback-ref member expressions from marking their receiver as a React ref. Add a compiler fixture covering the regression.
Fixes#23753.
Validated with `just ready`.
AI assistance was used to prepare this change.
Normalize computed string-literal destructuring keys before React Compiler HIR lowering so aliased bindings are initialized correctly.
Fixes#23750.
AI assistance: Codex was used to investigate, implement, and validate this change.
## Summary
`oxlint` with `react/react-compiler` enabled aborted the whole process with
```
panicked at .../prune_non_escaping_scopes.rs:
Expected a node for all scopes
```
on inputs like:
```ts
export const Component = () => {
const raw = useValue();
return (() => {
try {
return check(raw) ? raw : null;
} catch {
return null;
}
})();
};
```
## Root cause
Running the same input through the version-matched `babel-plugin-react-compiler` fails the **identical invariant** (still present at facebook/react HEAD), so the analysis inconsistency is an upstream bug the port reproduces faithfully:
- IIFE inlining turns both inner `return`s into reassignments of a temporary, and scope alignment widens the `check(raw)` scope over the try/catch, so `visit_scope` associates the temporary with that scope;
- the scope's only own declarations sit in the ternary *test*, which `computeMemoizationInputs` deliberately never descends into ("Conditionals do not alias their test value"), so `visit_operand` — the only place scope nodes are registered — never fires for it;
- when the escaping temporary is memoized, `force_memoize_scope_dependencies` looks up the unregistered scope and fails the invariant.
The port's actual defect is the error channel: upstream raises a *catchable* `CompilerError`, so with `panicThreshold: 'none'` (what linting uses) the function is silently skipped and the rest of the file proceeds. The port had written this invariant as `.expect()`, turning the per-function bail-out into a process abort.
## Fix
- `compute_memoized_identifiers` and its recursive helpers now return `Result`; the invariant becomes `Err(ErrorCategory::Invariant.diagnostic(...))` and flows through the existing per-function `handle_error` / `panic_threshold` path — the same observable behavior as Babel (function skipped, everything else still compiles/lints).
- The `react/react-compiler` rule now drops `Invariant`-category diagnostics unless `reportAllBailouts` is enabled, matching the eslint plugin where the `invariant` rule is `preset: Off` ("only meant for the compiler team's consumption"); without this, the fix would have replaced the crash with a spurious error-level report on valid code. `ErrorCategory` is now exported from `oxc_react_compiler` for this check.
- New fixture `error.bug-prune-non-escaping-scopes-iife-try-catch-ternary.js` (upstream's `error.bug-*` convention for known compiler bugs) plus rule tests covering both configs.
With the rule enabled, `oxlint` on the repro now exits cleanly with no report — identical to ESLint on the same input.
## Also fixes#24842
The rule-side filter resolves#24842 as well: `react/react-compiler` reported `Invariant: Expected a variable declaration` on a `for` loop without a variable-declaration initializer (`for (; i < 3; i++)`). Same pattern, different stage: that invariant fires in `codegenForInit` and already flowed through the catchable `Err` channel, so it surfaced as a spurious error report rather than a panic. It too reproduces upstream faithfully — the version-matched `babel-plugin-react-compiler` fails the identical invariant (`Got ExpressionStatement`) and upstream's own `error.todo-kitchensink` fixture expects it; by default Babel silently skips the function and ESLint reports nothing. The #24842 repro is added to the rule tests (clean by default, surfaced under `reportAllBailouts`).
## Relation to #25347#25347 diagnosed this accurately (thanks @controversial!) but fixes it by registering scope nodes eagerly so compilation succeeds and emits memoized output. That would make oxc compile an input every upstream compiler version refuses to compile, producing memoization decisions with no upstream validation, exactly where upstream's invariant declares its internal model inconsistent — a wrong scope association would then surface as a missed invalidation (stale UI) instead of a crash, and the pass would permanently diverge from upstream for future syncs. The behavioral fix belongs upstream in facebook/react; this PR restores upstream's bail-out semantics so oxlint no longer crashes, and the port will inherit the upstream fix when it lands.
Closes#24710Closes#24842Closes#25347
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Preserve catch-observable property and computed loads during dead-code elimination, and record their root operands as reactive-scope dependencies so throwing reads remain inside `try`.
This prevents memoization from suppressing catch behavior or hoisting potentially throwing property access into cache guards.
Validated with the React Compiler crate suite, NAPI transform suite, and original-vs-compiled runtime cases.
AI assistance was used to investigate and implement this change.
Preserve JSX component classification through React Compiler HIR and codegen so underscore-prefixed components are emitted as identifiers instead of intrinsic string tags.
Update the existing context-variable JSX fixture to cover `_Component`.
Validated with the React Compiler crate suite, NAPI suite, and all six ecosystem reproductions.
AI assistance was used.
React Compiler rebuilt source-derived AST nodes with empty spans, so compiled function bodies had no useful source mappings. Preserve retained HIR and reactive spans during codegen while keeping synthetic memoization scaffolding unmapped.
AI-assisted: implementation used Codex; the author remains responsible for review.
Model tagged-template expressions as function applications during mutation and alias inference. This keeps mutations of returned objects within the memoization scope and preserves the implicit template argument position.
Checked with `cargo test -p oxc_react_compiler` and the transform-react NAPI suite.
AI-assisted.
The discovery walker let a declarator name flow through `forwardRef`/`memo` calls, so `const Foo = forwardRef((props, viewTracker) => ...)` was treated as a named component and rejected by the component param check (a second param must look like a ref). Upstream only names a function from its direct parent (declarator/assignment/property); a function in call arguments is anonymous and is detected through the forwardRef/memo callback branch, which never checks params.
Fixes the two Simorgh files from the ecosystem comparison (`TopStoriesItem`, `LatestMediaItem`): both now compile with output matching upstream, including identical memo cache sizes. Adds a fixture for the pattern; no existing fixture snapshots change.
Created with assistance from Claude.
Compile parenthesized arrow components by preserving declarator name inference through nested parentheses.
Adds a React Compiler fixture for the ecosystem reproduction.
Created with assistance from Codex.
Align ref validation with Babel React Compiler 1.0 by limiting call-site alias-effect checks to values actually passed to the call. This keeps refs captured by returned event handlers valid while preserving diagnostics for refs passed to invoked functions.
Validated with `cargo test -p oxc_react_compiler`, the rebuilt `oxc-transform-react` binding against Outline’s `ContentEditable.tsx`, and `just ready`.
AI-assisted; the contributor remains responsible for review and submission.
Disable exhaustive memo dependency validation by default and align
suppression handling.
AI-assisted.
<sub>Stack created with <a
href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a
href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
Preserve Babel's non-node classification for parenthesized object returns so ordinary PascalCase factories are not compiled as React components.
Validated against the React Compiler suite and the Kibana reproduction.
AI-assisted.