## 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> <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> </div>
---------
Co-authored-by: Hanzo Dev <[email protected]>
## 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 ✅
## 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.
## 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.
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
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 <[email protected]>
Co-authored-by: Mike Vitousek <[email protected]>
Co-authored-by: Mike Vitousek <[email protected]>
Co-authored-by: lauren <[email protected]>
Co-authored-by: lauren <[email protected]>
## 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
7e26eb89fc3264a34a9f03a68ac7a870d9f74b51. 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
## 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.
<!--
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.
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.
-->
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.
-->
<!--
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 <[email protected]>
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.
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.
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.
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
Rename `state: Environment` to `env: Environment` in
ValidateMemoizedEffectDependencies visitor methods, and
`errorState: Environment` to `env: Environment` in
ValidatePreservedManualMemoization's validateInferredDep.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35883).
* #35888
* #35884
* __->__ #35883
Removes unnecessary indirection in 17 compiler passes that previously
accumulated errors in a local `CompilerError` instance before flushing
them to `env.recordErrors()` at the end of each pass. Errors are now
emitted directly via `env.recordError()` as they're discovered.
For passes with recursive error-detection patterns
(ValidateNoRefAccessInRender,
ValidateNoSetStateInRender), the internal accumulator is kept but
flushed
via individual `recordError()` calls. For InferMutationAliasingRanges,
a `shouldRecordErrors` flag preserves the conditional suppression logic.
For TransformFire, the throw-based error propagation is replaced with
direct recording plus an early-exit check in Pipeline.ts.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35882).
* #35888
* #35884
* #35883
* __->__ #35882
Remove `tryRecord()` from the compilation pipeline now that all passes
record
errors directly via `env.recordError()` / `env.recordErrors()`. A single
catch-all try/catch in Program.ts provides the safety net for any pass
that
incorrectly throws instead of recording.
Key changes:
- Remove all ~64 `env.tryRecord()` wrappers in Pipeline.ts
- Delete `tryRecord()` method from Environment.ts
- Add `CompileUnexpectedThrow` logger event so thrown errors are
detectable
- Log `CompileUnexpectedThrow` in Program.ts catch-all for non-invariant
throws
- Fail snap tests on `CompileUnexpectedThrow` to surface pass bugs in
dev
- Convert throwTodo/throwDiagnostic calls in HIRBuilder (fbt, this),
CodegenReactiveFunction (for-in/for-of), and BuildReactiveFunction to
record errors or use invariants as appropriate
- Remove try/catch from BuildHIR's lower() since inner throws are now
recorded
- CollectOptionalChainDependencies: return null instead of throwing on
unsupported optional chain patterns (graceful optimization skip)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35881).
* #35888
* #35884
* #35883
* #35882
* __->__ #35881
Add test fixture demonstrating fault tolerance: the compiler now reports
both a mutation error and a ref access error in the same function, where
previously only one would be reported before bailing out.
Update plan doc to mark all phases as complete.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35877).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* __->__ #35877
- Change runWithEnvironment/run/compileFn to return
Result<CodegenFunction, CompilerError>
- Wrap all pipeline passes in env.tryRecord() to catch and record
CompilerErrors
- Record inference pass errors via env.recordErrors() instead of
throwing
- Handle codegen Result explicitly, returning Err on failure
- Add final error check: return Err(env.aggregateErrors()) if any errors
accumulated
- Update tryCompileFunction and retryCompileFunction in Program.ts to
handle Result
- Keep lint-only passes using env.logErrors() (non-blocking)
- Update 52 test fixture expectations that now report additional errors
This is the core integration that enables fault tolerance: errors are
caught,
recorded, and the pipeline continues to discover more errors.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35874).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* #35875
* __->__ #35874
Add error accumulation methods to the Environment class:
- #errors field to accumulate CompilerErrors across passes
- recordError() to record a single diagnostic (throws if Invariant)
- recordErrors() to record all diagnostics from a CompilerError
- hasErrors() to check if any errors have been recorded
- aggregateErrors() to retrieve the accumulated CompilerError
- tryRecord() to wrap callbacks and catch CompilerErrors
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35873).
* #35888
* #35884
* #35883
* #35882
* #35881
* #35880
* #35879
* #35878
* #35877
* #35876
* #35875
* #35874
* __->__ #35873
Remove dead code left behind after the removal of retryCompileFunction,
enableFire, and inferEffectDependencies:
- Delete ValidateNoUntransformedReferences.ts (always a no-op)
- Remove CompileProgramMetadata type and retryErrors from ProgramContext
- Remove 'client-no-memo' output mode
- Change compileProgram return type from CompileProgramMetadata | null
to void
Cleans up feature flags that do not have an active experiment and which
we don't currently plan to ship, one commit per flag. Notable removals:
* Automatic (inferred) effect dependencies / Fire: abandoned due to
early feedback. Shipped useEffectEvent which addresses some of the
use-cases.
* Inline JSX transform (experimented, not a consistent win)
* Context selectors (experimented, not a sufficient/consistent win given
the benefit the compiler already provides)
* Instruction Reordering (will try a different approach)
To decide which features to remove, I looked at Meta's internal repos as
well as eslint-pugin-react-hooks to see which flags were never
overridden anywhere. That gave a longer list of flags, from which I then
removed some features that I know are used in OSS.
Tracks locations for reactive scope dependencies, both on the deps and
portions of the path. The immediate need for this is a non-public
experiment where we're exploring type-directed compilation, and
sometimes look up the types of expressions by location. We need to
preserve locations accurately for that to work, including the locations
of the deps.
## Test Plan
Locations for dependencies are not easy to test: i manually spot-checked
the new fixture to ensure that the deps look right. This is fine as
best-effort since it doesn't impact any of our core compilation logic, i
may fix forward if there are issues and will think about how to test.
## Summary
Fix react-hooks/set-state-in-effect false negatives when Hooks are
called via a namespace import (e.g. `import * as React from 'react'` and
`React.useEffect(...))`. The validation now checks the MethodCall
property (the actual hook function) instead of the receiver object.
Issue: Bug: #35377
## How did you test this change?
Added a regression fixture;
Ran tests and verified it reports `EffectSetState` and matches the
expected output.
<img width="461" height="116" alt="Screenshot 2025-12-27 at 14 13 38"
src="https://github.com/user-attachments/assets/fff5aab9-0f2c-40e9-a6a5-b864c3fa6fbd"
/>
More snap improvements for use with agents:
* `yarn snap compile [--debug] <path>` for compiling any file,
optionally with debug logs
* `yarn snap minimize <path>` now accepts path as a positional param for
consistency w 'compile' command
* Both compile/minimize commands properly handle paths relative to the
compiler/ directory. When using `yarn snap` the current working
directory is compiler/packages/snap, but you're generally running it
from the compiler directory so this matches expectations of callers
better.
This is a combination of a) a subagent for investigating compiler errors
and b) testing that agent by fixing bugs with for loops within
try/catch. My recent diffs to support maybe-throw within value blocks
was incomplete and handled many cases, like optionals/logicals/etc
within try/catch. However, the handling for for loops was making more
assumptions and needed additional fixes.
Key changes:
* `maybe-throw` terminal `handler` is now nullable. PruneMaybeThrows
nulls the handler for blocks that cannot throw, rather than changing to
a `goto`. This preserves more information, and makes it easier for
BuildReactiveFunction's visitValueBlock() to reconstruct the value
blocks
* Updates BuildReactiveFunction's handling of `for` init/test/update
(and similar for `for..of` and `for..in`) to correctly extract value
blocks. The previous logic made assumptions about the shape of the
SequenceExpression which were incorrect in some cases within try/catch.
The new helper extracts a flattened SequenceExpression.
Supporting changes:
* The agent itself (tested via this diff)
* Updated the script for invoking snap to keep `compiler/` as the
working directory, allowing relative paths to work more easily
* Add an `--update` (`-u`) flag to `yarn snap minimize`, which updates
the fixture in place w the minimized version
Adds support for value terminals (optional/logical/ternary/sequence)
within try/catch clauses.
Try/catch expressions insert maybe-throw terminals after each
instruction, but BuildReactiveFunction's value block extraction was not
expecting these terminals. The fix is to roughly treat maybe-throw
similarly to goto, falling through to the continuation block, but there
are a few edge cases to handle.
I've also added extensive tests, including testing that errors correctly
flow to the catch handler.
Fixes missing source locations for ReturnStatement nodes in generated
ast. Simple change using existing pattern, only required changes to the
codegen step, no other pipeline changes.
**Most file changes are new lines in generated code.** [First
commit](https://github.com/facebook/react/commit/d15e90ebe09250afc111f27109f4e698ea0d4361)
has the relevant changes, second commit has the noisy snap updates.
I added an exception to the validator to not report an error when a
return statement will be optimized to an implicit return by codegen, as
there's no separate return statement to instrument anyways in the final
ast. An edge case when it comes to preserving source locations for
instrumentation that is likely not as common for most babel transforms
since they are not doing optimizations.
Autogenerated summaries of each of the compiler passes which allow
agents to get the key ideas of a compiler pass, including key
input/output invariants, without having to reprocess the file each time.
In the subsequent diff this seemed to help.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35595).
* #35607
* #35298
* #35596
* #35573
* __->__ #35595
* #35539
`invariant()` was a pain to use - we always record a single location,
but the API required passing a compiler detail. This PR replaces
`invariant()` (keeping the name) with `simpleInvariant()`s signature,
and updates call sites accordingly. I've noticed that agents
consistently get invariant() wrong, which aligns with it being tedious
to call when you're writing code by hand. The simplified API should help
a bit.
A few times an agent has constructed fixtures that are silently skipped
because the component has no jsx or hook calls. This PR updates snap to
ensure that for each fixture either:
1) There are at least one compile success/failure *and* the
`@expectNothingCompiled` pragma is missing
2) OR there are zero success/failures *and* the `@expectNothingCompiled`
pragma is present
This ensures we are intentional about fixtures that are expected not to
have compilation, and know if that expectation breaks.
`react-hooks/exhaustive-effect-dependencies` from
`ValidateExhaustiveDeps` reports errors for both missing and extra
effect deps. We already have `react-hooks/exhaustive-deps` that errors
on missing dependencies. In the future we'd like to consolidate this all
to the compiler based error, but for now there's a lot of overlap. Let's
enable testing the extra dep warning by splitting out reporting modes.
This PR
- Creates `on`, `off`, `missing-only`, and `extra-only` reporting modes
for the effect dep validation flag
- Temporarily enables the new rule with `extra-only` in
`eslint-plugin-react-hooks`
- Adds additional null checking to `manualMemoLoc` to fix a bug found
when running against the fixture
Summary:
These validations are not essential for compilation, with this we only
run that logic when outputMode is 'lint'
Test Plan:
Update fixtures and run tests
Putting up https://github.com/facebook/react/pull/35129 again
Reverted in https://github.com/facebook/react/pull/35346 after breaking
main before security patch
This change impacts output formatting in a lot of snaps, so is very
sensitive to additions in main to the fixtures resulting in broken tests
after merging, so we should try merge quickly after rebasing or do a
fast follow to the merge with a snap update.
### What
Fixes source locations for VariableDeclarator in the generated AST.
Fixes a number of the errors in the snapshot I added yesterday in the
source loc validator PR https://github.com/facebook/react/pull/35109
I'm not entirely sure why, but a side effect of the fix has resulted in
a ton of snaps needing updating, with some empty lines no longer present
in the generated output. I broke the change up into 2 separate commits.
The [first
commit](https://github.com/facebook/react/pull/35129/commits/f4e4dc0f44a9ef453392ed63e02254f9acc947ec)
has the core change and the update to the missing source locations test
expectation, and the [second
commit](https://github.com/facebook/react/pull/35129/commits/cd4d9e944c5d93bc5125b365764d3039860db33d)
has the rest of the snapshot updates.
### How
- Add location for variable declarators in ast codegen.
- We don't actually have the location preserved in HIR, since when we
lower the declarations we pass through the location for the
VariableDeclaration. Since VariableDeclarator is just a container for
each of the assignments, the start of the `id` and end of the `init` can
be used to accurately reconstruct it when generating the AST.
- Add source locations for object/array patterns for destructuring
assignment source location support
The current `validateNoSetStateInEffects` error has potential false
positives because
we cannot fully statically detect patterns where calling setState in an
effect is
actually valid. This flag `enableVerboseNoSetStateInEffect` adds a
verbose error mode that presents multiple possible
use-cases, allowing an agent to reason about which fix is appropriate
before acting:
1. Non-local derived data - suggests restructuring state ownership
2. Derived event pattern - suggests requesting an event callback from
parent
3. Force update / external sync - suggests using `useSyncExternalStore`
This gives agents the context needed to make informed decisions rather
than
blindly applying a fix that may not be correct for the specific
situation.
Alternative approach to #35282 for validating effect deps in the
compiler that builds on the machinery in ValidateExhaustiveDependencies.
Key changes to that pass:
* Refactor to track the dependencies of array expressions as temporaries
so we can look them up later if they appear as effect deps.
* Instead of not storing temporaries for LoadLocals of locally created
variables, we store the temporary but also propagate the local-ness
through. This allows us to record deps at the top level, necessary for
effect deps. Previously the pass was only ever concerned with tracking
deps within function expressions.
* Refactor the bulk of the dependency-checking logic from
`onFinishMemoize()` into a standalone helper to use it for the new
`onEffect()` helper as well.
* Add a new ErrorCategory for effect deps, use it for errors on
effects
* Put the effect dep validation behind a feature flag
* Adjust the error reason for effect errors
---------
Co-authored-by: Jack Pope <[email protected]>
Fixes an edge case where a function expression would fail to take a
dependency if it referenced a hoisted `const` inferred as a primitive
value. We were incorrectly skipping primitve-typed operands when
determing scopes for merging in InferReactiveScopeVariables.
This was super tricky to debug, for posterity the trick is that Context
variables (StoreContext etc) are modeled just like a mutable object,
where assignment to the variable is equivalent to `object.value = ...`
and reading the variable is equivalent to `object.value` property
access. Comparing to an equivalent version of the repro case replaced
with an object and property read/writes showed that everything was
exactly right, except that InferReactiveScopeVariables wasn't merging
the scopes of the function and the context variable, which led me right
to the problematic line.
Closes#35122
Adds a new `enableUseKeyedState` compiler flag that changes the error
message for unconditional setState calls during render.
When `enableUseKeyedState` is enabled, the error recommends using
`useKeyedState(initialState, key)` to reset state when dependencies
change. When disabled (the default), it links to the React docs for the
manual pattern of storing previous values in state.
Both error messages now include helpful bullet points explaining the two
main alternatives:
1. Use useKeyedState (or manual pattern) to reset state when other
state/props change
2. Compute derived data directly during render without using state
ValidateNoSetStateInEffects already supports transitive setter
functions. This PR marks any synchonous state setter useEffectEvent
function so we can validate that uEE isn't being used only as
misdirection to avoid the validation within an effect body.
The error points to the call of the effect event.
Example:
```js
export default function MyApp() {
const [count, setCount] = useState(0)
const effectEvent = useEffectEvent(() => {
setCount(10)
})
useEffect(() => {
effectEvent()
}, [])
return <div>{count}</div>;
```
```
Found 1 error:
Error: Calling setState synchronously within an effect can trigger cascading renders
Effects are intended to synchronize state between React and external systems such as manually updating the DOM, state management libraries, or other platform APIs. In general, the body of an effect should do one or both of the following:
* Update external systems with the latest state from React.
* Subscribe for updates from some external system, calling setState in a callback function when external state changes.
Calling setState synchronously within an effect body causes cascading renders that can hurt performance, and is not recommended. (https://react.dev/learn/you-might-not-need-an-effect).
5 | })
6 | useEffect(() => {
> 7 | effectEvent()
| ^^^^^^^^^^^ Avoid calling setState() directly within an effect
8 | }, [])
9 | return <div>{count}</div>;
10 | }
```
First, this adds some more tests and organizes them into an
`exhaustive-deps/` subdirectory.
Second, the diagnostics are overhauled. For each memo block we now
report a single diagnostic which summarizes the issue, plus individual
errors for each missing/extra dependency. Within the extra deps, we
distinguish whether it's truly extra vs whether its just a more (too)
precise version of an inferred dep. For example, if you depend on
`x.y.z` but the inferred dep was `x.y`. Finally, we print the full
inferred deps at the end as a hint (it's also a suggestion, but this
makes it more clear what would be suggested).