Commit Graph

7016 Commits

Author SHA1 Message Date
Ruslan Lesiutin
37bcdcde04 fix[devtools]: feature-check document with typeof instead of direct reference (#35343)
Follow-up to https://github.com/facebook/react/pull/35296.

We can get `ReferenceError` if this is unavailable. Using `typeof` check
instead for safety.
2025-12-11 12:15:00 +00:00
Ruslan Lesiutin
5a970933c0 fix[devtools]: feature-check structure stack trace methods (#35293)
`Error.prepareStackTrace` is non-standard feature and not all JavaScript
runtimes implement the methods that we are using in React DevTools
backend.

This PR adds additional checks for the presence of the methods that we
are using.
2025-12-10 19:21:54 +00:00
Ruslan Lesiutin
5d80124345 fix[devtools]: still show overlay, if getClientRects is not implemented (#35294)
Follow-up to https://github.com/facebook/react/pull/34653.

React Native doesn't implement `getClientRect`, since this is applicable
to CSS box, which is not a concept for Native (maybe yet).

I am loosening the condition that gates `showOverlay()` call to pass if
`getClientRect` is not implemented.

Conceptually, everything that is inside `react-devtools-shared/backend`
should be Host-agnostic, because both on Web and Native it is installed
inside the Host JavaScript runtime, be it main frame of the page, or RN
instance. Since overlay & highlighting logic also lives there, it should
also follow these principles.
2025-12-10 19:21:28 +00:00
Jack Pope
eade0d0fb7 Attach instance handle to DOM in DEV for enableInternalInstanceMap (#35341)
Continue attaching `internalInstanceKey` to DOM nodes in DEV. This
prevents breaking some internal dev tooling while we experiment with the
broader change. Note that this does not reference the DOM handle within
the flag, just attaches it and deletes it. Internals tracking is still
done through the private map.
2025-12-10 13:35:20 -05:00
emily8rown
d763f3131e [Devtools] Navigating commits performance panel hotkey (#35238)
## Summary
Add keyboard shortcuts (Cmd/Ctrl + Left/Right arrow keys) to navigate
between commits in the Profiler's snapshot view.

Moved `filteredCommitIndices` management and commit navigation logic
(`selectNextCommitIndex`, `selectPrevCommitIndex`) from
`SnapshotSelector` into `useCommitFilteringAndNavigation` used by
`ProfilerContext` to enable keyboard shortcuts from the top-level
Profiler component.

## How did you test this change?
- New tests in ProfilerContext-tests
- Built browser extension: `yarn build:<browser name>`
- tested in browser: `yarn run test:<browser name>`
- Manually verified Left/Right arrow navigation cycles through commits
- Verified navigation respects commit duration filter
- Verified reload-and-profile button unaffected

Chrome:


https://github.com/user-attachments/assets/01d2a749-13dc-4d08-8bcb-3d4d45a5f97c

Edge with duration filter:


https://github.com/user-attachments/assets/a7f76ff7-2a0b-4b9c-a0ce-d4449373308b

firefox mixing hotkey with clicking arrow buttons:


https://github.com/user-attachments/assets/48912d68-7c75-40f2-a203-5e6d7e6b2d99
2025-12-10 13:07:35 +00:00
lauren
734f1bf1ac [eprh] Enable enableUseKeyedState and enableVerboseNoSetStateInEffect (#35338)
Temporarily enables these 2 flags for internal testing.
2025-12-09 14:20:22 -05:00
dan
61331f3c9e Fix ViewTransition crash in Mobile Safari (#35337)
Speculative fix to https://github.com/facebook/react/issues/35336
written by Claude.

I have verified that applying a similar patch locally to the repro from
#35336 does fix the crash.

I'm not familiar enough with the underlying APIs to tell whether the fix
is correct or sufficient.
2025-12-10 03:35:15 +09:00
Sebastian "Sebbie" Silbermann
380778d296 [test] Cleanup stack assertions in tests mixing React Server and Client (#35316) 2025-12-07 17:45:06 +01:00
Ruslan Lesiutin
ad5971febd fix[devtools]: no-op unsupported backend bridge events (#35296)
Follow-up to https://github.com/facebook/react/pull/34641.
Similar to https://github.com/facebook/react/pull/35293,
https://github.com/facebook/react/pull/35294.

React DevTools backend can be used in non-DOM environments, so we have
to feature-check some DOM APIs.
For now I am just no-oping newly added commands for Native, we should
revisit this decision once we would roll out Suspense panel there, if
needed. I am not sure if scrolling will be required as much as it is
needed on Web.

`isReactNativeEnvironment()` check is kinda clowny, but we've been
relying on it for quite some time already.
2025-12-05 16:41:58 +00:00
Sebastian "Sebbie" Silbermann
378973b387 [Flight] Move react-server-dom-webpack/*.unbundled to private react-server-dom-unbundled (#35290)
Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2025-12-05 03:59:21 +01:00
Sebastian Markbåge
3016ff87d8 [Flight] Never parse "then" functions (#35289)
AFAIK this is not needed to prevent any exploit but we don't really need
this. We allow functions on pretty much any other object anyway but
never on the "then" property since those would be serialized as Promises
by the client anyway.
2025-12-04 19:05:52 -05:00
Kyℓe Hensel
66ae640b36 [eprh] fix react-compiler rules missing meta.docs.url property (#35258)
## Summary

To help people access the documentation easier, we can [add
`meta.docs.url`](https://eslint.org/docs/latest/extend/custom-rules#:~:text=Specifies%20the%20URL)
to the new react-compiler rules. This allows IDEs to make the rule name
a clickable link.

## How did you test this change?

`yarn test`, `yarn prettier`, `yarn lint` and in a separate project
[using file:// URLs](https://stackoverflow.com/a/38417065)
2025-12-04 15:28:13 -05:00
Felipe Cardozo
bf1afade8d [react-dom/server] Fix hanging on Deno (#35235) 2025-12-04 06:50:27 +01:00
Sebastian Markbåge
7dc903cd29 Patch FlightReplyServer with fixes from ReactFlightClient (#35277)
FlightReplyServer are for client->server and ReactFlightClient is for
server->client. They're not 100% symmetrical.

We did a number of refactors to ReactFlightClient in PRs like #29823 and
#33664 to change the structure of the resolution. This PR brings those
changes to synchronize the two approaches. Which addresses deep
resolution of cycles and deferred error handling.

This also fixes a critical security vulnerability.
2025-12-03 10:41:19 -05:00
Sebastian "Sebbie" Silbermann
6875c3eab4 [test] Only run tests overriding fallback and error states in supported versions (#35234) 2025-11-27 16:15:14 +01:00
Sebastian "Sebbie" Silbermann
74fa1667a7 [DevTools] Move "Back to full tree view" into Activity list item (#35164) 2025-11-26 10:42:18 +01:00
emily8rown
fd524fe02a [DevTools] hotkey to start/stop profiling (#35160)
## Summary

The built-in browser profiler supports starting/stopping with Cmd+E. For
Symmetry this adds the same hotkey for react devtools profiler.

## How did you test this change?
yarn build:\<browser name\> 
yarn run test:\<browser name\>

<img width="483" height="135" alt="Screenshot 2025-11-17 at 14 30 34"
src="https://github.com/user-attachments/assets/426939aa-15da-4c21-87a4-e949e6949482"
/>

firefox:

https://github.com/user-attachments/assets/6f225b90-828f-4e79-a364-59d6bc942f83

edge:

https://github.com/user-attachments/assets/5b2e9242-f0e8-481b-99a2-2dd78099f3ac

chrome:

https://github.com/user-attachments/assets/790aab02-2867-4499-aec1-e32e38c763f9

---------

Co-authored-by: Ruslan Lesiutin <28902667+hoxyq@users.noreply.github.com>
2025-11-21 11:37:10 -05:00
Joseph Savona
50e7ec8a69 [compiler] Deprecate noEmit, add outputMode (#35112)
This deprecates the `noEmit: boolean` flag and adds `outputMode:
'client' | 'client-no-memo' | 'ssr' | 'lint'` as the replacement.
OutputMode defaults to null and takes precedence if specified, otherwise
we use 'client' mode for noEmit=false and 'lint' mode for noEmit=true.

Key points:
* Retrying failed compilation switches from 'client' mode to
'client-no-memo'
* Validations are enabled behind
Environment.proto.shouldEnableValidations, enabled for all modes except
'client-no-memo'. Similar for dropping manual memoization.
* OptimizeSSR is now gated by the outputMode==='ssr', not a feature flag
* Creation of reactive scopes, and related codegen logic, is now gated
by outputMode==='client'
2025-11-20 15:12:40 -08:00
Sebastian "Sebbie" Silbermann
8ac5f4eb36 Fix form status reset when component state is updated (#34075)
Co-authored-by: Vordgi <sasha2822222@gmail.com>
2025-11-19 18:22:07 +01:00
Sebastian Markbåge
eb89912ee5 Add expertimental optimisticKey behind a flag (#35162)
When dealing with optimistic state, a common problem is not knowing the
id of the thing we're waiting on. Items in lists need keys (and single
items should often have keys too to reset their state). As a result you
have to generate fake keys. It's a pain to manage those and when the
real item comes in, you often end up rendering that with a different
`key` which resets the state of the component tree. That in turns works
against the grain of React and a lot of negatives fall out of it.

This adds a special `optimisticKey` symbol that can be used in place of
a `string` key.

```js
import {optimisticKey} from 'react';
...
const [optimisticItems, setOptimisticItems] = useOptimistic([]);
const children = savedItems.concat(
  optimisticItems.map(item =>
    <Item key={optimisticKey} item={item} />
  )
);
return <div>{children}</div>;
```

The semantics of this `optimisticKey` is that the assumption is that the
newly saved item will be rendered in the same slot as the previous
optimistic items. State is transferred into whatever real key ends up in
the same slot.

This might lead to some incorrect transferring of state in some cases
where things don't end up lining up - but it's worth it for simplicity
in many cases since dealing with true matching of optimistic state is
often very complex for something that only lasts a blink of an eye.

If a new item matches a `key` elsewhere in the set, then that's favored
over reconciling against the old slot.

One quirk with the current algorithm is if the `savedItems` has items
removed, then the slots won't line up by index anymore and will be
skewed. We might be able to add something where the optimistic set is
always reconciled against the end. However, it's probably better to just
assume that the set will line up perfectly and otherwise it's just best
effort that can lead to weird artifacts.

An `optimisticKey` will match itself for updates to the same slot, but
it will not match any existing slot that is not an `optimisticKey`. So
it's not an `any`, which I originally called it, because it doesn't
match existing real keys against new optimistic keys. Only one
direction.
2025-11-18 16:29:18 -05:00
Sebastian "Sebbie" Silbermann
194c12d949 [DevTools] Name root "Transition" when focusing on Activity (#35108) 2025-11-18 10:16:58 +01:00
Sebastian "Sebbie" Silbermann
7f1a085b28 [DevTools] Show list of named Activities in Suspense tab (#35092) 2025-11-18 09:52:44 +01:00
Sebastian "Sebbie" Silbermann
7df96b0c1a [Flight] Complete list of Node.js' internal Promise awaits (#35161) 2025-11-17 19:28:26 +01:00
Sebastian "Sebbie" Silbermann
45bc3c9f04 [Flight] Reduce risk of maximum call stack exceeded when emitting async sequence (#35159) 2025-11-17 18:54:13 +01:00
Hendrik Liebau
fb2177c153 [Flight] Fix pending chunks count for streams & async iterables in DEV (#35143)
In DEV, we need to prevent the response from being GC'd while there are
still pending chunks for ReadableSteams or pending results for
AsyncIterables.

Co-authored-by: Sebastian "Sebbie" Silbermann <silbermann.sebastian@gmail.com>
2025-11-14 23:52:11 +01:00
Hendrik Liebau
93fc57400b [Flight] Fix broken byte stream parsing caused by buffer detachment (#35127)
This PR fixes a critical bug where `ReadableStream({type: 'bytes'})`
instances passed through React Server Components (RSC) would stall after
reading only the first chunk or the first few chunks in the client. This
issue was masked by using `web-streams-polyfill` in tests, but manifests
with native Web Streams implementations.

The root cause is that when a chunk is enqueued to a
`ReadableByteStreamController`, the spec requires the underlying
ArrayBuffer to be synchronously transferred/detached. In the React
Flight Client's chunk parsing, embedded byte stream chunks are created
as views into the incoming RSC stream chunk buffer using `new
Uint8Array(chunk.buffer, offset, length)`. When embedded byte stream
chunks are enqueued, they can detach the shared buffer, leaving the RSC
stream parsing in a broken state.

The fix is to copy embedded byte stream chunks before enqueueing them,
preventing buffer detachment from affecting subsequent parsing. To not
affect performance too much, we use a zero-copy optimization: when a
chunk ends exactly at the end of the RSC stream chunk, or when the row
spans into the next RSC chunk, no further parsing will access that
buffer, so we can safely enqueue the view directly without copying.

We now also enqueue embedded byte stream chunks immediately as they are
parsed, without waiting for the full row to complete.

To simplify the logic in the client, we introduce a new `'b'` protocol
tag specifically for byte stream chunks. The server now emits `'b'`
instead of `'o'` for `Uint8Array` chunks from byte streams (detected via
`supportsBYOB`). This allows the client to recognize byte stream chunks
without needing to track stream IDs.

Tests now use the proper Jest environment with native Web Streams
instead of polyfills, exposing and validating the fix for this issue.
2025-11-13 21:23:02 +01:00
Sebastian "Sebbie" Silbermann
093b3246e1 [react-dom] Batch updates from resize until next frame (#35117) 2025-11-13 13:30:21 +01:00
Ricky
bbe3f4d322 [flags] disableLegacyMode in native-fb (#35120)
this is failing test too because we need the legacy mode in the react
package until we fix the tests
2025-11-12 15:38:58 -05:00
Sebastian "Sebbie" Silbermann
1ea46df8ba [DevTools] Batch updates when updating component filters (#35093) 2025-11-11 23:20:22 +01:00
Sebastian "Sebbie" Silbermann
8c15edd57c [DevTools] Send root unmount as a regular removal operation (#35107) 2025-11-11 23:08:54 +01:00
Ricky
04ee54cd12 [tests] add more portal activity tests (#35095)
I copied some tests from
[`Activity-test.js`](1d68bce19c/packages/react-reconciler/src/__tests__/Activity-test.js)
and made them portal specific just to confirm my understanding of how
Portals + Activity interact is correct. Seems good to include them.
2025-11-11 12:47:56 -05:00
Sebastian "Sebbie" Silbermann
ce4054ebdd [DevTools] Measure when reconnecting Suspense (#35098) 2025-11-10 20:55:31 +01:00
Sebastian "Sebbie" Silbermann
21c1d51acb [DevTools] Don't attempt to draw bounding box if inspected element is not a Suspense (#35097) 2025-11-10 20:01:59 +01:00
Facebook Community Bot
be48396dbd Remove Dead Code in WWW JS
Differential Revision: D86593830

Pull Request resolved: https://github.com/facebook/react/pull/35085
2025-11-10 16:34:01 +00:00
Andrew Clark
5268492536 Fix: Activity should hide portal contents (#35091)
This PR updates the behavior of Activity so that when it is hidden, it
hides the contents of any portals contained within it.

Previously we had intentionally chosen not to implement this behavior,
because it was thought that this concern should be left to the userspace
code that manages the portal, e.g. by adding or removing the portal
container from the DOM. Depending on the use case for the portal, this
is often desirable anyway because the portal container itself is not
controlled by React.

However, React does own the _contents_ of the portal, and we can hide
those elements regardless of what the user chooses to do with the
container. This makes the hiding/unhiding behavior of portals with
Activity automatic in the majority of cases, and also benefits from
aligning the DOM mutations with the rest of the React's commit phase
lifecycle.

The reason we have to special case this at all is because usually we
only hide the direct DOM children of the Activity boundary. There's no
reason to go deeper than that, because hiding a parent DOM element
effectively hides everything inside of it. Portals are the exception,
because they don't exist in the normal DOM hierarchy; we can't assume
that just because a portal has a parent in the React tree that it will
also have that parent in the actual DOM.

So, whenever an Activity boundary is hidden, we must search for and hide
_any_ portal that is contained within it, and recursively hide its
direct children, too.

To optimize this search, we use a new subtree flag, PortalStatic, that
is set only on fiber paths that contain a HostPortal. This lets us skip
over any subtree that does not contain a portal.
2025-11-10 10:42:26 -05:00
Sebastian Markbåge
c83be7da9f [Fizz] Simplify createSuspenseBoundary path (#35087)
Small follow up to #35068.

Since this is now a single argument we can simplify the creation
branching a bit and make sure it's const.
2025-11-09 15:19:43 -05:00
Sebastian Markbåge
6362b5c711 [DevTools] Special case the selected root outline (#35071)
When I moved the outline to above all other rects, I thought it was
clever to unify with the root so that the outline was also used for the
root selection. But the root outline is not drawn like the other rects.
It's outside the padding and doesn't have the 1px adjustment which leads
the overlay to be slightly inside the other rect instead of above it.

This goes back to just having the selected root be drawn by the root
element.

Before:

<img width="652" height="253" alt="Screenshot 2025-11-07 at 11 39 28 AM"
src="https://github.com/user-attachments/assets/334237d1-f190-4995-94cc-9690ec0f7ce1"
/>

After:

<img width="674" height="220" alt="Screenshot 2025-11-07 at 11 44 01 AM"
src="https://github.com/user-attachments/assets/afaa86d8-942a-44d8-a1a5-67c7fb642c0d"
/>
2025-11-09 15:03:31 -05:00
Sebastian "Sebbie" Silbermann
5a9921b839 [DevTools] Apply Activity slice filter when double clicking Activity (#34908) 2025-11-08 18:09:44 +01:00
Andrew Clark
717e70843e Fix: Errors should not escape a hidden Activity (#35074)
If an error is thrown inside a hidden Activity, it should not escape
into the visible part of the UI. Conceptually, a hidden Activity
boundary is not part of the current UI; it's the same as an unmounted
tree, except for the fact that the state will be restored if it's later
revealed.

Fixes:
- https://github.com/facebook/react/issues/35073
2025-11-07 18:18:24 -05:00
Stian Jensen
a10ff9c857 Upgrade devtools dependency update-notifier to 5.x (#31655)
## Summary

This PR upgrades the dependency on update-notifier, used in
react-devtools, to 5.x
This is the latest non-ESM version, so upgrading to it should be
unproblematic (while updating to 6.x and beyond will have to wait).

Upgrading means we avoid installing a lot of outdated dependencies (as
can be seen from the diff in yarn.lock), and resolves part of
https://github.com/facebook/react/issues/28058

Changelog:
https://github.com/yeoman/update-notifier/releases

The most relevant breaking change seems to be that the minimum support
node version is increased from v6 to v10, but I couldn't find what is
currently React's official node version support.

## How did you test this change?

I ran the test-suite locally (`yarn test` in root folder), but I'm not
sure if that one actually covers devtools?

I also built and tested this version of devtools with some internal
company projects (both react and react-native based) – following
guidelines from
https://github.com/facebook/react/issues/28058#issuecomment-1943619292.
2025-11-07 18:43:42 +00:00
Sebastian Markbåge
fa50caf5f8 [Fizz] Unify preamble only fields to save a field (#35068)
Stacked on #35067.

Same idea of saving a field on the SuspenseBoundary in the common case.
The case where they can have a preamble is rare.
2025-11-07 09:19:19 -05:00
Sebastian Markbåge
1e986f514f [Fizz] Unify prerender only fields to save a field (#35067)
I need to regain a field because the SuspenseBoundary type is already at
16 fields in prod, after which it deopts v8.

There are two fields that are only used in prerender to track postpones.
These are ripe to be split into an optional object so that they only
take up one field when they're not used.
2025-11-07 09:18:28 -05:00
Sebastian Markbåge
38bdda1ca6 Don't skip content in visible offscreen trees for Gesture View Transitions (#35066)
Follow up to #35063.

I forgot there's another variant of this in the ApplyGesture path.
2025-11-06 20:59:08 -05:00
Jack Pope
a44e750e87 Store instance handles in an internal map behind flag (#35053)
We already append `randomKey` to each handle name to prevent external
libraries from accessing and relying on these internals. But more
libraries recently have been getting around this by simply iterating
over the element properties and using a `startsWith` check.

This flag allows us to experiment with moving these handles to an
internal map.

This PR starts with the two most common internals, the props object and
the fiber. We can consider moving additional properties such as the
container root and others depending on perf results.
2025-11-06 18:17:53 -05:00
Sebastian Markbåge
37b089a59c Don't skip content in visible offscreen trees for View Transitions (#35063)
Also, don't not skip hidden trees.

Memoized state is null when an Offscreen boundary (Suspense or Activity)
is visible.

This logic was inversed in a couple of View Transition checks which
caused pairs to be discovered or not discovered incorrectly for
insertion and deletion of Suspense or Activity boundaries.
2025-11-06 16:03:02 -05:00
Sebastian Markbåge
1a31a814f1 Escape View Transition Name Strings as base64 (#35060)
This is an alternative to #35059.

If the name needs escaping, then instead of escaping it, we just use a
base64 name. This wouldn't allow you to match on an escaped name in your
own CSS like you should be able to if browsers worked properly. But at
least it would provide matching name in current browsers which is
probably sufficient if you're using auto-generated names.

This also covers some cases where `CSS.escape()` isn't sufficient anyway
like when the name ends in a dot.
2025-11-06 16:02:06 -05:00
Sebastian Markbåge
fa767dade6 Remove unstable_expectedLoadTime option (#35051)
Follow up to #35022.

It's now replaced by the `defer` option.

Sounds like nobody is actually using this option, including Meta, so we
can just delete it.
2025-11-05 15:52:21 -05:00
Sebastian Markbåge
0ba2f01f74 Rename <Suspense unstable_expectedLoadTime> to <Suspense defer> and implement in SSR (#35022)
We've long had the CPU suspense feature behind a flag under the terrible
API `unstable_expectedLoadTime={arbitraryNumber}`. We've known for a
long time we want it to just be `defer={true}` (or just `<Suspense
defer>` in the short hand syntax). So this adds the new name and warns
for the old name.

For only the new name, I also implemented SSR semantics in Fizz. It has
two effects here.
1) It renders the fallback before the content (similar to prerender)
allowing siblings to complete quicker.
2) It always outlines the result. When streaming this should really
happen naturally but if you defer a prerendered content it also implies
that it's expensive and should be outlined. It gives you a opt-in to
outlining similar to suspensey images and css but let you control it
manually.
2025-11-05 14:12:09 -05:00
Sebastian Markbåge
dd048c3b2d Clean up enablePostpone Experiment (#35048)
We're not shipping this and it's a lot of code to maintain that is
blocking my refactor of Fizz for SuspenseList.
2025-11-05 00:05:59 -05:00
Sebastian Markbåge
c308cb5905 Disable enablePostpone flag in experimental (#31042)
I don't think we're ready to land this yet since we're using it to run
other experiments and our tests. I'm opening this PR to indicate intent
to disable and to ensure tests in other combinations still work. Such as
enableHalt without enablePostpone. I think we'll also need to rewrite
some tests that depend on enablePostpone to preserve some coverage.

The conclusion after this experiment is that try/catch around these are
too likely to block these signals and consider them error. Throwing
works for Hooks and `use()` because the lint rule can ensure that
they're not wrapped in try/catch. Throwing in arbitrary functions not
quite ecosystem compatible. It's also why there's `use()` and not just
throwing a Promise. This might also affect the Catch proposal.

The "prerender" for SSR that's supporting "Partial Prerendering" is
still there. This just disables the `React.postpone()` API for creating
the holes.
2025-11-04 23:23:25 -05:00