Commit Graph
21577 Commits
Author SHA1 Message Date
Sophie AlpertandGitHub 3596bf4463 [Fiber] Update input.defaultValue for type=number too (#36980)
We used to avoid updating .defaultValue until blur on number inputs
because of a Chrome bug, but the relevant Chromium behavior was fixed in
2020 in M85 https://issues.chromium.org/issues/40124871 and likewise in
WebKit https://bugs.webkit.org/show_bug.cgi?id=217156 so I think we can
probably just remove this special-cased logic.

We do still need some special logic for number inputs because it's the
only type where we let users specify a non-string value that has
multiple string representations and need to be careful not to clobber
the .value if it has a slightly different string than what you'd get
from casting a number to string.

Fixes #29862.

Tested with the number inputs fixture in current Chrome, Safari, and
Firefox.
2026-07-09 19:58:34 -07:00
Sophie AlpertandGitHub de89c17390 [Fast Refresh] Find and remount wrapper edits behind lazy() (#36965)
Two bugs:

- Type changes to a lazy's inner component (e.g., adding a `memo`
  comparison function) were missed by the refresh scan.
- If a SimpleMemoComponent needs to be remounted, previously
  resolveRemountTypeForHotReloading would return the inner function
  and we would incorrectly recreate the fiber as a FunctionComponent.
  Instead, we can just use elementType consistently now that the
  beginWork functions consistently call resolveFamily on the wrapped
  components.
2026-07-08 09:59:07 -07:00
Sophie AlpertandGitHub c72b00e783 [Fast Refresh] Make edits to a memo comparison function take effect (#36964)
Editing the second argument of memo() previously never took effect
until something else remounted the tree, for two separate reasons:

- When adding a comparison function, we need to switch from
  SimpleMemoComponent to MemoComponent so canPreserveStateBetween
  should return false.
- MemoComponent was missing from the hot reload type resolution in
  createWorkInProgress, so existing fibers kept reading .compare from
  the old memo object forever.

New behavior:

- Adding or removing the comparison function remounts; we need to do
  this when adding (SimpleMemoComponent doesn't support a comparison
  function) so let's also do it when removing.
- Editing the comparison function implementation applies in place
  with state preserved

The TODO in isCompatibleFamilyForHotReloading is removed as that
wasn't the right place to do this check.
2026-07-08 09:58:35 -07:00
Sophie AlpertandGitHub 670d402ccd [Fast Refresh] Derive the fiber tag from the resolved type when mounting (#36963)
If an edit changed the kind of the type (e.g. a plain function got
wrapped in memo()), the fiber could get the old kind's tag with the
new kind's type, usually throwing an error.
2026-07-08 09:56:38 -07:00
Sophie AlpertandGitHub 1fb25ae06a [Fast Refresh] Unify hot reload type resolution (#36962)
Previously resolveForwardRefForHotReloading was treated as a special
case. It's simpler to move this logic to updateForwardRef which
mirrors how updateMemoComponent and mountLazyComponent already are,
and it allows us to unify to a single resolveTypeForHotReloading which
we'll use in the next commit.

Since .type is no longer changing we need to set didReceiveUpdate
manually.

This behavior is already tested in "can update forwardRef render
function in isolation".
2026-07-08 09:11:20 -07:00
ff48978c00 [test] Fix Error Proxy stack assignment in Node.js 21+ (#36967)
Same as #35227, but for `set`.

V8's `stack` setter silently noops when `this` is a Proxy
(nodejs/node#60862), so prodstack-stripping assignments like
`error.stack = 'Error: ' + message` in `resolveErrorProd` were dropped
and reads returned the real stack.

This caused `yarn test --prod` failures on Node 21+.

## Test Plan

`yarn test --prod` passes on both old and new Nodes.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-08 13:33:19 +01:00
2a784bedd2 [Fixture] flight-ssr-bench: drain immediates between iterations to fix false memory leak (#36968)
Claude found this while running the Flight/Fizz bench.

---

While measuring memory in this fixture, the Flight server appeared to
leak ~140KB per render: `heapUsed` grew steadily across hundreds of
renders even with forced GC, and roughly 3x worse when a Flight client
consumed the stream in the same process. Fizz-only runs were flat, so it
looked like a real Flight leak.

On a closer look, the benchmark loop seems to be lying. React schedules
one `setImmediate` per request, but every render in this fixture
completes entirely in promises and `nextTick` callbacks, so a tight
benchmark loop never gives the event loop a chance to run immediates at
all. They quietly pile up, and each pending callback keeps its
already-finished request alive.

Heap snapshots show that every "leaked" request was held by a pending
immediate, and nothing else. Adding a single `await setImmediate` per
iteration makes memory completely flat over 300 renders, with or without
a same-process Flight client. A real server yields to the event loop on
every request, so this can't happen outside a synthetic loop.

The fix is to yield to the event loop between benchmark iterations,
outside the timed window, so latency numbers are unaffected. Each
variant now also reports how much heap it retains after the run settles.
That number was meaningless before — the pile-up made variants with
bigger per-request graphs look like they used more memory when they
didn't.

After the fix, a full `yarn bench:bare` run retains +0.3–0.6MB total per
variant after 1000 iterations (JIT/warmup noise), instead of drifting by
hundreds of MB.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-08 13:31:23 +01:00
9277edc43a [Flight] Recognize Node 22+ V8 frames for Promise statics in debug info (#36969)
## Summary

Newer V8 versions (Node.js ≥ ~22) name the stack frames of static
methods on the `Promise` constructor `Promise.all`, `Promise.race`,
etc., where older versions named them `Function.all`, `Function.race`,
etc. The `isPromiseCreationInternal` and `isPromiseAwaitInternal`
allowlists in `ReactFlightServer` only matched the old spellings.

## How did you test this change?

- `yarn test --no-watchman ReactFlightAsyncDebugInfo` — 18/18 pass on
both Node 20.19.0 and Node 24.16.0 (previously, "can track async
information when awaited" failed on Node 24)
- `yarn test --silent --no-watchman packages/react-server/src/__tests__
packages/react-server-dom-webpack` — 243/243 pass on both Node versions,
default and `-r=experimental` channels
- `yarn test-www --silent --no-watchman
packages/react-server/src/__tests__` with `__VARIANT__` true and false —
pass
- `yarn flow dom-node`, `yarn prettier`, `yarn linc` — pass

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-08 13:02:09 +01:00
5894c3a0be [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: Hanzo Dev <[email protected]>
2026-07-07 20:46:37 -07:00
d665a321a8 [Fast Refresh] Remount correctly when an edit changes the component kind (#36950)
Fixes #30659. I'm not confident in this yet but here's what Fable said
based on
https://github.com/react/react/pull/32214#issuecomment-2734436781:

Editing a component from a plain function to a memo or forwardRef
wrapper (or between wrapper kinds) crashed with "Component is not a
function", because canPreserveStateBetween only compared hook
signatures. The edit was classified as state-preserving, so
createWorkInProgress swapped the wrapper object in as the type of a
FunctionComponent fiber and renderWithHooks tried to call it.

Two fixes, both DEV-only:

- canPreserveStateBetween returns false when typeof or $$typeof differ.
A fiber's tag is derived from the kind of its type, so state can never
be preserved across a kind change; these edits must go to staleFamilies
(remount). Nested wrappers need no special handling because register()
creates a family per nesting level ($type/$render ids), and each level's
kind is compared against its own family.

- The reconciler now finds and remounts wrapper fibers whose outer kind
changed. scheduleFibersWithFamiliesRecursively previously resolved
families only through the inner implementation (type/type.render), but a
kind-changing edit is recorded only on the outer type's family, so edits
like memo -> function never reached the scanner and were silently
dropped. The scanner now also checks the elementType's family, and the
_debugNeedsRemount branch in beginWork rebuilds the fiber from
family.current (the latest registered type for the fiber's identity)
rather than the fiber's possibly-stale inner type. This also fixes stale
simple memo remounts recreating as plain functions, dropping the memo
wrapper.

Unlike the reverted #30660 (see #32214), this does not fabricate
families or guess type shapes: families are still created in exactly one
place, and the same ID always resolves to the same family.

Restores the tests from #30660 and adds coverage for changing the inner
type of a memo between function and forwardRef.

Co-authored-by: BIKI DAS <[email protected]>
Co-authored-by: dan <[email protected]>
2026-07-07 16:30:46 -07:00
laurenandGitHub cda292d3ff [compiler] Port JSX tag classification fix to Rust (not-lowercase is a component) (#36951) 2026-07-07 09:46:24 -07:00
Sophie AlpertandGitHub 3f19e80d4f [Fiber] Don't invoke effects on moved children in StrictMode (#36948)
Fixes #29585.
2026-07-07 09:09:44 -07:00
Sophie AlpertandGitHub e0c95af0b2 [Fiber] Don't set .innerHTML when it hasn't changed (#36949)
Fixes #30994. We compared this previously but it regressed in #26501.
2026-07-07 09:09:10 -07:00
Kurtd Daniel BigtasandGitHub b444fd4533 [DevTools] Fix WhatChanged scrolling out of view in profiler sidebar (#36244)
## Summary
Previously, WhatChanged and InspectedElementBadges were rendered inside
the same scrollable [Content
div](https://github.com/facebook/react/blob/main/packages/react-devtools-shared/src/devtools/views/Profiler/SidebarSelectedFiberInfo.js#L130-L147)
as the commit list. When a component had many render timestamps,
scrolling down to pick one caused the "what changed" context to scroll
out of view, requiring the user to scroll back up to see it.

While the profiler already exposes render details via hover tooltips on
fibers, those are transient and pointer-dependent. This change
introduces a sticky header section at the top of the Content div that
holds the current commit summary, badges, and WhatChanged. The section
is collapsible (click or Enter/Space) so users can reclaim vertical
space for the commit list when needed. The commit list now sits below
the sticky header as a sibling, so it scrolls independently while the
summary remains always visible.

Closes #36525 

---
### Current Behavior

https://github.com/user-attachments/assets/05f2889a-4e0d-4464-965a-2821c63c2d30

### Proposed UI
Apologies on this one. I had to [upload it on
YouTube](https://youtu.be/g3KMaqn33G0) since the video is quite big.

---

## How did you test this change?

### Manual Testing
| Case  | Screenshot |
| ------------- | ------------- |
| Expanded simple | <img width="1512" height="948"
alt="expanded-default"
src="https://github.com/user-attachments/assets/00d3bf11-ef98-463e-96ec-c918e58147ff"
/> |
| Collapsed Simple | <img width="1512" height="948"
alt="collapsed-default"
src="https://github.com/user-attachments/assets/75d0ae79-c96f-4c55-9dc1-538ba65037b8"
/> |
| Focused using tab navigation while expanded | <img width="1512"
height="948" alt="expanded-tab-focused-simple"
src="https://github.com/user-attachments/assets/c4098f2d-1c43-4b04-ab69-1e646e79bc8e"
/> |
| Focused using tab navigation while collapsed | <img width="1512"
height="948" alt="collapsed-tab-focused-simple"
src="https://github.com/user-attachments/assets/75840812-b700-44dd-b82d-51d5a867f501"
/> |
| Long render info( expanded ) | <img width="1512" height="948"
alt="long-desc-expanded"
src="https://github.com/user-attachments/assets/53c4d776-55d5-4bb0-818d-a70e032e57d7"
/> |
| Long render info( collapsed ) | <img width="1512" height="948"
alt="long-desc-collapsed"
src="https://github.com/user-attachments/assets/45eb7f6f-fcc6-4f7b-88e7-e3ece9e59d19"
/> |

#### Hover (expanded)

https://github.com/user-attachments/assets/d04668d3-1e0e-44c7-af7e-087d246ec07d

#### Hover (collapsed)

https://github.com/user-attachments/assets/86638b8d-60c2-4cfa-b357-c1104e76767e

---

## Why this matters

This is more than a visual polish issue:

* The Profiler is fundamentally a context-driven debugging tool
* Losing the “why did this render?” explanation during navigation
interrupts analysis flow
* The issue becomes progressively worse on large applications or heavily
interactive surfaces
* Hover tooltips are not an adequate replacement because they are
transient and pointer-dependent
* Keyboard users are disproportionately affected due to repeated
navigation and scrolling

In practice, this increases cognitive load during performance debugging
and makes long profiling sessions unnecessarily cumbersome.
2026-07-07 14:18:51 +01:00
DmitriiandGitHub dd1ba0ffd6 [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
gtkatakuraandGitHub efedd75011 [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
Ruslan LesiutinandGitHub 051ca30658 fix[flow]: apply new type cast syntax (#36938)
Quick follow-up to https://github.com/react/react/pull/36786, which
wasn't rebased onto version of `main` that already had Flow upgraded.
2026-07-06 15:23:25 +01:00
BIKI DASandGitHub aa4358de54 [DevTools] Make component search results directly navigable (#36786)
## Summary

In a large react app, especially when components having similar starting
names like Table, TableColumn, TableCell, TableRow all together 100+
components when rendered in a virtualized table. Traversing the search
result is sometimes difficult with scroll

The component tree search only let you step through matches one at a
time (Enter / Shift+Enter). In large apps with many similarly-named
components (Table, TableRow, TableCell, ...) a search can return 100+
matches in a virtualized list, making a specific match tedious to reach.

- the result counter is an editable, live-scrubbing
  index field: typing a number scrolls to that match as you type
  (clamped to range)

- Fixes re-search getting stuck, clearing the box and retyping the same
  term while a match was still selected snapped back to that same
  component. It now advances to the next match (find-next semantics).
  
  


## How did you test this change?


Adds a SearchableTable example to the DevTools shell and unit tests for
the new action and the retype behavior.




https://github.com/user-attachments/assets/7ea9801a-7bcb-4e8f-bf73-a5307a0fdbae


cc @hoxyq Let me know what do you feel about this feature, if its
helpful for devtools.
2026-07-06 14:59:51 +01:00
Anas KhanandGitHub e01aa00cd7 [DevTools] Fix printOperationsArray decode of applied activity slice change (#36935)
`printOperationsArray` in `react-devtools-shared` walks an operations
array under
the invariant that each `switch` case leaves `i` pointing at the next
opcode (loop
header at `packages/react-devtools-shared/src/utils.js`).

The `TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE` case broke that
invariant:

```js
case TREE_OPERATION_APPLIED_ACTIVITY_SLICE_CHANGE: {
  i++;                                        // skip opcode -> i now at the value slot
  const activitySliceIDChange = operations[i + 1]; // reads the slot AFTER the value; i not advanced
  ...
}
```

The operation is exactly two slots, `[opcode, activitySliceID]` (see the
writer in
`packages/react-devtools-shared/src/backend/fiber/renderer.js`, which
pushes the
opcode then the id). So the case did two things wrong:

1. It logged the wrong number: `operations[i + 1]` reads the slot
*after* the
value (the next operation's opcode, or `undefined` at the end of the
array).
2. It left `i` pointing at the value slot, so the outer `while (i <
operations.length)` loop re-read the activity-slice id as an opcode. For
any
non-zero slice id that falls through to `default: throw
Error("Unsupported
   Bridge operation ...")`, aborting the whole dump.

The two canonical decoders of this same operation both use the correct
pattern
(skip the opcode, then read *and* advance past the value):

- `devtools/store.js`: `i++; nextActivitySliceID = operations[i++];`
- `devtools/views/Profiler/CommitTreeBuilder.js`: `i++; const
activitySliceIDChange = operations[i++];`

This change makes `printOperationsArray` match them by reading
`operations[i++]`.

This is a debug-only diagnostic path: the only caller is the
`__DEBUG__`-guarded
dump in `backend/legacy/renderer.js`, so it is not a production crash.
The bug was
introduced in #34908.

## How did you test this change?

Added a regression test for `printOperationsArray` in
`packages/react-devtools-shared/src/__tests__/utils-test.js`. The
fixture chains
two activity-slice operations, `[rendererID, rootID, stringTableSize=0,
opcode, 42,
opcode, 0]`; the trailing operation is what forces the reader to advance
past the
first value slot rather than re-read it. It asserts the call does not
throw, logs
once, and that the message contains both `Applied activity slice change
to 42` and
`Reset applied activity slice`.

Ran the DevTools Jest project (built first, as that project requires a
build):

- With the fix: 51/51 pass, including the new test.
- Reverting only the one-line fix back to `operations[i + 1]` and
rebuilding: the
new test fails with `Unsupported Bridge operation "42"` (exactly the
predicted
  failure), 50 pass / 1 fail. Restored the fix and it is green again.

`yarn prettier-check` and `yarn linc` are clean on the changed files.
2026-07-06 14:27:17 +01:00
Anas KhanandGitHub 3169e63296 docs: remove stale parentType param from validateChildKeys JSDoc (#36928)
`validateChildKeys` in `packages/react/src/jsx/ReactJSXElement.js` had a
signature
of `validateChildKeys(node, parentType)`, but #34174 ("Remove unused
arguments from
ReactElement") dropped the second argument, changing the signature to
`validateChildKeys(node)` and updating every call site to pass a single
argument.

That change removed several now-unused `@param` lines from the same
file, but left
one behind on `validateChildKeys`:

```js
/**
 * ...
 * @internal
 * @param {ReactNode} node Statically passed child of any type.
 * @param {*} parentType node's parent's type.   // <- no such parameter anymore
 */
function validateChildKeys(node) {
```

`parentType` no longer appears anywhere in the function signature or
body, so this
`@param` line is stale and misleading to anyone reading the doc comment.
This PR
deletes that single line. The remaining `@param {ReactNode} node`
already fully and
correctly documents the sole parameter. No code or behavior change.

## How did you test this change?

This is a documentation-only change (a JSDoc comment on an `@internal`
helper), so
there is no runtime behavior to test. I verified it as follows:

- Confirmed `parentType` no longer appears anywhere in
`packages/react/src/jsx/ReactJSXElement.js` (`grep -n parentType`
returns no
  matches after the change).
- Confirmed the signature `function validateChildKeys(node)` and all
call sites are
  unchanged by this diff.
- `yarn prettier` (via `scripts/prettier/index.js check-changed`) -
clean.
- `yarn linc` (ESLint on changed files) - passed.
- `yarn flow dom-node` - No errors.

---

## Diff (for reference)

```diff
diff --git a/packages/react/src/jsx/ReactJSXElement.js b/packages/react/src/jsx/ReactJSXElement.js
@@ -860,7 +860,6 @@ export function cloneElement(element, config, children) {
   *
   * @internal
   * @param {ReactNode} node Statically passed child of any type.
- * @param {*} parentType node's parent's type.
   */
 function validateChildKeys(node) {
```
2026-07-06 14:18:44 +01:00
Anas KhanandGitHub 3deee48461 [react-devtools] Keep console specifiers literal when no argument is supplied (#36930)
`formatConsoleArguments` in

`packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js`
is used by the DevTools backend (via `hook.js`) to inline `console.*`
printf-style substitutions after stripping React's appended component
stack.
For `%s`/`%d`/`%i`/`%f` it consumes the next argument with
`args.splice(argumentsPointer, 1)` and formats the result.

When a format string has more specifiers than arguments, `splice`
returns an
empty array, so `arg` is `undefined` and the specifier is rendered as
text:
`%s` becomes `"undefined"`, and `%d`/`%i`/`%f` become `"NaN"`.

```js
formatConsoleArguments('%s %s', 'the');
// before: ['the undefined']
// after:  ['the %s']
```

Browsers and Node's `util.format` leave an unmatched specifier as a
literal
(`console.log('%s %s', 'a')` prints `a %s`; `console.log('%d')` prints
`%d`).
So a message like `console.warn('value: %s')` was shown in DevTools as
`value: undefined` instead of `value: %s`.

This guards each of the `%d`/`%i`, `%f`, and `%s` cases on argument
availability (`argumentsPointer >= args.length`): when nothing is left
to
consume it keeps the specifier text and does not splice, mirroring the
existing trailing-`%` handling added in #36852. An explicitly passed
`undefined`/`null` argument is unchanged and still renders as
`undefined`/`null`, since a value is present at that position (the
`formats nullish values` test still passes).

## How did you test this change?

Added a regression test to the existing `formatConsoleArguments`
describe
block in `packages/react-devtools-shared/src/__tests__/utils-test.js`:

```js
it('keeps specifiers literal when no argument is supplied', () => {
  expect(formatConsoleArguments('%s %s', 'the')).toEqual(['the %s']);
  expect(formatConsoleArguments('%s %d', 'value')).toEqual(['value %d']);
  expect(formatConsoleArguments('%s %i', 'value')).toEqual(['value %i']);
  expect(formatConsoleArguments('%s %f', 'value')).toEqual(['value %f']);
});
```

Each assertion fails on `main` (it produces `['the undefined']` and
`['value NaN']`) and passes with the fix.

Commands run locally:

```
yarn test --build --project devtools packages/react-devtools-shared/src/__tests__/utils-test.js
# 51 passed, 51 total

yarn lint packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js \
          packages/react-devtools-shared/src/__tests__/utils-test.js
# Lint passed.

yarn flow dom-node
# No errors!

yarn prettier-check packages/react-devtools-shared/src/backend/utils/formatConsoleArguments.js \
                    packages/react-devtools-shared/src/__tests__/utils-test.js
# clean
```

Cross-checked the expected output against Node `util.format`:
`util.format('%s %s', 'the')` -> `the %s`; `util.format('%d')` -> `%d`.
2026-07-06 14:18:10 +01:00
Anas KhanandGitHub 45a8cf5349 [react-devtools] substitute %i and %f in console format strings (#36929)
`formatConsoleArgumentsToSingleString` in
`packages/react-devtools-shared/src/backend/utils/index.js`
inlines `console.*` printf-style substitutions into a single string.
That string is
used both as the dedup key and as the displayed text for per-component
warnings/errors.

The `switch` that consumes the captured flag handles `s`, `d`, `i`, and
`f`, and the
function's own header comment says it "Implements s, d, i and f
placeholders". But
the substitution regex only captured `[jds]`:

```js
const REGEXP = /(%?)(%([jds]))/g;
```

So `%i` and `%f` were never matched. The `case 'i'` and `case 'f'` arms
were dead
code: the specifier was emitted literally and its argument was never
consumed. Worse,
because the unmatched specifier does not shift its argument, every
following specifier
in the same format string then binds to the wrong argument (a cascading
off-by-one
over the remaining args).

`%i` and `%f` are standard console integer/float specifiers (Node
`util.format` and
browsers both support them), so this affected common log formats. The
fix adds `i`
and `f` to the regex class so the existing switch arms run:

```js
const REGEXP = /(%?)(%([jdisf]))/g;
```

This is a one-character-class change that reconciles the regex with the
switch and
the header comment. The pre-existing behavior that `%j` is matched but
has no
`case` (so it falls through unchanged) is intentionally left as-is; it
is out of
scope for this fix.

## How did you test this change?

Added three regression tests to the existing
`formatConsoleArgumentsToSingleString`
describe block in
`packages/react-devtools-shared/src/__tests__/utils-test.js`:

- `formatConsoleArgumentsToSingleString('%i', 3.14)` -> `'3'`
- `formatConsoleArgumentsToSingleString('%f', 3.5)` -> `'3.5'`
- `formatConsoleArgumentsToSingleString('a %i b %s', 7, 'x')` -> `'a 7 b
x'` (locks
  in argument alignment)

Commands run locally (experimental devtools bundles):

```
yarn build-for-devtools
yarn test --build --project=devtools -r=experimental packages/react-devtools-shared/src/__tests__/utils-test.js
```

Result: `Tests: 53 passed, 53 total`.

To confirm the tests actually cover the bug, I reverted the
one-character fix back to
`[jds]` and reran: the three new tests fail exactly as the bug predicts,
e.g. `%i`
yields `"%i 3.14"` and `a %i b %s` yields `"a %i b 7 x"` (the `%s` binds
to `7`
instead of `x`, showing the off-by-one). Restoring the fix makes them
pass again.

Also green:

```
yarn linc            # ESLint on changed files: passed
yarn prettier        # no files reflagged
yarn flow dom-node   # No errors!
```
2026-07-06 14:16:15 +01:00
uditandGitHub 417ffffe22 [Perf Tracks] Don't enumerate typed array props in dev (#36913) 2026-07-02 20:51:05 +02:00
Ruslan LesiutinandGitHub 48ed07a4b1 [react-devtools] add parent stack tool (#36825)
This PR adds a new `getParentStack` tool to the Facade and an
integration of it in `react-devtools-cdt-mcp`.

Similarly to the owner stack tool, it receives a `uid` of a specific
component and returns structural parent stack. For every node, it
includes `uid`, `name`, `type`.
2026-07-02 19:49:03 +04:00
7bac16f3ea [Fizz] Guard the shell-error callbacks instead of nulling them out on the request (#36916)
Follow-up to #36903

The previous change stopped `onAllReady` from firing after a fatal shell
error by assigning `noop` to `request.onAllReady` inside `fatalError`.
This mutated a completion callback on the request as a side channel,
mirroring the existing `request.onShellError = noop` in `completeShell`.
This refactor removes both noop assignments and instead guards the calls
at the point where they would happen, so the callbacks on the request
stay the ones the caller passed in.

For `onAllReady`, `erroredTask` now returns right after `fatalError` in
the root (`boundary === null`) case, so it no longer falls through to
the `allPendingTasks === 0` check that calls `completeAll`. This matches
the sibling `finishAbortedTask`, which already returns after a fatal
root error. Later task completions cannot reach `completeAll` either,
because `performWork` bails out once the request status is past `OPEN`.

For `onShellError`, `fatalError` now decides whether to call it based on
the same condition that governed the assignment: the shell is complete
exactly when `request.pendingRootTasks === 0`, and `completeShell` only
runs at that point, so guarding the call is equivalent to the previous
nulling out. `onFatalError` still always fires because the error is
always fatal to the request.

This is a behavior-preserving refactor of the fix landed in #36903; the
tests added there continue to pass unchanged.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-02 11:27:44 +02:00
Ruslan LesiutinandGitHub f72cb3a3e1 [react-devtools-cdt-mcp] run E2E tests in CI (#36824)
Adds a GH Actions workflow for the newly introduced e2e test runner for
`react-devtools-cdt-mcp`.

Example run -
https://github.com/react/react/actions/runs/27830544937/job/82366479218?pr=36824.
2026-07-02 09:30:20 +04:00
de34f43004 [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 <[email protected]>
2026-07-02 00:16:29 -05:00
Jack PopeandGitHub af3e652c36 Add parentEnter/parentExit props to ViewTransition (#36690)
An alternative to https://github.com/facebook/react/pull/36135

___

Adds experimental support for `parentEnter` and `parentExit` on nested
`<ViewTransition>` components, so descendants can animate when an
ancestor boundary enters or exits.

This is gated behind the `enableViewTransitionParentEnterExit` feature
flag.

Addresses the use case of animating individual items in a list (such as
sliding inactive feed posts off-screen) when a parent `<ViewTransition>`
is the one being added or removed during the update.

Today, `exit`/ `enter` only fire on the `<ViewTransition>` that was
directly mounted or unmounted. Nested boundaries in the same subtree are
not visited for their own `exit`/`enter` when the whole tree goes away
as one unit, which is usually what you want. But some cases need an
explicit opt-in to nested animations.

This PR adds `parentEnter`/`parentExit`, and their handlers
`onParentEnter`/`onParentExit`. When an ancestor runs a real `enter` or
`exit`, React walks descendants and activates boundaries that define the
nested props. This only continues through `<ViewTransition>` nodes that
define `parentEnter` / `parentExit` / `onParentEnter` / `onParentExit` /
`onGestureParentEnter` / `onGestureParentExit`, otherwise the chaining
stops. A boundary’s own `enter`/`exit` still do not run when only an
ancestor animates. So it is possible to have different animations on the
same `<ViewTransition>` for `enter` vs `parentEnter`.

```jsx
function ExampleOnAncestorExit() {
  return (
    // This boundary exits — starts the parentExit walk
    <ViewTransition exit="panel-exit">
      <div>
        {/* ✓ Activates: parentExit set. Walk continues inside */}
        <ViewTransition parentExit="slide-out">
          <div>
            {/* ✗ parentEnter is not consulted on an exit walk */}
            <ViewTransition parentEnter="slide-in">
              ...
            </ViewTransition>
            {/* ✗ Chain broken: no parentExit or onParentExit */}
            <ViewTransition>
              <div>
                {/* ✗ Never reached on this path, blocked by parent above */}
                <ViewTransition parentExit="slide-out">
                  ...
                </ViewTransition>
              </div>
            </ViewTransition>
            {/* ✓ Handler-only chain: onParentExit continues the walk */}
            <ViewTransition onParentExit={() => animateOut()}>
              <ViewTransition onParentExit={() => animateDeepOut()}>
                ...
              </ViewTransition>
            </ViewTransition>
            {/* ✓ Activates: sibling under the chain */}
            <ViewTransition parentExit="slide-out">
              ...
            </ViewTransition>
          </div>
        </ViewTransition>
      </div>
    </ViewTransition>
  );
}
```
2026-07-01 12:16:12 -04:00
Ruslan LesiutinandGitHub 3ea43bdd38 [react-devtools-cdt-mcp] add chrome-devtools E2E coverage (#36823)
A node runner that bootstraps `fixtures/app`, and then uses
`chrome-devtools` CLI to test third-party tools.

```
$ node e2e/run.js
Starting fixture at http://127.0.0.1:60033/
Starting chrome-devtools daemon...
Checking third-party tool discovery...
Checking tree, details, search, and DOM lookup...
Checking source, owners, and error payloads...
Checking profiling through a real CLI click...
react-devtools-cdt-mcp E2E passed.
✨  Done in 11.66s.
```
2026-07-01 19:02:36 +04:00
cd2c625d68 [Fizz] Stop firing onAllReady after the shell errored (#36903)
Co-authored-by: Hanzo Dev <[email protected]>
2026-07-01 12:07:32 +02:00
7349854f35 Fix: Treat incomplete tree as an error during recovery (#36911)
In `recoverFromConcurrentError`, there's logic to determine if the
latest render attempt resulted in another error or if it completed
successfully, by checking whether the exit status matches RootErrored.

The logic was incomplete because RootSuspendedAtTheShell is also
considered an errored state in this context. This can cause an
incomplete tree (i.e. one that unwinds without entering the complete
phase) to be mistaken for a complete one, leading to confusing errors.
An incomplete tree can never be committed because it's not guaranteed to
represent a coherent state.

An example of how this can manifest as a bug: in #33580, an error caught
by an error boundary triggers a synchronous recovery render. During that
render a parent component suspends on `use(thenable)` with no Suspense
boundary above it, so the tree unwinds to the shell instead of
completing. Because the incomplete tree is mistaken for a recovered one
and committed, the parent fiber becomes current with a truncated hook
list — only the hooks it rendered before suspending. On the next render
it calls the rest of its hooks and throws "Rendered more hooks than
during the previous render."

Fixes #33580.

Co-authored-by: Arunanshu Biswas
<[email protected]>

Co-authored-by: Arunanshu Biswas <[email protected]>
2026-06-30 15:38:59 -04:00
Ruslan LesiutinandGitHub 809897e869 [react-devtools-cdt-mcp] add DOM element component lookup (#36822)
Integrates newly added `getComponentByHostInstance` tool from
`react-devtools-facade` into `react-devtools-cdt-mcp`.

The API uses `uid` of the element from CDT MCP tree snapshot, but under
the hood it gets converted to the DOM Element reference, which is
exactly what we pass to `getComponentByHostInstance`.
2026-06-30 20:45:19 +02:00
Ruslan LesiutinandGitHub e00b96634f [react-devtools-facade] add host instance component lookup (#36820)
Adds a new tool to the Facade, where the input is the `HostInstance` and
the output is the corresponding component payload for the `HostFiber` of
this `HostInstance`. Supports multiple Host objects from Fiber:
`HostComponent`, `HostText`, `HostSingleton`, `HostHoistable`.

> [!NOTE]
> This doesn't bring much value now, because this returns the payload
for the corresponding HostFiber, not the closest user-defined Fiber.
This will be improved by adding a parentUid reference to the element
payload, so the agent can crawl up through the component tree.
2026-06-30 20:33:47 +04:00
Ruslan LesiutinandGitHub 54831395ea [react-devtools-cdt-mcp] chrome-devtools-mcp integration (#36600)
> [!NOTE]
> I had to invalidate `node_modules` cache keys on CI, because we are
now using `react-devtools-facade` package and we need to create a
symlink to it, otherwise Flow will fail. We use a hash value of
`yarn.lock`, but it's not invalidated when a package inside yarn
workspace is using another private package.

---

This change introduces new `react-devtools-cdt-mcp` package, which is
responsible for installing React tools for `chrome-devtools-mcp` server.
It is using `react-devtools-facade` as a dependency: it doesn't own the
implementation of these tools, it just relays Facade's tools to
`chrome-devtools-mcp` format.

When CDT MCP navigates to a fresh page, it emits unique
`devtoolsdiscovery` method, that has `respondWith` method, which is the
integration point between runtime libraries and MCP server.
`react-devtools-cdt-mcp` is the owner for this integration. More details
are available here -
https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/third-party-developer-tools.md.

Here is the list of tools that are being installed:
- `react_get_component_tree`
- `react_get_component_by_uid`
- `react_find_components`
- `react_get_component_source`
- `react_get_owner_stack_trace`
- `react_get_owner_stack`
- `react_start_profiling`
- `react_stop_profiling `
- `react_get_trace_overview`
- `react_get_commit_report`

--- 
Quick demo how to test the package with `chrome-devtools-mcp` CLI. Steps
are also mentioned in `README` file:


https://github.com/user-attachments/assets/af9fefce-03f0-4ebd-a94f-e9dd72b87f4d
2026-06-30 17:52:30 +02:00
Sebastian "Sebbie" SilbermannandGitHub ff51e873f0 [compiler] Ensure builds use the current checkout's compiler (#36881) 2026-06-30 17:03:13 +02:00
Sebastian "Sebbie" SilbermannandGitHub e0ab3b0cde [test] Use awaited serverAct to drive task loop (#36908) 2026-06-30 16:36:12 +02:00
Pieter De BaetsandGitHub efb4c0028a [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
Ruslan LesiutinandGitHub a1a86c2f7f [react-devtools-facade] Add includeHooks option to component lookup (#36874)
The tool now has a boolean argument which controls the presence of hook
tree in the return payload. If hooks were requested, and the inspection
fails, the error payload will be returned instead.
2026-06-29 23:29:48 +04:00
b164ad8258 [changelog] Add 19.0.x, 19.1.x, 19.2.x patch releases (#36898)
Documents the patch releases that shipped across the 19.0, 19.1, and
19.2 lines (`.2`/`.3` through `.7`/`.8`), which were missing from the
changelog.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-29 16:04:55 +02:00
bb18a5e917 [test] Add coverage for multi-byte code units in stream APIs (#36900)
Adds a test documenting why a `string.length`
fast path is unsound.

`byteSize` feeds heuristic decisions (outlining threshold at 500 bytes,
progressive chunk sizing). `string.length` undercounts multi-byte
content, so a boundary that is really >500 bytes could drop below the
outlining threshold.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-29 16:04:17 +02:00
uditandGitHub ec27349142 [Float] Forward nonce option in ReactDOM.preloadModule() (#36851) 2026-06-29 13:33:22 +02:00
Pieter De BaetsandGitHub fe3362f0ef [scripts] Fix missing VERSION_NATIVE_FB in commit artifacts (#36889)
## Summary

The `runtime_commit_artifacts` workflow has been failing in the "move
relevant files for react in fbsource into compiled-rn" step with:

```
mv: cannot stat 'build/facebook-react-native/VERSION_NATIVE_FB': No such file or directory
```

`VERSION_NATIVE_FB` was only written in `processExperimental`, *inside*
`build/facebook-react-native/`. That directory is not part of the
allowlist in the cleanup loop at the end of `processExperimental`, so
the experimental workers wrote the version file and then immediately
deleted it.

This was masked until #36859. The cleanup loop previously used `rm -rm`
(an invalid flag), so `rm` errored out and deleted nothing — the loop
was effectively a no-op. Once it was changed to `fs.rmSync`, the
deletion actually took effect. After CI merges the per-worker artifacts,
`facebook-react-native/*/cjs` still survives (the **stable** workers
produce it and `processStable` has no cleanup loop), but
`VERSION_NATIVE_FB` is gone because stable never wrote it.

## Fix

The `build/facebook-react-native` artifacts are identical across release
channels:
- RN packages resolve to the generic `.fb.js` entry fork (no
`.modern.fb.js` / `.classic.fb.js` split).
- The `native-fb` feature-flag forks don't reference `__EXPERIMENTAL__`.
- The version string is identical
(`<ReactVersion>-native-fb-<sha>-<date>`).

So this writes `VERSION_NATIVE_FB` from `processStable`, where
`facebook-react-native` is preserved, and drops the now-dead write from
`processExperimental`.

## Test Plan

Verified locally by building the `react` package through each code path:

- **Stable channel** (`yarn build --r=stable react/index`):
`build/facebook-react-native/VERSION_NATIVE_FB` is written with the
correct `19.3.0-native-fb-<sha>-<date>` version string. ✅
- **Experimental channel** (`yarn build --r=experimental react/index`):
`build/facebook-react-native` is deleted by the cleanup loop, leaving
only `oss-experimental` and `facebook-www` — confirming the version file
cannot come from this channel. ✅
- **Local merged build** (`yarn build react/index`, builds both channels
and merges): the final `build/facebook-react-native/` contains
`VERSION_NATIVE_FB`, matching what CI produces after merging per-worker
artifacts. ✅
2026-06-26 15:55:51 +01:00
Jiwon ChoiandGitHub 4950b5b789 [DevTools] Add ignore-listed stack frame disclosure (#36828) 2026-06-25 15:57:28 +02:00
Ruslan LesiutinandGitHub 0b98ace89e [react-devtools-facade] consolidate owner stack tools naming (#36871)
Instead of potential confusion coming from `stack` vs `branch`, we will
use `getOwnerStackTrace` for the error-like stack trace and
`getOwnerStack` for list of structured entities that represent owners.
2026-06-25 09:57:37 +01:00
f53d2cb16e [scripts] Fix release channel artifact cleanup for local builds (#36859)
Co-authored-by: Sebastian Sebbie Silbermann <[email protected]>
2026-06-25 10:42:20 +02:00
Minh VuandGitHub c4ebd8814f [scripts] Fix Linux benchmark xvfb startup (#36860) 2026-06-25 08:31:54 +02:00
Minh VuandGitHub 5382c3a97b [test] Assert aborted Replies reject Promises (#36862) 2026-06-25 08:29:19 +02:00
Hendrik LiebauandGitHub 1c0c7faf17 [Flight] Tailor the warning for binary data with a toJSON (Node Buffer) (#36873)
A Node.js `Buffer` carries a `toJSON` method, so Flight serializes it
through that method instead of as binary, and it is deserialized as a
plain `{type: 'Buffer', data: [...]}` object rather than a `Buffer` or
`Uint8Array`. The warning React emits in this case reads "Uint8Array
objects are not supported", which is misleading on two counts: the value
is actually a `Buffer`, reported as `Uint8Array` only because that is
its `Object.prototype.toString` tag, and a real `Uint8Array` is in fact
supported, since it has no `toJSON` and serializes as binary. This makes
the message especially confusing when binary data such as a font is
passed as a serialized value.

The first commit adds a test that pins down the current behavior,
capturing both the misleading warning and the deserialization to a plain
`{type: 'Buffer', data: [...]}` object. The second commit replaces the
warning, for values that are `ArrayBuffer.isView`, with one that names
the actual cause and the fix: the data is serialized through `toJSON`
instead of as binary, so a `Uint8Array` or `ArrayBuffer` should be
passed to send binary data. The new branch only fires for a typed array
that also carries a `toJSON`, which in practice is a Node `Buffer`; a
plain `Uint8Array` or `ArrayBuffer` has no `toJSON`, never reaches this
branch, and continues to serialize as binary.
2026-06-24 21:04:03 +02:00
Alexander LichterandGitHub 1a528d49bc [Float] Support fetchPriority for module resources (#36835) 2026-06-24 19:41:46 +02:00
Minh VuandGitHub 45b8774c8e Avoid HTML injection in standalone errors (#36839)
## Summary
- Render standalone DevTools server errors with DOM nodes instead of
HTML strings.
- Preserve the existing error box classes and copy while inserting error
text with `textContent`.

## How did you test this?
- `corepack yarn prettier`
- `corepack yarn lint packages/react-devtools-core/src/standalone.js`
2026-06-23 11:28:42 +01:00