2757 Commits
Author SHA1 Message Date
laurenandRaashish Aggarwal d083ec1da1 [compiler] Allow setState after await in effect functions (#36734)
`react-hooks/set-state-in-effect` flagged setState calls that happen
after an `await` inside an async function invoked from an effect.
Post-await code resumes in a microtask after the effect body has
returned, so the "synchronous setState cascades a render" rationale does
not apply; the lint was a false positive on a common data-loading shape
(15 reactions on the issue).

The fix exempts a setState only when it is *provably* post-await: a
forward must-dataflow over the HIR CFG computes the blocks that begin
after an await has executed on every path from the function entry
(optimistic initialization so loop back-edges do not pessimize the meet,
fixpoint to the greatest solution), plus an intra-block flag for
instructions after an Await in the same block. A setState reachable on
any await-free path still flags, so the conditional-await case remains
an error by design, with a fixture documenting that choice. Suppression
is sound under try/catch because HIRBuilder terminates blocks after each
instruction in a try region, and an awaited rejection also resumes in a
microtask.

Fixtures: post-await setState (event gone), setState before the first
await (still flags), setState after a conditional await (still flags).
First commit documents the false positive via the lint-mode logger
output, second removes it.

Builds on the approach in #36417 by @raashish1601, hardened from a
seen-await flag to the path-sensitive analysis above. Implemented
identically in the TypeScript compiler and the Rust port.

Verification: TS snap 1807/1807, Rust snap 1807/1807, cargo workspace
green, scoped TS-vs-Rust HIR parity harness green.

Closes #34905

---------

Co-authored-by: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com>
2026-09-22 10:26:59 -07:00
Jimmy Miller 8b0da1c6e4 [rust-compiler] Fix detection of shorthand and computed properties (#37674)
Before this would incorrectly mark certain computed properties as
shorthand. The babel code didn't care about this because of how it was
written, but the swc short circuited on shorthand. Causing incorrect
code generation
2026-09-22 09:13:10 -07:00
Jimmy Miller 59aff3e18c [compiler] Bail on use of arguments object (#37645)
Before this change, the compiler would incorrectly memoize the arguments
object. Causing rerenders to not work properly. I looked into adding
proper support, but it seemed it would require a decent amount of change
to do it cleanly, so I figured getting rid of the miscompilation was a
good start.

We had tests that were using it for compiler purposes, from what I can
tell changing to ...args kept the intent, so I moved them to that.
2026-09-18 11:10:50 +02:00
Rusty Raven 2b19aecd0e [rust-compiler] Fix exponentiation to match js semantics (#37587)
## Summary

The Rust port's constant propagation folds `**` with `f64::powf`, which
follows IEEE 754. IEEE `pow` returns `1` for `pow(1, NaN)` and `pow(±1,
±∞)`, but ECMAScript's
[`Number::exponentiate`](https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate)
returns `NaN` in those cases. The TS compiler folds with native `lhs **
rhs`, so the two backends disagree, and the Rust-compiled output changes
runtime behavior:

```js
const x = 1 ** (1 / 0); // JS: NaN, Rust-compiled: 1
```

This adds `js_exponentiate`, which returns `NaN` when `|base| == 1` and
the exponent is not finite, and otherwise defers to `powf`. `**=` lowers
to the same `BinaryOperator::Exponent`, so it is covered too.

To check the guard is neither too narrow nor too broad, I compared
`f64::powf` against JS `**` over all 225 pairs drawn from {NaN,
±Infinity, ±0, ±1, ±2, ±0.5, ±3, ±1e308}. The only mismatches were `1 **
NaN`, `1 ** ±Infinity` and `(-1) ** ±Infinity`. The condition also
matches `(-1) ** NaN`, where `powf` already returns `NaN`.

While in this file I noticed two places where `js_to_number` diverges
from `ToNumber`. I left them out to keep this PR focused, and can send a
follow-up if that's useful:

- `trimmed.parse::<f64>()` accepts `"inf"`, `"infinity"` and
`"INFINITY"`, so `"inf" == 1 / 0` folds to `true` (JS: `false`).
- The `0x`/`0o`/`0b` branches use `u64::from_str_radix`, which fails
past 64 bits (`"0xFFFFFFFFFFFFFFFFF" == 295147905179352830000` folds to
`false`, JS: `true`) and accepts a sign after the prefix (`"0x+10" ==
16` folds to `true`, JS: `false`).

## How did you test this change?

Added `constant-propagation-exponent-non-finite.js` with the five
mismatching cases plus five controls: `(-1) ** NaN`, `NaN ** 0`, `2 **
Infinity`, `0.5 ** Infinity` and `2 ** 10`. The snapshot was generated
with the TS compiler.

- Before the fix, `yarn snap --rust -p
constant-propagation-exponent-non-finite` fails on eval output: expected
`[null,null,null,null,null,null,1,null,0,1024]`, got
`[1,1,1,1,1,null,1,null,0,1024]`.
- With a narrower guard (`exponent.is_infinite()` instead of
`!exponent.is_finite()`) it still fails, only on `1 ** NaN`.
- With the fix, it passes on both backends.

I also ran the steps from `compiler_rust.yml` locally:

- `cargo check`, `cargo build`, `cargo test --workspace` (49 passed)
- `bash scripts/test-babel-ast.sh`
- `bash scripts/test-rust-port.sh`: 1816 passed, 0 failed (1815 on
`main`, before the new fixture)
- `yarn snap --rust` and `yarn snap`: 1817 passed, 0 failed
2026-09-16 12:35:58 +02:00
Srinu desetti 98cec2ec26 [compiler] Add regression test for useEffectEvent reading a variable declared later (#37618)
Fixes #37209

The stale value from the issue does not reproduce on main anymore.
The compiler leaves the useEffectEvent callback unmemoized, so it
always reads the latest value. I added the repro from the issue as
a test fixture so this stays covered.

The new fixture passes with yarn snap.
2026-09-16 10:53:44 +02:00
Yuri KarpenkovandPieter De Baets ff8f88fcd2 [compiler] Treat zero-argument new Date() as impure during render (#37576)
## Summary

`react-hooks/purity` already reports `Date.now()` during render, but not
`new Date()` / `new Date().getTime()` / `new Date().getFullYear()`.
Those also read the current clock. React's purity docs list `new Date()`
next to `Date.now()`.

Zero-argument `new Date()` is now treated as an impure call (same
diagnostic as `Date.now()`). Constructing from an explicit timestamp
stays allowed: `new Date(timestamp)`.

Fixes #37553

## How did you test this change?

- `yarn snap --pattern "**/*new-date*"` (4 fixtures passed)
- `yarn workspace eslint-plugin-react-compiler test --
ImpureFunctionCallsRule` (4 tests passed)


Made with [Cursor](https://cursor.com)

---------

Co-authored-by: Pieter De Baets <pieter.debaets@gmail.com>
2026-09-15 12:41:26 +02:00
Scott CooperandCodex 6e9c55c3a2 perf(compiler): Check component params before scanning bodies (#37622)
Backports https://github.com/oxc-project/oxc/pull/26451 to the Rust
React Compiler.

Check component parameters before scanning for hooks or JSX, so
functions with invalid component signatures skip the traversal.
Component classification stays the same.

Co-authored-by: Codex <noreply@openai.com>
2026-09-15 12:10:14 +02:00
Pieter De Baets d0a776cde3 [compiler] Support update expressions on captured variables (#37513)
## Summary

- support prefix and postfix update expressions on captured variables in
both TypeScript and Rust compiler backends
- represent updates with explicit `PrefixUpdateLocal`,
`PrefixUpdateContext`, `PostfixUpdateLocal`, and `PostfixUpdateContext`
HIR variants
- model captured updates as mutations so SSA, effect inference,
dead-code elimination, and post-render validation preserve their
semantics
- convert the existing TODO fixtures to passing coverage and add the
Airwave-shaped map regression from T286959188
- correct Rust workspace manifest paths in the rust-port documentation

## How did you test this change?

- `yarn prettier`
- `yarn linc`
- `yarn flow dom-node`
- `yarn test --silent --no-watchman React-hooks-arity`
- `yarn test-www --silent --no-watchman useMemoCache`
- `yarn test-www --variant=false --silent --no-watchman useMemoCache`
- `node_modules/.bin/tsc --noEmit -p
compiler/packages/babel-plugin-react-compiler/tsconfig.json`
- `cargo +1.93.1 fmt --manifest-path compiler/Cargo.toml --all --
--check`
- `cargo +1.93.1 check --manifest-path compiler/Cargo.toml -p
react_compiler -j 1`
- targeted Cargo tests for lowering, SSA, inference, optimization, and
validation
- TypeScript and Rust snapshot runs for `*update-expression*` (11 tests
each)
2026-09-11 11:22:39 +02:00
Masaharu Hori 78c2d377d7 [rust-compiler] Fix non-ASCII panic in ValidateNoSetStateInEffects (#37539)
## Summary

`get_identifier_name_with_loc` reads an identifier's name out of the
source when SSA has dropped it. `SourceLocation.index` is a Babel
position and counts UTF-16 code units, but the fallback used it to slice
a Rust `&str`, which indexes UTF-8 bytes:

```rust
let slice = &code[start_idx..end_idx];
```

These agree only while the source is ASCII. After any non-ASCII
character, later offsets are short by the extra bytes, so the slice
reads the wrong span, or panics when it lands inside a character:

```
panicked at crates/react_compiler_validation/src/validate_no_set_state_in_effects.rs:168:30:
start byte index 637 is not a char boundary; it is inside 'う' (bytes 636..639 of string)
```

The panic aborts validation for the whole file, so every diagnostic in
it is lost.

`validate_no_derived_computations_in_effects.rs` already converts
correctly in its own copy of this function. Both were added in #36173
and only one got the UTF-16 handling, so this ports that logic over.

Found through Biome, which embeds these crates for its
`useReactCompiler` rule.

## How did you test this change?

Added `repro-setState-in-effect-non-ascii-source.ts`, which panics
before this change and compiles cleanly after:

```
bash scripts/test-rust-port.sh ValidateNoSetStateInEffects \
  packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-setState-in-effect-non-ascii-source.ts
```

The non-ASCII comments in it are test input, not documentation; removing
them realigns the offsets and the crash disappears.

Everything `compiler_rust.yml` runs is green on macOS arm64, including
`scripts/test-rust-port.sh` (1811 passed) and `yarn snap --rust` (1812
passed).
2026-09-09 14:06:00 +02:00
Jimmy Miller 503efb4862 [rust-compiler] Fix js_to_int32 to match js semantics (#37545)
Before things like 1e21 | 0 would return -1 instead of -559939584 like
js expects. This should make this match in all cases.
2026-09-08 15:40:39 -07:00
Jimmy Miller f1f7ed2ac2 [rust-compiler] Preserve ref access location across phi joins (#37524)
Preserve the RefValue source location when joining mixed ref types so
validation errors point to the original ref access.

Before this change it used to show the incorrect location when using the
rust compiler.

```rust
  2 |   const ref = useRef(null);
  3 |   const x = cond ? ref : ref.current;
> 4 |   return <Foo value={x} />;
    |                      ^ Cannot access ref value during render
  5 | }
  6 |
```
2026-09-04 13:59:38 -07:00
Will Binns-Smith 0bbf02475c [rust-compiler] Propagate block lowering errors (#37364)
Propagate errors from block lowering instead of continuing with
incomplete HIR.

This prevents the Rust compiler from emitting partial output when it
encounters unsupported syntax, including nested TypeScript `this`
parameters.

This landed earlier as #37232, which exposed an existing FBT diagnostic
ordering issue. Resolve local FBT bindings before checking whether an
`<fbt>` tag comes from a module import, so the compiler reports the
earlier, more useful Todo instead of a later invariant.

Run from the repository root:

- `yarn --cwd compiler workspace babel-plugin-react-compiler-rust test`
- `cargo test --manifest-path compiler/Cargo.toml -p
react_compiler_lowering`
2026-09-02 13:28:52 -07:00
Andrew Imm d04798f2be React Compiler: move compiled function bodies into AST instead of cloning (#37376)
tl;dr reduces memory churn by 7.8%, allocations by 4.1%, wall time by
~4%

## Summary

When compiled functions are written back to the AST,
`apply_compiled_functions` took a reference to a slice, and deep-cloned
each compiled body out of it. In theTS version this step assigns
references through Babel paths, which are essentially free. The Rust
port translated that as `.clone()` for safety, deep-copying the entire
codegen output for every compiled function.

Nothing needs these bodies after they are inserted, and the caller
already owns the vector. So this takes `compiled_fns` by value and moves
the data into the AST instead:

* `ReplaceFnVisitor` holds an `Option<CodegenFunction>` and moves it to
whatever arm matches
* Outlined function declarations move their id+params+body out of
`codegen_fn.outlined` rather than cloning them
* `needs_memo_import` is computed before the loop that consumes the
vector. Only the computation moved; the block that registers the import
stays where it was, so ordering and identifier numbering don't change.

This doesn't fully eliminate clones, just ones where it's easy to do a
move instead.

## How did you test this change?

All compiler fixtures pass with byte-identical output
2026-09-01 22:11:26 -07:00
Andrew Imm 469c4ec4d3 React Compiler: Store aliasing values in an inline set (#37366)
97.6% of the value sets tracked per identifier in mutation / aliasing
inference hold exactly one element, but each was a `FxHashSet`, meaning
each was a heap allocation.
Because the inference code retains a full state in each basic block,
these single-element hashsets were a major contributor to peak memory
allocation.

This replaces them with a small inline set inspired by smolvec /
tinyvec. Five values are stored inline, and any more spill to the heap.
This was only needed in **0.02%** of sets in my data corpus.

This also makes iteration order match the TS implementation. TS uses
`Set` and iterates in insertion order; the Fx set iterated in hash
order. This brings the two behaviors in line.

| Benchmark | Peak allocation | Allocation count | Wall time |

|------------------|-----------------------------|------------------|-----------|
| legacy/image.tsx | 33.40 -> 28.07 MiB (-16.0%) | -66.7% | -28.8% |
| next-client | 33.40 -> 28.07 (-16.0%) | -42.0% | -15.7% |
| devtools | 16.29 -> 14.25 (-12.5%) | -19.9% | -7.3% |
| fixtures | 9.41 -> 7.90 (-16.0%) | -7.3% | -3.3% |
| next-examples | 4.85 -> 4.85 ( 0.0%) | -4.8% | -1.6% |
2026-09-01 22:10:29 -07:00
Will Binns-Smith fba23062c4 Add Rust compiler crate publishing workflows (#37465)
## Summary

- configure the React Compiler Rust crates as a Cargo workspace with
shared package metadata and versioned internal dependencies
- add workflows to open a signed version-bump pull request and publish
the workspace through crates.io trusted publishing
- add release scripts and contributor documentation for versioning,
validation, and publishing

## Test plan

- Run `cargo check --locked --workspace` from `compiler/`.
- Run **(Compiler) Publish Rust Crates** from the Actions tab with **Dry
run** enabled; confirm every workspace crate packages successfully
without publishing.
- Run **(Compiler) Update Rust Crate Version** with a test version;
confirm it verifies one shared version and opens a signed version-bump
pull request containing the updated workspace manifest and lockfile.
2026-09-01 10:04:34 -07:00
Will Binns-Smith 6c3bd60a90 Revert "[rust-compiler] Bail out on nested TypeScript this parameters (#37232)" (#37363)
This reverts commit 35e64cf2bc.
2026-08-24 15:52:03 -07:00
Will Binns-Smith 35e64cf2bc [rust-compiler] Bail out on nested TypeScript this parameters (#37232)
## Summary

Propagate errors returned while lowering block statements instead of
discarding them and continuing with partially built HIR.

The original issue was triggered by the SWC path, where a TypeScript
`this` pseudo-parameter remains in the AST but is omitted from
`ScopeInfo`. Lowering rejects the AST parameter, but the function-body
block wrapper previously swallowed that error and returned a partial
function.

```ts
function Component() {
  useEffect(() => {
    const get = (): Val => {
      window.value = {
        count: 0,
        method(this: Val) {},
      };
      return window.value;
    };
    get().count++;
  }, []);
}
```

This could emit a partial transform with the assignment removed:

```js
function Component() {
  useEffect(() => {
    const get = () => {
      return window.value;
    };
    get().count++;
  }, []);
}
```

## How did you test this change?

Added a [source-level reproduction against the SWC
adapter](https://github.com/wbinnssmith/swc/blob/114e9c55b2/crates/swc_ecma_react_compiler/src/tests/integration.rs#L1563-L1591)
using the same case above.

- With the current lowering crate, the test fails because the adapter
emits a partial program.
- With this change patched into the lowering dependency, the test passes
because compilation bails out.
- `cargo test --manifest-path compiler/Cargo.toml -p
react_compiler_lowering`
2026-08-24 15:23:00 -07:00
Andrew Imm b939280bb3 React Compiler: avoid deep ast clone and quadratic-time codegen (#37206)
tl;dr this reduces peak memory allocation by 5-15%, and reduces codegen
time by 30-70% depending on payload. On heavy components with deep ASTs
the impact is more exaggerated.

## Summary

Codegen of temp vars records the expressions they replaced, so that they
can be unwound. In the TS version this uses a `Map` and stores pointers
to AST nodes - relatively cheap. For borrow-checking reasons, the Rust
version clones the AST. This results in recurring deep clones, making
codegen accidentally quadratic and using significant amounts of memory.

This introduces a convenience data structure for emitting temp vars in
an unwindable manner, without heavy AST allocation.

It also avoids a separate AST deep clone when propagating null values.

## How did you test this change?

All fixtures pass with byte-identical outputs.

Ran this against real codebases and pathological benchmark cases,
confirming byte-identical output as well.
2026-08-24 13:38:48 -04:00
Andrew Imm 8c94e0e1d9 React Compiler: Avoid unnecessary clone in effect inference (#37207)
## Summary

In the mutation / aliasing inference, the state is used twice, and both
get cloned. Only one clone is necessary, the second can be safely moved.

This has minimal impact on peak memory usage, but it does reduce wall
time by *10%* on real Next.js benchmark apps.

## How did you test this change?

Ran against benchmark apps to confirm identical byte output.
Ran against the 1800+ Rust Compiler test fixtures.
2026-08-24 13:38:39 -04:00
Andrew Imm 05769e026b React Compiler: Make AbstractValue Copy to reduce heap allocation (#37225)
## Summary

By replicating the `IndexSet` behavior with a simple inline array, this
is able to avoid any heap allocation / memory thrash for
`AbstractValue`. It also reduces the size of AbstractValue from 72 bytes
+ all of the heap allocations, to only 18 bytes.

## How did you test this change?

Ran all of the fixtures to confirm byte output is identical.

Effects on memory and compile time:

| Benchmark | Peak allocation | Allocation count | Wall time |

|------------------|-----------------------------|------------------|-----------|
| legacy/image.tsx | 58.21 -> 33.40 MiB (-42.6%) | -51.1% | -39.5% |
| next-client | 58.21 -> 33.40 (-42.6%) | -40.5% | -25.1% |
| devtools | 26.39 -> 16.29 (-38.3%) | -26.7% | -14.8% |
| fixtures | 17.35 → 9.41 (-45.8%) | -9.9% | -9.0% |
2026-08-24 13:38:26 -04:00
laurenandCursor Agent 5a90a5a7a5 [compiler] Bail out with Todo on using and await using declarations (#36946)
## Summary

The compiler silently rewrote functions containing `using` and `await
using` declarations (explicit resource management). BuildHIR's
`VariableDeclaration` case only special-cases `var`; every other kind
falls through to `InstructionKind.Const`. A `using` declaration
therefore compiled as a plain memoized `const`, and the implicit dispose
call at scope exit was dropped from the compiled output. No diagnostic
was emitted. The Rust port mirrored the same bug in `build_hir.rs`.

The Rust port had a second, harder failure: its
`VariableDeclarationKind` enum had no `await using` variant, so a Babel
AST containing one failed serde deserialization at the NAPI boundary
("unknown variant `await using`") and threw for the whole file.

The fix records the same per-function Todo bail as the existing `var`
case, in both implementations, then continues lowering the declaration
as `const` so references do not break while the error unwinds (mirroring
how `var` continues as `let`). This matches the compiler's established
Todo-bail precedent for unsupported syntax: the function is skipped with
a logged `CompileError` diagnostic, `panicThreshold` is respected, and
sibling functions in the same file still compile. This deliberately
contrasts with oxc-project/oxc#24217, which handles the same syntax with
a silent per-function skip; nothing in this compiler is skipped without
a surfaced diagnostic.

Changes:

- TS: `BuildHIR.ts` records a Todo `CompilerErrorDetail` for `using` and
`await using` kinds, in the style of the existing `var` case.
- Rust: same Todo `record_error` in `build_hir.rs`. New `AwaitUsing`
variant (serde name `"await using"`) in `react_compiler_ast`, so `await
using` survives the NAPI boundary, covered by a serde round-trip unit
test.
- Test harness: enables the `explicitResourceManagement` parser plugin
in snap and in the e2e script's Babel baseline so fixtures can use the
syntax.

Fixtures (identical snapshots for the TS and Rust pipelines):

- `error.todo-using-declaration` and
`error.todo-await-using-declaration` show the Todo diagnostic with code
frames.
- `using-declaration-bailout-sibling-compiles` shows the bailing
function left untouched (disposal preserved) while a sibling component
in the same file is memoized.

The first commit snapshots the previous broken behavior (`using`
memoized as `const` with disposal dropped); the second commit lands the
fix and updates the snapshots.

## How did you test this change?

All results below are from the branch rebased onto current main.

TS implementation:

- `yarn snap`: 1809 tests, 1809 passed, 0 failed.
- `yarn workspace babel-plugin-react-compiler lint`: clean. `yarn
prettier-check` at the repo root: clean.
- Manually verified pre-fix output: a component with `using resource =
getResource(props.id)` compiled to a memoized `const` with no disposal.

Rust implementation:

- `cargo test --workspace`: 42 passed, 0 failed (includes the new serde
round-trip unit test for both `using` kinds).
- `bash scripts/test-babel-ast.sh`: passed.
- `bash scripts/test-rust-port.sh`: 1808 passed, 0 failed.
- `yarn snap --rust`: 1809 tests, 1809 passed, 0 failed, against the
same `.expect.md` snapshots as the TS run.
- `scripts/test-e2e.ts` (babel variant): the three using fixtures pass
code and events parity.
- Manually verified `await using` now deserializes across the NAPI
boundary and records the Todo instead of throwing.


<div><a
href="https://cursor.com/agents/bc-5fe4350d-5d42-4c0e-9001-429ed0b5e5b7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-web-light.png"><img
alt="Open in Web" width="114" height="28"
src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a
href="https://cursor.com/background-agent?bcId=bc-5fe4350d-5d42-4c0e-9001-429ed0b5e5b7"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source
media="(prefers-color-scheme: light)"
srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img
alt="Open in Cursor" width="131" height="28"
src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-07 20:46:37 -07:00
lauren 12a4baeca1 [compiler] Port JSX tag classification fix to Rust (not-lowercase is a component) (#36951) 2026-07-07 09:46:24 -07:00
Dmitrii fc08d76031 [compiler] Fix JSX tags prefixed with _ or $ incorrectly treated as host elements (#36688)
## Summary

Fixes #36601

### Problem

In `lowerJsxElementName` (BuildHIR.ts), the condition `if
(tag.match(/^[A-Z]/))` only treats JSX tags starting with an **uppercase
letter** as component references. Tags starting with `_`, `$`, or any
other non-letter character fell through to the `else` branch and were
incorrectly classified as `BuiltinTag` (host/intrinsic elements).

This meant that `<_Bar />` or `<$Foo />` was treated like `<div />`,
causing the compiler to skip memoization of the component and
potentially producing incorrect output.

### Root Cause

JSX semantics (as implemented by Babel's JSX transform) are:
- Tag starts with **lowercase** letter → host/intrinsic element (string
tag)
- **Everything else** → component reference (in-scope identifier)

The original code only handled the first half of that rule ("starts with
uppercase → component") while ignoring identifiers like `_Bar` and
`$Foo`.

### Fix

Change:
```ts
if (tag.match(/^[A-Z]/)) {
```
To:
```ts
if (!tag.match(/^[a-z]/)) {
```

This correctly classifies any JSX identifier that does NOT start with a
lowercase letter as a component reference, matching JSX spec semantics.

### Test

Added fixture `jsx-underscore-prefix-component` that renders `<_Bar />`
and verifies the compiler correctly memoizes it as a component
reference.

## How did you test this change?

- Added new compiler fixture test: `jsx-underscore-prefix-component`
- Ran `yarn workspace babel-plugin-react-compiler lint` ✅
- Ran snap tests with the new fixture to generate expected output ✅
2026-07-06 22:24:54 -07:00
gtkatakura 08725f2132 [compiler]: add useWindowVirtualizer to known incompatible libraries (#36912)
## Summary

#34493 added `@tanstack/react-virtual`'s `useVirtualizer` to the known
incompatible libraries, but not `useWindowVirtualizer`.

Both hooks are thin wrappers around the same internal
`useVirtualizerBase`, so they return the same referentially-stable
virtualizer instance. Its methods (e.g. `getVirtualItems()`,
`getTotalSize()`) return internally-mutated values rather than new ones,
which is exactly the "interior mutability" pattern that breaks
memoization described in the module type provider.

Because only `useVirtualizer` was registered, code using
`useWindowVirtualizer` is silently compiled with the same unsafe
memoization. In our app this froze `getVirtualItems()` to its
first-render value (an empty `[]` before measurement), producing
permanently-empty virtualized lists — the same class of bug #34493
fixed, just via the window variant.

This registers `useWindowVirtualizer` alongside `useVirtualizer` with
the identical shape, in **both** implementations:
-
`compiler/packages/babel-plugin-react-compiler/src/HIR/DefaultModuleTypeProvider.ts`
-
`compiler/crates/react_compiler_hir/src/default_module_type_provider.rs`
(Rust port)

See also the community report referenced in #34493:
https://github.com/TanStack/virtual/issues/736#issuecomment-3065658277

## How did you test this change?

Each addition mirrors the existing `useVirtualizer` entry exactly (same
kind, params, return type, and `knownIncompatible` message shape); both
are data-only additions to the module type providers, following the
precedent set by #34493 which changed only the TS file. The Rust change
keeps the two providers in sync.
2026-07-06 16:54:22 -07:00
Michael Vitousekandmvitousek 7a7e893c50 [compiler] Fix failing Rust compiler test case for todo-locally-require-fbt (#36767)
Two fundamental changes (plus a lot of formatting that got mixed in :/
): Modify how we determine if a local binding exists for the case where
the scope is extracted from a function, rather than a module, which is
the case for snap tests. And, loosen the rust port tester to allow debug
output to be printed from one compiler or another as long as both sides
error in the same phase with the same error message (basically, the rust
port emits debug information before throwing an error, whereas the TS
version does one or the other -- but it's not actually a real
difference).

With this change and loosening, the Rust compiler conforms on the
todo-locally-require-fbt case.

---------

Co-authored-by: mvitousek <mvitousek@devvm12588.pnb0.facebook.com>
2026-07-02 00:16:29 -05:00
Pieter De Baets 9c1f0977db [compiler] Restore code frames in ESLint compiler error messages (#36901)
## Summary

The Rust port (#36173) changed `CompileError` `LoggerEvent`s to carry
plain serialized detail objects instead of
`CompilerError`/`CompilerDiagnostic` class instances. As a result, the
ESLint integrations could no longer call
`detail.printErrorMessage(source, {eslint: true})` and were given a
replacement `printErrorMessage()` helper that only emitted the `reason`
and `description`.

This regressed error printing: the **source code frame(s) and
`file:line:column` location** that used to appear for each error detail
were dropped from lint output.

This PR restores the previous behaviour:

- Export `printCodeFrame` from `CompilerError` and reuse it from both
ESLint integrations instead of duplicating it.
- Rebuild the full message (reason, description, per-detail code frames,
and hints) in `printErrorMessage`.
- Handle **both** detail shapes that flow through `LoggerEvent`s:
- a `details` array (`CompilerDiagnostic` and the **Rust** compiler),
and
  - a legacy flat `loc` (deprecated `CompilerErrorDetail`).

`formatDetailForLogging` emits one or the other, so the previous
unconditional iteration over `error.details` would have thrown
`TypeError: not iterable` on the flat-`loc` path. Normalizing to a list
fixes that and keeps the code working with both the TypeScript and Rust
compilers.

## Test plan

- `tsc --noEmit` on both ESLint packages is clean (no new errors vs.
baseline).
- Verified the detail loop handles the Rust compiler's `details` array
shape and the legacy flat `loc` shape.
2026-06-30 10:20:26 +01:00
Boshen 06b2a50f32 [rust-compiler] Use rustc-hash FxHasher for all maps and sets (#36811)
Swaps the default hashers across the Rust compiler crates for
rustc-hash's faster `FxHasher`.

## Why

`FxHasher` is a simple, fast, non-cryptographic hasher. std's default
(SipHash) is DoS-resistant but heavy — the compiler keys maps/sets
almost entirely on small integer IDs (`IdentifierId`, `BlockId`,
`ScopeId`, …) where that protection buys nothing. Switching gives:

- **Performance** — faster hashing on the hot ID-keyed maps/sets used
throughout the passes.
- **Slight binary size reduction** — drops the heavy SipHash hasher
pulled in by std's `HashMap`/`HashSet`.

## Changes

- `std::collections::HashMap`/`HashSet` →
`rustc_hash::FxHashMap`/`FxHashSet`
- `indexmap` `IndexMap`/`IndexSet` now use `FxBuildHasher`, inlined as
`IndexMap<K, V, FxBuildHasher>` (no `FxIndexMap` type aliases)
- `rustc-hash = "2"` added to all 12 crate manifests
- `rustc_hash`/`indexmap` imports consolidated into single brace imports
at the top of each file

The change is serde-neutral: `FxHashMap` and `IndexMap<_, _,
FxBuildHasher>` serialize to byte-identical JSON, and `IndexMap` keeps
insertion order.

`cargo check`, `cargo fmt --check`, and `cargo test` all pass with no
warnings.
2026-06-22 14:30:43 -07:00
Boshen 560db51408 [rust-compiler] Switch to hmac-sha256 and bump napi/similar (#36809)
## Summary

Dependency maintenance for the Rust compiler crates.

### Fast Refresh hash: sha2 + hmac → hmac-sha256
The Fast Refresh source hash (`enableResetCacheOnSourceFileChanges`)
only needs an HMAC-SHA256 of the source text, matching the TS compiler's
`createHmac('sha256', code).digest('hex')`. RustCrypto's `sha2` + `hmac`
pulled in **11 crates** (`sha2`, `hmac`, `digest`, `block-buffer`,
`typenum`, `crypto-common`, `hybrid-array`, `const-oid`, `cpufeatures`,
`cmov`, `ctutils`) — generic-hashing / constant-time machinery that is
irrelevant for a non-security content fingerprint. This replaces them
with the single **zero-dependency** `hmac-sha256` crate: a net reduction
of **10 crates**.

HMAC-SHA256 is a standardized deterministic algorithm, so the emitted
hash is unchanged. A new unit test
`source_file_hash_matches_node_create_hmac` pins the result against
Node's `createHmac('sha256', code).digest('hex')` for several inputs.

### Other bumps
- `napi` / `napi-derive` 2 → 3
- `similar` 2 → 3 (dev-dependency)
- Drop the unused `react_compiler_lowering` dependency from
`react_compiler_ssa`

`cargo check --workspace --all-targets` passes, including the napi 3
native crate.
2026-06-17 16:07:24 -07:00
lauren 0ca9d20772 [compiler] Count loop reassignments as the enclosing scope reassigning the variable (#36732)
A counter initialized before a memo scope and incremented inside it
(`a++` or `a = a + 1` in a `for` body) was emitted as a scope
*dependency*, compared at its pre-loop value (constant every render)
while the cache stored its post-loop value. The memo could never hit, so
the scope recomputed on every render.

Root cause: the phi-union rule in
`InferReactiveScopeVariables.findDisjointMutableValues` only unioned a
phi into the scope when the phi value was mutated after creation, which
range-extension only does for object mutation. Primitive reassignments
around a loop back-edge never extend ranges, so the counter's SSA
versions stayed outside the scope and downstream dependency propagation
classified the pre-loop read as a dep.

The fix unions a phi with its operands and declaration when any operand
is defined at or after the phi's block, i.e. the value is reassigned
around a loop back-edge. This matches the shape the compiler already
produced for non-primitive loop reassignment (`x = [...x, i]`).
Implemented identically in the TypeScript compiler and the Rust port.

Both `a++` and `a = a + 1` variants are pinned by fixtures; the first
commit documents the previously-wrong codegen, the second fixes it
(counter becomes a scope output, dep on `count` only).

Corpus delta beyond the new fixtures is 4 fixtures, all strict
improvements with byte-identical eval output: `for-in-statement-break`,
`for-in-statement-continue`, `for-in-statement-type-inference` (loops
previously re-ran every render, now memoized), and `sequence-expression`
(two memo blocks collapse into one).

Known limitation, unchanged from before: conditional reassignment in a
loop (`for (...) { if (c) a++; }`) routes through a join phi that this
rule cannot see; that shape behaves as it did before this change.

Verification: TS snap 1806/1806, Rust snap 1806/1806, cargo workspace
green, scoped TS-vs-Rust HIR parity harness green.

Closes #34971
2026-06-17 15:54:14 -07:00
lauren 0038f63c0d [rust-compiler] Represent string values as JsString (WTF-16 aware) (#36731)
Last of the boundary trilogy, after #36729 and #36730 (both merged);
rebased onto main as a standalone 3-commit change.

JS strings are WTF-16: a lone surrogate in source (`"\uD83E"`) is a
legal string value, but Rust `String` is UTF-8 and cannot hold it. The
compiler previously leaned on the napi bridge's `__SURROGATE_XXXX__`
marker encoding end to end, so the core compiler compared, concatenated,
and constant-folded marker text as if it were the actual string value.

`JsString` (in `react_compiler_diagnostics`, our lowest layer) holds the
common well-formed case as a plain UTF-8 `String` with zero overhead and
falls back to UTF-16 code units only for ill-formed values.
`StringLiteral.value` and `PrimitiveValue::String` carry it through the
pipeline; constant folding concatenates via code units so split
surrogate halves re-pair exactly as they do in JS; markers are emitted
only at the napi edge, where the babel bridge requires them (serde_json
can neither parse nor emit a lone `\uXXXX` escape).

The representation is encapsulated behind an opaque struct (private
`Repr` enum, borrowed `JsStringRef` view via `as_ref`) so the
"well-formed values are always UTF-8" invariant that makes the derived
`PartialEq`/`Hash` sound holds by construction, per review feedback.

The marker decoder scans byte-wise (an earlier draft range-sliced at
fixed offsets and panicked on multibyte UTF-8 following `__SURROGATE_`),
validates hex digits, and accepts uppercase only, exactly mirroring what
the bridge emits; lowercase marker-shaped user text survives verbatim.
The HIR debug printer renders unpaired surrogates as `\uXXXX` escapes
byte-identical to the TS printer.

This closes the lone-surrogate divergence on the e2e harness. Verified
on the rebased branch: cargo workspace tests, Rust snap channel
1804/1804, and HIR + Code parity on the lone-surrogate fixture through
the comparison harness.
2026-06-15 23:43:55 -07:00
Sebastian "Sebbie" Silbermann dbc37501ff Update required references to GitHub repo (#36752) 2026-06-12 09:50:15 +02:00
Michael Vitousekandmvitousek d3da200820 [compiler] Remove OXC and SWC plugins in favor of them being handled by those projects (#36743)
I believe the consensus is that the OXC and SWC plugins should live in
those projects, and instead consume the React compiler as a crate

---------

Co-authored-by: mvitousek <mvitousek@devvm12588.pnb0.facebook.com>
2026-06-11 15:41:04 -07:00
laurenandBoshen b49e04151e [rust-compiler] Carry uninspected AST subtrees as raw JSON text (#36730)
Stacked on #36729 (upstream rejects cross-fork base branches, so this
targets main as a draft; the first commit belongs to the parent PR.
Review the last three commits. Will rebase and mark ready when #36729
lands.)

Unmodeled AST subtrees (type annotations, class bodies, unknown
statements) were stored as `serde_json::Value` trees: every node
allocated through a `Map<String, Value>`, and pass-through subtrees were
repeatedly traversed by code that never looks inside them. They are now
`RawNode`, a newtype over `Box<RawValue>` holding the original JSON text
verbatim.

Design notes, since two obvious alternatives fail:
- Bare `RawValue` fields break under `#[serde(tag = "type")]` enums:
internally-tagged deserialization buffers content into serde's private
`Content` tree, which `RawValue` cannot read from.
`RawNode::deserialize` instead streams whatever deserializer it is
handed through `serde_transcode` into a fresh JSON string, which works
behind tagged enums, `flatten`, and `from_value` alike.
- Default-limit reparses break deep ASTs: internal `RawNode` reparse
sites use `from_json_str_unbounded` (disables serde_json's 128-level
recursion limit, matching how the top-level parse is configured);
regression-tested with a 400-deep statement chain.

`parse_value` fails loudly on malformed text rather than masking
corruption with `Value::Null`; RawNode holds valid JSON by construction.

Size-neutral in the shipped binary; the win is structural (no
speculative `Value` trees on the hot path, pass-through subtrees stay
untouched text).

Verified on this exact tree: cargo workspace tests, both snap channels
1804/1804.

---------

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-06-10 20:19:43 -07:00
laurenandBoshen ae6fe8a3e1 [rust-compiler] Return the compiled AST by value instead of JSON (#36729)
`compile_program` serialized the transformed `File` to a JSON string and
every in-process consumer immediately deserialized it back, so each
oxc/swc compile paid a full serialize/parse round-trip of the entire
program for nothing. `CompileResult::Success.ast` is now
`Option<react_compiler_ast::File>`; the napi bridge keeps its JSON
boundary by serializing at the edge, while the oxc and swc frontends
consume the typed AST directly.

This ports the typed-AST patch the Oxc team carries on their fork
(co-authored with Boshen), which also makes their integration patch-free
going forward.

Verified: cargo workspace tests, both snap channels 1804/1804, e2e
comparison harness at parity baseline (1802 byte-identical, fbt
local-require the one known divergence).

First of a stack of three with #36730 and #36731.

Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
2026-06-10 20:19:29 -07:00
lauren 34b78a2897 [rust-compiler] Drop the regex dependency from the napi binary (#36727)
The regex crate services exactly one pattern, the dynamic-gating
directive `^use memo if\(([^\)]*)\)$`, while costing ~570KB of `.text`
(regex + regex_automata + regex_syntax + aho_corasick) in the shipped
binary; after LTO the removal saves ~1MB through dead-code cascade.

Replaced with an exact hand parse: strip the `use memo if(` prefix and
`)` suffix, reject conditions containing a close paren. Equivalence with
the TS `DYNAMIC_GATING_DIRECTIVE` regex verified on the full gating
fixture directory: 30/30 byte-identical TS-vs-Rust on the e2e comparison
harness, dynamic-gating snap fixtures green.

Independent of #36726; the two compose to take the default release
binary from 11.2MB to 6.1MB.
2026-06-09 15:38:32 -07:00
lauren 7e71552e49 [rust-compiler] Add release profile: fat LTO, one codegen unit, strip (#36726)
The workspace has no `[profile.release]`, so the shipped napi binary
(`index.node`) is built on stock cargo defaults with the symbol table
retained: 11.2MB on arm64 macOS. Fat LTO + `codegen-units = 1` + `strip
= true` lands at 7.2MB with no runtime cost. Release builds get slower;
debug builds (`snap --rust`, the e2e harness) are unaffected.

`profile-rust-port.ts --release` overrides `strip` so profiling runs
keep symbol names.

Measured (arm64 macOS, `cargo build --release -p react_compiler_napi`):

| profile | size |
|---|---|
| defaults | 11.2MB |
| this PR | 7.2MB |

`opt-level = "s"` was measured and rejected: it cuts a further 2MB but
costs ~50% compile throughput (3.9s to 5.85s median compiling 598
fixtures through the release CLI).
2026-06-09 15:38:10 -07:00
03e775554a [compiler] Port React Compiler to Rust (#36173)
This is an experimental, work-in-progress port of React Compiler to
Rust. Key points:

* Work-in-progress - we are sharing early, prior to testing internally
at Meta, to get feedback from partners in parallel with continued
development.
* No builds available yet, you'll have to do some hacking if you want to
try this.
* All fixtures pass, no known gaps but there may be lurking bugs.
* The architecture was heavily guided by humans (me, @josephsavona) but
majority coded by AI. I was very hands-on in setting the architecture,
the testing and verification strategy, incremental migration approach,
etc. I also kept a close eye on the code and spent a decent amount of
time going back and forth to get code quality to a decent level.
* The public API is basically "Rust Babel AST" + Scope Info in, Rust
Babel AST out. We use a Rust representation of the Babel AST as our
"public API", as it were, and then each integration (Babel, OXC, SWC)
converts to/from their native representation. For now integrations must
also provide scope information - in the future React Compiler may
compute bindings and references itself from the AST.
* Internally, the Rust version uses the same architecture as the
TypeScript version. The compiler converts from the AST into our own
intermediate representation (HIR, short for High-level Intermediate
Representation) which uses a control-flow graph (CFG) and single-static
assignment (SSA). We go through the same series of passes, with the same
overall algorithms. It's very much a pass-by-pass port. The main
differences are in the data representation - using arena-like structures
(and indices into these arenas) to work within Rust's borrowing system.
* Early performance numbers are derived from AI and i haven't spent much
time validating the benchmark setup, beyond the fact that the
optimization opportunities it discovered made complete sense and the
fixes were right. With that caveat, itt does appear that the Rust
version is quite fast already: 3x faster when operating as a Babel
plugin. The serialization cost is quite high, but the actual
transformation logic is ~10x faster, so it's net faster. Native
integrations (oxc, swc) should be even faster.
* There are 3 integrations right now: an alternative Babel plugin (which
will eventually get removed as we integrate into
babel-plugin-react-compiler), and examples of what OXC and SWC
integrations could look like (see react_compiler_oxc and
react_compiler_swc crates).

correctness:
* all 1725 fixtures pass in snap when comparing the temporary rust
version of the plugin with the main version. this compares generated
code output as well as errors.
* all fixtures also pass a full comparison of the per-pass compiler
intermediate representation — the intermediate state (including log
events and errors) are ~identical after every single pass (modulo some
normalization of ids)
* The OXC and SWC example integrations seem to be working well, though i
haven't manually verified this to the same extent as i have the Babel
integration.

development:
* `yarn snap --rust` is the primary test suite, testing that we error or
compile as expected. It does not test the inner state of the compiler
along the way, though, making it less suitable for finding subtle logic
gaps btw the TS and Rust versions. It's also Babel based, making it less
easy to test OXC and SWC integrations.
* `compiler/scripts/test-e2e.sh` is an e2e test of all 3 variants (babel
wrapper around Rust, OXC/SWC integrations) against the TS
implementation. This does a partial comparison, focused on final output
code only (doesn't test error details etc). Useful for getting the swc
and oxc integrations closer to parity.
* `compiler/script/test-rust-port.sh` does detailed testing of the
internal compiler state after each pass, in addition to checking the
final output code. This is the key script used to port the compiler,
ensuring not just that the output was the same but that each pass was
capturing all the same detail. This script can be pointed at *any*
directory of JS files, which we expect to use for internal testing at
Meta.


## For Partners

We're excited to partner with teams to integrate the Rust version of
React Compiler into other tools, like OXC and SWC. If you're interested
in working with us on this, the best place to start is by taking a look
at the react_compiler_swc and react_compiler_oxc crates. These give you
an idea of the API shape that we're thinking of.

Note that the conversion from any AST into our HIR is complex, and we
can only maintain one version. Hence we've aligned on using a Babel-like
AST as our public API. Another key point is that we don't yet implement
our own scope analysis (since the TS version of the compiler relied on
Babel's scope analysis), so for now we require that the scope data be
serialized. It's a denormalized graph, and some metadata has to be
stored to associate nodes with scopes. We're open to feedback about the
AST and scope representation - we iterated a bit just to get things to
work, but it can be more optimal.

Key changes that we are considering:
* Currently the compiler returns `Option<Program>`, which is `Some` if
anything changed. This requires replacing the entire program. We plan to
change this to return a series of patches to apply, in a form that is
reasonably usable and efficient for all the integrations we care about
(Babel, OXC, SWC, etc).
* The Rust representation of the Babel AST is fine enough, but we could
make it more optimal by doing arena allocation. We also plan to change
the string representation to smol_str.
* The scope representation, and association of data btw AST and scope,
is very much a first pass approach that is good enough. We expect to
implement our own scope resolution, though, so we hopefully won't need
to iterate on the scope representation and can just throw it away.

In terms of the shape of the integration, we anticipate that each
integration would have the following:

* Implementor repo (OXC, SWC, etc): lightweight code transform and lint
pipeline integration that delegates to `crates/react_compiler_<name>`
from our repo
* Our repo: one crate per implementor, eg react_compiler_swc,
react_compiler_oxc, where most of the logic lives.

This setup lets us make changes to the integration layer easily within
our repo. Feedback appreciated!

---------

Co-authored-by: Joe Savona <joesavona@meta.com>
Co-authored-by: Mike Vitousek <mvitousek@fb.com>
Co-authored-by: Mike Vitousek <mvitousek@meta.com>
Co-authored-by: lauren <poteto@users.noreply.github.com>
Co-authored-by: lauren <lauren@anysphere.co>
2026-06-09 14:53:06 -07:00
lauren 008a6d4aeb [compiler] Don't emit spurious import { c as _c } for discarded functions (#36500)
## What

Codegen registers `_c` (the memo cache import) as a side effect whenever
a function compiles with memo slots. The registration persists on
`ProgramContext.imports` even if the function is later discarded (`'use
no forget'`, `'use no memo'`, lint mode, validation errors). If other
applied functions in the file compile to 0 memo slots, the stale `import
{ c as _c } from "react/compiler-runtime";` leaks into the output.

## Fix

In `applyCompiledFunctions`, drop the memo cache import if no applied
function uses memo slots. If `react/compiler-runtime` has no remaining
specifiers, drop the module entry too so we don't emit a bare `import
"react/compiler-runtime";`.

## Reproducer

`use-no-forget-multiple-with-eslint-suppression.js`:

```js
import {useRef} from 'react';

const useControllableState = options => {};
function NoopComponent() {}

function Component() {
  'use no forget';
  const ref = useRef(null);
  // eslint-disable-next-line react-hooks/rules-of-hooks
  ref.current = 'bad';
  return <button ref={ref} />;
}
```

`NoopComponent` applies with 0 memo slots. `Component` is opted out, but
codegen already registered `_c` for it. Before:

```js
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";
```

After:

```js
import { useRef } from "react";
```

## Prior art

TS counterpart to the Rust port's fix in
7e26eb89fc. That commit also added
`no-cache-slots-no-import.js`, which codifies the "no memo slots, no
import" rule.

## Test plan

- `yarn snap`: 1719/1719 passing
- Snapshot for `use-no-forget-multiple-with-eslint-suppression` loses
its spurious `_c` import
- `no-cache-slots-no-import` still passes
2026-05-20 21:57:55 -07:00
Dmitrii 808e7ed8e2 [compiler] Fix set-state-in-effect false negative with NewExpression default param (#36107)
## Summary

Fixes #36101

When a component function has a destructured prop with a `NewExpression`
default value (e.g. `{ value = new Number() }`), the React Compiler
bails out during HIR construction when trying to lower the default value
via `lowerReorderableExpression`. This causes
`validateNoSetStateInEffects` to never run, silently suppressing the
`set-state-in-effect` diagnostic.

**Root cause:** `isReorderableExpression` did not have a case for
`NewExpression`, so it fell through to the `default: return false`
branch. `lowerReorderableExpression` then recorded a `Todo` error and
aborted compilation of the function before any validation passes ran.

**Fix:** Add a `NewExpression` case to `isReorderableExpression` that
mirrors the existing `CallExpression` case — the expression is safe to
reorder when the callee and all arguments are themselves reorderable
(e.g. global identifiers and literals).

## How did you test this change?

Added a new compiler fixture
`invalid-setState-in-useEffect-new-expression-default-param` that
reproduces the bug from the issue. The fixture verifies that the
`EffectSetState` diagnostic is correctly emitted for a component with a
`NewExpression` default prop value.

All 1720 compiler snapshot tests pass.
2026-04-08 14:52:49 -04:00
ALİ DENİZ TARTMA 044d56f390 docs: fix typos and improve abbreviation usage in DESIGN_GOALS.md (#36170)
Hi! While reviewing the React Compiler documentation, I noticed a few
minor issues in DESIGN_GOALS.md:


- Fixed a typo: `outweight` → `outweigh` in the Non-Goals section.

- Updated all instances of `ie` to the standard `i.e.` for better
consistency and clarity throughout the document.


Happy to contribute!

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

Fixed a typo (outweight -> outweigh) and standardized abbreviation usage
(ie -> i.e.) in the DESIGN_GOALS.md file for the React Compiler
documentation. This improves the overall professionalism and readability
of the document.

## How did you test this change?

This is a documentation-only change. I verified the formatting and
consistency of the edits.
2026-03-30 16:25:51 -07:00
mofeiZ 2c2fd9d12c [compiler][playground] parse compiler configs using json5 (#36159)
Compiler config parsing is currently done with new Function(...) which
is a XSS vulnerability. Replacing this with json parsing for safety
reasons.

Almost all compiler options (except for moduleTypeProvider) are json
compatible, so this isn't a big change to capabilities. Previously
created playground URLs with non-default configs may not be compatible
with this change, but we should be able to get the correct config
manually (by reading the JS version)
2026-03-30 13:04:50 -04:00
o-m12a 677818e4a2 Fix typos in tests and comments (#35627)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

I just fixed typos as followings.

- `succesful` → `successful`
- `becuase` → `because`
- `enought` → `enough`
- `defualt` → `default`

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->

This PR only includes test case description, dummy strings for test, and
comments updates, so it has no impact on runtime behavior.
Therefore, I manually reviewed changed texts to ensure correctness.
2026-03-27 14:53:32 -07:00
Bodhi Russell Silberling 2233b7d728 Fix typos: explicitlyu->explicitly, intialized->initialized (#35621)
Fixed spelling errors:
- Fixed 'explicitlyu' -> 'explicitly' in compiler/CLAUDE.md
- Fixed 'intialized' -> 'initialized' in InferReactiveScopeVariables.ts
(comment)
- Fixed 'intialized' -> 'initialized' in InferMutationAliasingEffects.ts
(error message)

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
2026-03-27 14:52:23 -07:00
Bodhi Russell Silberling ba833da405 Fix typo: accomodate -> accommodate (#35623)
Fixed spelling error in comment:
- Fixed 'accomodate' -> 'accommodate' in InferMutationAliasingEffects.ts

<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
2026-03-27 14:51:37 -07:00
Rahul salunkeandYummy_Bacon5 c0c29e8906 Fix typos in the documentation (#35439)
<!--
  Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.

Before submitting a pull request, please make sure the following is
done:

1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
  2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
  9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
  10. If you haven't already, complete the CLA.

Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->

## Summary

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
So in this PR the typo mistakes in the docs are corrected such as the 
1. **Ie** it should be **"i.e"**.
2. **errros** should be the **"errors"**.
3. **consdier** should be the **"consider"**.
4. **CreatFrom** should be **"CreateForm"**.


## How did you test this change?

<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
  If you leave this empty, your PR will very likely be closed.
-->
I verified the fixes by reviewing the updated files locally to ensure
the corrected terms appear consistently and accurately in the
documentation.

---------

Co-authored-by: Yummy_Bacon5 <68166338+YummyBacon5@users.noreply.github.com>
2026-03-24 16:09:59 -07:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> a74302c02d Bump undici from 6.21.2 to 6.23.0 in /compiler (#35512)
Bumps [undici](https://github.com/nodejs/undici) from 6.21.2 to 6.23.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/nodejs/undici/releases">undici's
releases</a>.</em></p>
<blockquote>
<h2>v6.23.0</h2>
<h2>⚠️ Security Release</h2>
<p>This fixes <a
href="https://github.com/nodejs/undici/security/advisories/GHSA-g9mf-h72j-4rw9">https://github.com/nodejs/undici/security/advisories/GHSA-g9mf-h72j-4rw9</a>
and CVE-2026-22036.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nodejs/undici/compare/v6.22.0...v6.23.0">https://github.com/nodejs/undici/compare/v6.22.0...v6.23.0</a></p>
<h2>v6.22.0</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: fix wrong stream canceled up after cloning (v6) by <a
href="https://github.com/snyamathi"><code>@​snyamathi</code></a> in <a
href="https://redirect.github.com/nodejs/undici/pull/4414">nodejs/undici#4414</a></li>
<li>[Backport v6.x] fix: fix EnvHttpProxyAgent for the Node.js bundle by
<a
href="https://github.com/github-actions"><code>@​github-actions</code></a>[bot]
in <a
href="https://redirect.github.com/nodejs/undici/pull/4432">nodejs/undici#4432</a></li>
<li>feat(ProxyAgent): match Curl behavior in HTTP-&gt;HTTP Proxy
connections (<a
href="https://redirect.github.com/nodejs/undici/issues/4180">#4180</a>)
by <a href="https://github.com/metcoder95"><code>@​metcoder95</code></a>
in <a
href="https://redirect.github.com/nodejs/undici/pull/4433">nodejs/undici#4433</a></li>
<li>feat(ProxyAgent) improve Curl-y behavior in HTTP-&gt;HTTP Proxy
connections (<a
href="https://redirect.github.com/nodejs/undici/issues/4180">#4180</a>)
(<a
href="https://redirect.github.com/nodejs/undici/issues/4340">#4340</a>)
by <a href="https://github.com/metcoder95"><code>@​metcoder95</code></a>
in <a
href="https://redirect.github.com/nodejs/undici/pull/4445">nodejs/undici#4445</a></li>
<li>Backport 4472 to v6.x by <a
href="https://github.com/Uzlopak"><code>@​Uzlopak</code></a> in <a
href="https://redirect.github.com/nodejs/undici/pull/4480">nodejs/undici#4480</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nodejs/undici/compare/v6.21.3...v6.22.0">https://github.com/nodejs/undici/compare/v6.21.3...v6.22.0</a></p>
<h2>v6.21.3</h2>
<h2>What's Changed</h2>
<ul>
<li>[Backport v6.x] append crlf to formdata body by <a
href="https://github.com/github-actions"><code>@​github-actions</code></a>
in <a
href="https://redirect.github.com/nodejs/undici/pull/4210">nodejs/undici#4210</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/nodejs/undici/compare/v6.21.2...v6.21.3">https://github.com/nodejs/undici/compare/v6.21.2...v6.21.3</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/nodejs/undici/commit/fbc31e21d7e1dffea61166ab7a827f74b6483d26"><code>fbc31e2</code></a>
Bumped v6.23.0</li>
<li><a
href="https://github.com/nodejs/undici/commit/3477c948c30dd44a6431230ce67dd5d216cd0fdb"><code>3477c94</code></a>
chore: release flow using provenance</li>
<li><a
href="https://github.com/nodejs/undici/commit/d3aafea7a2b3c351970c0c634b7cba8231763ca4"><code>d3aafea</code></a>
fix: limit Content-Encoding chain to 5 to prevent resource
exhaustion</li>
<li><a
href="https://github.com/nodejs/undici/commit/f9c91853e7a73d8148e3d2914f8200dd160dd050"><code>f9c9185</code></a>
Bumped v6.22.0</li>
<li><a
href="https://github.com/nodejs/undici/commit/f670f2a27970abfd6c5b56e692f025067824726f"><code>f670f2a</code></a>
feat: make UndiciErrors reliable to instanceof (<a
href="https://redirect.github.com/nodejs/undici/issues/4472">#4472</a>)
(<a
href="https://redirect.github.com/nodejs/undici/issues/4480">#4480</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/422e39771877f62737f9e5fbdd336aaa22610a5d"><code>422e397</code></a>
feat(ProxyAgent) improve Curl-y behavior in HTTP-&gt;HTTP Proxy
connections (<a
href="https://redirect.github.com/nodejs/undici/issues/41">#41</a>...</li>
<li><a
href="https://github.com/nodejs/undici/commit/4a06ffe61fa11028a4443974ec0b0a793ee6c836"><code>4a06ffe</code></a>
feat(ProxyAgent): match Curl behavior in HTTP-&gt;HTTP Proxy connections
(<a
href="https://redirect.github.com/nodejs/undici/issues/4180">#4180</a>)...</li>
<li><a
href="https://github.com/nodejs/undici/commit/4cb397400e319505647e1705f535848db5949c18"><code>4cb3974</code></a>
fix: fix EnvHttpProxyAgent for the Node.js bundle (<a
href="https://redirect.github.com/nodejs/undici/issues/4064">#4064</a>)
(<a
href="https://redirect.github.com/nodejs/undici/issues/4432">#4432</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/44c23e5e166a30dd57eed47f1d4911b8ba77ce89"><code>44c23e5</code></a>
fix: fix wrong stream canceled up after cloning (v6) (<a
href="https://redirect.github.com/nodejs/undici/issues/4414">#4414</a>)</li>
<li><a
href="https://github.com/nodejs/undici/commit/da0e823ac0e89390256d61c429df0cf236afb79e"><code>da0e823</code></a>
Bumped v6.21.4</li>
<li>Additional commits viewable in <a
href="https://github.com/nodejs/undici/compare/v6.21.2...v6.23.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for undici since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=undici&package-manager=npm_and_yarn&previous-version=6.21.2&new-version=6.23.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/facebook/react/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-12 08:40:37 -07:00
Joseph Savona 6b113b7bd1 [compiler] Deduplicate errors between ValidateExhaustiveDependencies and ValidatePreservedManualMemoization (#35917)
With the recent changes to make the compiler fault tolerant and always
continue through all passes, we can now sometimes report duplicative
errors. Specifically, when `ValidateExhaustiveDependencies` finds
incorrect deps for a useMemo/useCallback call,
`ValidatePreservedManualMemoization` will generally also error for the
same block, producing duplicate errors. The exhaustive deps error is
strictly more informative, so if we've already reported the earlier
error we don't need the later one.

This adds a `hasInvalidDeps` flag to StartMemoize that is set when
ValidateExhaustiveDependencies produces a diagnostic.
ValidatePreservedManualMemoization then skips validation for memo blocks
with this flag set.
2026-02-26 12:40:55 -08:00
Joseph Savona e33071c614 [compiler] Improved ref validation for non-mutating functions (#35893)
If a function is known to freeze its inputs, and captures refs, then we
can safely assume those refs are not mutated during render.

An example is React Native's PanResponder, which is designed for use in
interaction handling. Calling `PanResponder.create()` creates an object
that shouldn't be interacted with at render time, so we can treat it as
freezing its arguments, returning a frozen value, and not accessing any
refs in the callbacks passed to it. ValidateNoRefAccessInRender is
updated accordingly - if we see a Freeze <place> and ImmutableCapture
<place> for the same place in the same instruction, we know that it's
not being mutated.

Note that this is a pretty targeted fix. One weakness is that we may not
always emit a Freeze effect if a value is already frozen, which could
cause this optimization not to kick in. The worst case there is that
you'd just get a ref access in render error though, not miscompilation.
And we could always choose to always emit Freeze effects, even for
frozen values, just to retain the information for validations like this.
2026-02-24 12:36:32 -08:00
Joseph Savona b354bbd2d2 [compiler] Update docs with fault tolerance summary, remove planning doc (#35888)
Add concise fault tolerance documentation to CLAUDE.md and the passes
README covering error accumulation, tryRecord wrapping, and the
distinction between validation vs infrastructure passes. Remove the
detailed planning document now that the work is complete.
2026-02-23 16:18:44 -08:00
Joseph Savona c92c579715 [compiler] Fix Pipeline.ts early-exit, formatting, and style issues (#35884)
Fix the transformFire early-exit in Pipeline.ts to only trigger on new
errors from transformFire itself, not pre-existing errors from earlier
passes. The previous `env.hasErrors()` check was too broad — it would
early-exit on validation errors that existed before transformFire ran.

Also add missing blank line in CodegenReactiveFunction.ts Context class,
and fix formatting in ValidateMemoizedEffectDependencies.ts.

---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35884).
* #35888
* __->__ #35884
2026-02-23 16:16:41 -08:00