Commit Graph

333 Commits

Author SHA1 Message Date
Sebastian "Sebbie" Silbermann
6b70072c4f [DevTools] Finalize heuristic for naming unnamed <Suspense> (#34428) 2025-09-09 17:56:26 +02:00
Sebastian "Sebbie" Silbermann
8943025358 [DevTools] Fix handling of host roots on mount (#34400) 2025-09-08 22:53:02 +02:00
KimCookieYa
c4e2508dad [react-devtools-shared] Fix URL construction when base URL is invalid (#34407)
### Problem
- Users encounter “Failed to construct 'URL': Invalid base URL” when
clicking the “View source” action in DevTools if the underlying base URL
is invalid.
- This exception originates from `new URL(relative, base)` and bubbles
up, interrupting the DevTools UI.
- Fixes GitHub issue
[#34317](https://github.com/facebook/react/issues/34317)

### Solution
- Wrap URL construction to:
  - First try `new URL(sourceMapAt, sourceURL)`.
  - If that fails, try `new URL(sourceMapAt)` as an absolute URL.
  - If both fail, return `null` (no symbolication) rather than throwing.
- This preserves normal behavior for valid bases and absolute URLs,
while avoiding crashes for invalid bases.

### Implementation details
- Updated `symbolicateSource` in
`packages/react-devtools-shared/src/symbolicateSource.js` to handle
invalid base URL scenarios without throwing.
- Added/verified tests in
`packages/react-devtools-shared/src/__tests__/utils-test.js`:
- “should not throw for invalid base URL with relative source map” →
resolves to `null`.
- “should resolve absolute source map even if base URL is invalid” →
still resolves correctly.

### Test plan
- Lint/format:
  - `yarn prettier-check`
  - `yarn linc`
- Type checking:
  - `yarn flow dom-node`
- Unit tests:
  - `yarn test --watchAll=false utils-test`
  - Optionally: `yarn test --watchAll=false utils-test inspectedElement`
- All of the above pass locally for experimental channel.

### Risks and rollout
- Risk: Low. Only affects cases where the base URL is invalid.
- Normal cases (valid base or absolute `sourceMappingURL`) are
unchanged.
- No user-facing API changes; DevTools UX becomes more resilient.

### Affected packages
- `react-devtools-shared`

### Related
- Fixes GitHub issue
[#34317](https://github.com/facebook/react/issues/34317)

### Checklist
- [x] Ran `yarn prettier-check`
- [x] Ran `yarn linc`
- [x] Ran `yarn flow dom-node`
- [x] Relevant unit tests passing
- [x] Linked issue and added a concise summary


<!--
  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.
-->
2025-09-06 14:00:45 +01:00
Sebastian "Sebbie" Silbermann
ba6590dd7c [DevTools] Rerender boundaries when they unsuspend when advancing the timeline (#34359) 2025-09-04 10:49:16 +02:00
Sebastian "Sebbie" Silbermann
cb1e73be04 [DevTools] Batch Suspense toggles when advancing the Suspense timeline (#34251) 2025-08-26 17:22:30 +02:00
Sebastian Markbåge
a85ec041d6 [DevTools] Ignore List Stack Traces (#34210)
Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2025-08-22 00:03:05 +02:00
Sebastian "Sebbie" Silbermann
ae5c2f82b3 [DevTools] Handle reorders when resuspending while fallback contains Suspense (#34225) 2025-08-19 20:22:54 +02:00
Sebastian "Sebbie" Silbermann
8dba9311e5 [DevTools] Handle fallback unmount in Suspense update path (#34199) 2025-08-15 19:40:35 +02:00
Sebastian "Sebbie" Silbermann
379a083b9a Include stack of cause in React instrumentation errors (#34198) 2025-08-13 19:18:02 +02:00
Sebastian "Sebbie" Silbermann
de06211dbe [DevTools] Send Suspense rects to frontend (#34170) 2025-08-12 16:48:35 +02:00
Sebastian Markbåge
d587434c35 [DevTools] Pick up suspended by info from use() (#34148)
Similar to #34144 but for `use()`.

`use()` dependencies don't get added to the `fiber._debugInfo` set
because that just models the things blocking the children, and not the
Fiber component itself. This picks up any debug info from the thenable
state that we stashed onto `_debugThenableState` so that we know it used
`use()`.

<img width="593" height="425" alt="Screenshot 2025-08-09 at 4 03 40 PM"
src="https://github.com/user-attachments/assets/c7e06884-4efd-47fa-a76b-132935db6ddc"
/>

Without #34146 this doesn't pick up uninstrumented promises but after
it, it'll pick those up as well. An instrumented promise that doesn't
have anything in its debug info is not picked up. For example, if it
didn't depend on any I/O on the server.

This doesn't yet pick up the stack trace of the `use()` call. That
information is in the Hooks information but needs a follow up to extract
it.
2025-08-11 12:10:05 -04:00
Sebastian Markbåge
7a934a16b8 [DevTools] Show Owner Stacks in "rendered by" View (#34130)
This shows the stack trace of the JSX at each level so now you can also
jump to the code location for the JSX callsite. The visual is similar to
the owner stacks with `createTask` except when you click the `<...>` you
jump to the Instance in the Components panel.

<img width="593" height="450" alt="Screenshot 2025-08-08 at 12 19 21 AM"
src="https://github.com/user-attachments/assets/dac35faf-9d99-46ce-8b41-7c6fe24625d2"
/>

I'm not sure it's really necessary to have all the JSX stacks of every
owner. We could just have it for the current component and then the rest
of the owners you could get to if you just click that owner instance.

As a bonus, I also use the JSX callsite as the fallback for the "View
Source" button. This is primarily useful for built-ins like `<div>` and
`<Suspense>` that don't have any implementation to jump to anyway. It's
useful to be able to jump to where a boundary was defined.
2025-08-11 11:41:30 -04:00
Sebastian Markbåge
59ef3c4baf [DevTools] Allow Introspection of React Elements and React.lazy (#34129)
With RSC it's common to get React.lazy objects in the children position.
This first formats them nicely.

Then it adds introspection support for both lazy and elements.

Unfortunately because of quirks with the hydration mechanism we have to
expose it under the name `_payload` instead of something direct. Also
because the name "type" is taken we can't expose the type field on an
element neither. That whole algorithm could use a rewrite.

<img width="422" height="137" alt="Screenshot 2025-08-07 at 11 37 03 PM"
src="https://github.com/user-attachments/assets/a6f65f58-dbc4-4b8f-928b-d7f629fc51b2"
/>

<img width="516" height="275" alt="Screenshot 2025-08-07 at 11 36 36 PM"
src="https://github.com/user-attachments/assets/650bafdb-a633-4d78-9487-a750a18074ce"
/>

For JSX an alternative or additional feature might be instead to jump to
the first Instance that was rendered using that JSX. We know that based
on the equality of the memoizedProps on the Fiber. It's just a matter of
whether we do that eagerly or more lazily when you click but you may not
have a match so would be nice to indicate that before you click.
2025-08-11 11:41:14 -04:00
Sebastian Markbåge
738aebdbac [DevTools] Add Badge to Owners and sometimes stack traces (#34106)
Stacked on #34101.

This adds a badge to owners if they are different from the currently
selected component's environment.

<img width="590" height="566" alt="Screenshot 2025-08-04 at 5 15 02 PM"
src="https://github.com/user-attachments/assets/e898254f-1b4c-498e-8713-978d90545340"
/>

We also add one to the end of stack traces if the stack trace has a
different environment than the owner which can happen when you call a
function (without rendering a component) into a third party environment
but the owner component was in the first party.

One awkward thing is that Suspense boundaries are always in the client
environment so their Server Components are always badged.
2025-08-07 10:39:08 -04:00
Sebastian Markbåge
b080063331 [DevTools] Source Map Stack Traces such in await locations (#34094)
Stacked on #34093.

Instead of using the original `ReactStackTrace` that has the call sites
on the server, this parses the `Error` object which has the virtual call
sites on the client. We'll need this technique for things stack traces
suspending on the client anyway like `use()`.

We can then use these callsites to source map in the front end.

We currently don't source map function names but might be useful for
this use case as well as getting original component names from prod.

One thing this doesn't do yet is that it doesn't ignore list the stack
traces on the client using the source map's ignore list setting. It's
not super important since we expect to have already ignore listed on the
server but this will become important for client stack traces like
`use()`.
2025-08-06 13:45:06 -04:00
Sebastian Markbåge
99fd4f2ac1 [DevTools] Reorder moved filtered Fibers with backing DevToolsInstance (#34104)
Instead, we just continue to collect the unfiltered children.

---------

Co-authored-by: Sebastian Sebbie Silbermann <sebastian.silbermann@vercel.com>
2025-08-05 12:39:45 -04:00
Sebastian Markbåge
557745eb0b [DevTools] Add structure full stack parsing to DevTools (#34093)
We'll need complete parsing of stack traces for both owner stacks and
async debug info so we need to expand the stack parsing capabilities a
bit. This refactors the source location extraction to use some helpers
we can use for other things too.

This is a fork of `ReactFlightStackConfigV8` which also supports
DevTools requirements like checking both `react_stack_bottom_frame` and
`react-stack-bottom-frame` as well as supporting Firefox stacks.

It also supports extracting the first frame of a component stack or the
last frame of an owner stack for the source location.
2025-08-04 09:37:46 -04:00
Sebastian Markbåge
5bbf9be246 [DevTools] Model Hidden Offscreen Boundaries as Unmounts (#34062)
This is modeling Offscreen boundaries as the thing that unmounts a tree
in the frontend. This will let us model this as a "hide" that preserves
state instead in a follow up but not yet.

By doing it this way, we don't have to special case suspended Suspense
boundaries, at least not for the modern versions that use Offscreen as
the internal node. It's still special cased for the old React versions.
Instead, this is handled by the Offscreen fiber getting hidden.

By giving this fiber an FilteredFiberInstance, we also have somewhere to
store the children on (separately from the parent children set which can
include other siblings too like the loading state).

One consequence is that Activity boundary content now disappears when
they're hidden which is probably a good thing since otherwise it would
be confusing and noisy when it's used to render multiple pages at once.
2025-07-31 10:30:10 -04:00
Sebastian Markbåge
5d7e8b90e2 [DevTools] Use use() instead of throwing a Promise in Caches (#34033) 2025-07-29 03:45:56 -04:00
Sebastian Markbåge
142fd27bf6 [DevTools] Add Option to Open Local Files directly in External Editor (#33983)
The `useOpenResource` hook is now used to open links. Currently, the
`<>` icon for the component stacks and the link in the bottom of the
components stack. But it'll also be used for many new links like stacks.
If this new option is configured, and this is a local file then this is
opened directly in the external editor. Otherwise it fallbacks to open
in the Sources tab or whatever the standalone or inline is configured to
use.

<img width="453" height="252" alt="Screenshot 2025-07-24 at 4 09 09 PM"
src="https://github.com/user-attachments/assets/04cae170-dd30-4485-a9ee-e8fe1612978e"
/>

I prominently surface this option in the Source pane to make it
discoverable.

<img width="588" height="144" alt="Screenshot 2025-07-24 at 4 03 48 PM"
src="https://github.com/user-attachments/assets/0f3a7da9-2fae-4b5b-90ec-769c5a9c5361"
/>

When this is configured, the "Open in Editor" is hidden since that's
just the default. I plan on deprecating this button to avoid having the
two buttons going forward.

Notably there's one exception where this doesn't work. When you click an
Action or Event listener it takes you to the Sources tab and you have to
open in editor from there. That's because we use the `inspect()`
mechanism instead of extracting the source location. That's because we
can't do the "throw trick" since these can have side-effects. The Chrome
debugger protocol would solve this but it pops up an annoying dialog. We
could maybe only attach the debugger only for that case. Especially if
the dialog disappears before you focus on the browser again.
2025-07-25 10:16:43 -04:00
Sebastian Markbåge
7513996f20 [DevTools] Unify by using ReactFunctionLocation type instead of Source (#33955)
In RSC and other stacks now we use a lot of `ReactFunctionLocation` type
to represent the location of a function. I.e. the location of the
beginning of the function (the enclosing line/col) that is represented
by the "Source" of the function. This is also what the parent Component
Stacks represents.

As opposed to `ReactCallSite` which is what normal stack traces and
owner stacks represent. I.e. the line/column number of the callsite into
the next function.

We can start sharing more code by using the `ReactFunctionLocation` type
to represent the component source location and it also helps clarify
which ones are function locations and which ones are callsites as we
start adding more stack traces (e.g. for async debug info and owner
stack traces).
2025-07-22 10:53:08 -04:00
Jack Pope
4ca97e4891 Clean up enableSiblingPrerendering flag (#32319) 2025-05-08 20:49:23 -04:00
Sebastian "Sebbie" Silbermann
c498bfce8b [devtools] Allow inspecting cause, name, message, stack of Errors in props (#33023) 2025-04-26 07:20:57 +02:00
Sebastian "Sebbie" Silbermann
197d6a0403 [devtools] 1st class support of used Thenables (#32989)
Co-authored-by: Ruslan Lesiutin <rdlesyutin@gmail.com>
2025-04-24 13:46:31 +02:00
Ricky
1a191701fe [refactor] Add element type for Activity (#32499)
This PR separates Activity to it's own element type separate from
Offscreen. The goal is to allow us to add Activity element boundary
semantics during hydration similar to Suspense semantics, without
impacting the Offscreen behavior in suspended children.
2025-03-17 09:17:00 -04:00
François Best
e0131f1eda fix(devtools): Handle nullish values passed to formatConsoleArguments (#32372)
<!--
  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?
-->

When using React Devtools, calling `console.log('%s', null)` in userland
can cause it to throw an error:

```
TypeError: Cannot read properties of null (reading 'toString')
```

## How did you test this change?

Added a unit test.

<!--
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.
-->

See https://github.com/47ng/nuqs/issues/808.
2025-02-13 12:02:55 +00:00
Ruslan Lesiutin
d634548243 DevTools: merge element fields in TreeStateContext (#31956)
Stacked on https://github.com/facebook/react/pull/31892, see commit on
top.

For some reason, there were 2 fields different fields for essentially
same thing: `selectedElementID` and `inspectedElementID`. Basically, the
change is:
```
selectedElementID -> inspectedElementID
selectedElementIndex -> inspectedElementIndex
```

I have a theory that it was due to previously used async approach around
element inspection, and the whole `InspectedElementView` was wrapped in
`Suspense`.
2025-01-09 18:13:24 +00:00
Ruslan Lesiutin
62208bee5a DevTools: fork FastRefresh test for <18 versions of React (#31893)
We currently have a failing test for React DevTools against React 17.
This started failing in https://github.com/facebook/react/pull/30899,
where we changed logic for error tracking and started relying on
`onPostCommitFiberRoot` hook.

Looking at https://github.com/facebook/react/pull/21183,
`onPostCommitFiberRoot` was shipped in 18, which means that any console
errors / warnings emitted in passive effects won't be recorded by React
DevTools for React < 18.
2025-01-02 14:07:21 +00:00
Piotr Tomczewski
a7b829524b [DevTools] Show component names while highlighting renders (#31577)
## Summary
This PR improves the Trace Updates feature by letting developers see
component names directly on the update overlay. Before this change, the
overlay only highlighted updated regions, leaving it unclear which
components were involved. With this update, you can now match visual
updates to their corresponding components, making it much easier to
debug rendering performance.

### New Feature: Show component names while highlighting
When the new **"Show component names while highlighting"** setting is
enabled, the update overlay display the names of affected components
above the rectangles, along with the update count. This gives immediate
context about what’s rendering and why. The preference is stored in
local storage and synced with the backend, so it’s remembered across
sessions.

### Improvements to Drawing Logic
The drawing logic has been updated to make the overlay sharper and
easier to read. Overlay now respect device pixel ratios, so they look
great on high-DPI screens. Outlines have also been made crisper, which
makes it easier to spot exactly where updates are happening.

> [!NOTE]
> **Grouping Logic and Limitations**
> Updates are grouped by their screen position `(left, top coordinates)`
to combine overlapping or nearby regions into a single group. Groups are
sorted by the highest update count within each group, making the most
frequently updated components stand out.
> Overlapping labels may still occur when multiple updates involve
components that overlap but are not in the exact same position. This is
intentional, as the logic aims to maintain a straightforward mapping
between update regions and component names without introducing
unnecessary complexity.

### Testing
This PR also adds tests for the new `groupAndSortNodes` utility, which
handles the logic for grouping and sorting updates. The tests ensure the
behavior is reliable across different scenarios.

## Before & After


https://github.com/user-attachments/assets/6ea0fe3e-9354-44fa-95f3-9a867554f74c


https://github.com/user-attachments/assets/32af4d98-92a5-47dd-a732-f05c2293e41b

---------

Co-authored-by: Ruslan Lesiutin <rdlesyutin@gmail.com>
2024-12-13 11:53:05 +00:00
Henry Q. Dineen
6e29479bff [devtools] allow non-coercible objects in formatConsoleArgumentsToSingleString (#31444)
## Summary

We have been getting unhandled `TypeError: Cannot convert object to
primitive value` errors in development that only occur when using
devtools. I tracked it down to `console.error()` calls coming from
Apollo Client where one of the arguments is an object without a
prototype (created with `Object.create(null)`). This causes
`formatConsoleArgumentsToSingleString()` in React's devtools to error as
the function does not defend against `String()` throwing an error.

My attempted fix is to introduce a `safeToString` function (naming
suggestions appreciated) which expects `String()` to throw on certain
object and in that case falls back to returning `[object Object]`, which
is what `String({})` would return.

## How did you test this change?

Added a new unit test.
2024-11-10 19:24:15 +00:00
Jan Kassens
07aa494432 Remove enableRefAsProp feature flag (#30346)
The flag is fully rolled out.
2024-11-04 14:30:58 -05:00
Ruslan Lesiutin
bf7e210cb5 tests[react-devtools]: added tests for Compiler integration (#31241)
Adds tests for Compiler integration.

This includes:
- Tests against Compiler from source.
- Versioned (18.2 - <19) tests against Compiler from npm.

For tests against React 18.2, I had to download `react-compiler-runtime`
from npm and put it to `react/compiler-runtime.js`.
2024-10-17 09:02:41 +01:00
Ruslan Lesiutin
ec2bf02245 fix[react-devtools]: fixed timeline profiler tests (#31261)
Fixes tests against React 18 after
https://github.com/facebook/react/pull/31154:
- Set `supportsTimeline` to true for `Store`.
- Execute `store.profilerStore.startProfiling` after `legacyRender`
import, because this is where `react-dom` is imported and renderer is
registered. We don't yet propagate `isProfiling` flag to newly
registered renderers, when profiling already started see:

d5bba18b5d/packages/react-devtools-shared/src/hook.js (L203-L204)
2024-10-15 12:46:05 +01:00
Ruslan Lesiutin
6e612587ec chore[react-devtools]: drop legacy context tests (#31059)
We've dropped the support for detecting changes in legacy Contexts in
https://github.com/facebook/react/pull/30896.
2024-10-01 14:26:12 +01:00
Ruslan Lesiutin
9ea5ffa9cb chore[react-devtools]: add legacy mode error message to the ignore list for tests (#31060)
Without this, the console gets spammy whenever we run React DevTools
tests against React 18.x, where this deprecation message was added.
2024-10-01 14:04:05 +01:00
Ruslan Lesiutin
a15bbe1475 refactor: data source for errors and warnings tracking is now in Store (#31010)
Stacked on https://github.com/facebook/react/pull/31009.

1. Instead of keeping `showInlineWarningsAndErrors` in `Settings`
context (which was removed in
https://github.com/facebook/react/pull/30610), `Store` will now have a
boolean flag, which controls if the UI should be displaying information
about errors and warnings.
2. The errors and warnings counters in the Tree view are now counting
only unique errors. This makes more sense, because it is part of the
Elements Tree view, so ideally it should be showing number of components
with errors and number of components of warnings. Consider this example:
2.1. Warning for element `A` was emitted once and warning for element
`B` was emitted twice.
2.2. With previous implementation, we would show `3 ⚠️`, because in
total there were 3 warnings in total. If user tries to iterate through
these, it will only take 2 steps to do the full cycle, because there are
only 2 elements with warnings (with one having same warning, which was
emitted twice).
2.3 With current implementation, we would show `2 ⚠️`. Inspecting the
element with doubled warning will still show the warning counter (2)
before the warning message.

With these changes, the feature correctly works.
https://fburl.com/a7fw92m4
2024-09-24 19:51:21 +01:00
Ruslan Lesiutin
3cac0875dc refactor[react-devtools]: move console patching to global hook (#30596)
Stacked on https://github.com/facebook/react/pull/30566 and whats under
it. See [this
commit](374fd737e4).

It is mostly copying code from one place to another and updating tests.
With these changes, for every console method that we patch, there is
going to be a single applied patch:
- For `error`, `warn`, and `trace` we are patching when hook is
installed. This guarantees that component stacks are going to be
appended even if browser DevTools are not opened. We pay some price for
it, though: if user has browser DevTools closed and if at this point
some warning or error is emitted (logged), the next time user opens
browser DevTools, they are going to see `hook.js` as the source frame.
Unfortunately, ignore listing from source maps is not applied
retroactively, and I don't know if its a bug or just a design
limitations. Once browser DevTools are opened, source maps will be
loaded and ignore listing will be applied for all emitted logs in the
future.
- For `log`, `info`, `group`, `groupCollapsed` we are only patching when
React notifies React DevTools about running in StrictMode. We unpatch
the methods right after it.
2024-09-18 18:12:18 +01:00
Sebastian Markbåge
0eab377a96 Add enableComponentPerformanceTrack Flag (#30960)
This flag will be used to gate a new timeline profiler that's integrate
with the Performance Tab and the new performance.measure extensions in
Chrome.

It replaces the existing DevTools feature so this disables
enableSchedulingProfiler when it is enabled since they can interplay in
weird ways potentially.

This means that experimental React now disable scheduling profiler and
enables this new approach.
2024-09-16 11:09:40 -04:00
Josh Story
94e652d505 disable enableSiblingPrerendering in experimental channel (#30952)
Disables `enableSiblingPrerendering` in the experimental builds until
the feature is tested at Meta first.
2024-09-12 09:33:20 -07:00
Andrew Clark
d6cb4e7713 Start prerendering Suspense retries immediately (#30934)
When a component suspends and is replaced by a fallback, we should start
prerendering the fallback immediately, even before any new data is
received. During the retry, we can enter prerender mode directly if
we're sure that no new data was received since we last attempted to
render the boundary.

To do this, when completing the fallback, we leave behind a pending
retry lane on the Suspense boundary. Previously we only did this once a
promise resolved, but by assigning a lane during the complete phase, we
will know that there's speculative work to be done.

Then, upon committing the fallback, we mark the retry lane as suspended
— but only if nothing was pinged or updated in the meantime. That allows
us to immediately enter prerender mode (i.e. render without skipping any
siblings) when performing the retry.
2024-09-11 11:41:54 -04:00
Sebastian Markbåge
0dbacf2041 [DevTools] Improve Layering Between Console and Renderer (#30925)
The console instrumentation should not know about things like Fibers.
Only the renderer bindings should know about that stuff. We can improve
the layering by just moving all that stuff behind a `getComponentStack`
helper that gets injected by the renderer.

This sets us up for the Flight renderer #30906 to have its own
implementation of this function.
2024-09-09 15:33:30 -04:00
Sebastian Markbåge
e07235b980 [DevTools] Refactor Error / Warning Count Tracking (#30899)
We can simplify this tracking by not having a separate pending set of
logs and the logs tracked per instance and instead we just track the
logs per Fiber. This avoids the need to move it back into the pending
set after unmounts in case a Fiber is reparented.

The main motivation for this is to unify with an upcoming tracking of
logs for Server Components. For those it doesn't make sense to move them
into a per instance set and because the same Server Component - and its
logs - may appear more than once. So no particular instance should steal
it.

The second part of this change is that instead of looking up the
instance from fiber, which requires the fiberToFiberInstanceMap, we
instead look up if a component has any new logs when we traverse it in
the commit phase. After all for a component to have had a log it must
have updated. This is a similar technique to #30897. This technique also
works for Server Components without having to maintain a one to many
relationship from ComponentInfo to VirtualInstance. So it unifies them.

Normally this look up would be fast since the `fiberToComponentsLogs`
set would be empty and so doesn't add any significant weight to the
commit phase. If there's a ton of logs on many different components then
it's not great since it would slow down the commit phase but that's not
what we expect to see so in typical usage, this is better.

There is an unfortunate consequence though which is that
`console.warn/error` in passive effects (i.e. `useEffect`) wouldn't be
picked up because currently we traverse the logs in
`handleCommitFiberRoot` which is too early. If we moved that to
`handlePostCommitFiberRoot` this wouldn't be a problem. In the meantime,
I just detect this and do a brute force flush by walking all mounted
instances if there's a `console.warn/error` inside a passive effect.

If we ever add "owners" to event handlers that are triggered outside the
render/commit phases (like `<div onClick={...}>`) and we want to
associate error/warnings in those, we'd need a different technique to
ensure those get flushed in time.
2024-09-09 15:10:04 -04:00
Sebastian Markbåge
6292398241 [DevTools] Simplify Context Change Tracking in Profiler (#30896)
When Context change tracking was added to support modern Context it
relied on the "memoizedValue" to read the current value. This only works
in React 18+ when it was added to support Lazy Context Propagation.
However, the backend stored the old value the same way it used to work
for legacy Context in a global map. This was unnecessary since we *also*
have the old value on the previous Fiber.

This removes all the costly tracking of previous values for every Fiber
that uses Contexts slowing down profiling. Instead, we just compare the
Contexts from

The downside is that this no longer supports detecting changes due to
legacy Context because it doesn't have a similar "previous" value.
However, legacy Context has long been deprecated and is completely
removed in 19. So I don't think it's worth supporting since you have to
be on an old version *and* actually use legacy Context *and* trying to
profile something that updates it. Which btw, updating legacy contexts
only worked at all from 16 something when we made updates work. So it
was unusual even in the slight gap where you could and before you had
migrated to modern Context introduced in 16.3.
2024-09-06 21:58:20 -04:00
Sebastian Markbåge
d76a5651f4 [DevTools] Handle reordered contexts in Profiler (#30887)
While looking at the Context tracking implementation for other reasons I
noticed this bug.

Originally it wasn't allowed to have conditional `useContext(context)`
(although we did because it's technically possible). With `use(context)`
it is officially allowed to be conditional as long as it is within a
Hook/Component and not within a try/catch.

This means that this loop comparing previous and next contexts need to
consider that the Context objects might not line up and so it's possibly
comparing apples to oranges. We already bailed if one was longer than
the other.

If the order of contexts changes later in the component that means
something else must have already changed earlier so the reason for the
rerender isn't the context so we can just return false in that case.
2024-09-06 21:45:52 -04:00
Sebastian Markbåge
a06cd9e1d1 [DevTools] Refactor Forcing Fallback / Error of Suspense / Error Boundaries (#30870)
First, this basically reverts
1f3892ef8c
to use a Map/Set to track what is forced to suspend/error again instead
of flags on the Instance. The difference is that now the key in the
Fiber itself instead of the ID. Critically this avoids the
fiberToFiberInstance map to look up whether or not a Fiber should be
forced to suspend when asked by the renderer.

This also allows us to force suspend/error on filtered instances. It's a
bit unclear what should happen when you try to Suspend or Error a child
but its parent boundary is filtered. It was also inconsistent between
Suspense and Error due to how they were implemented.

I think conceptually you're trying to simulate what would happen if that
Component errored or suspended so it would be misleading if we triggered
a different boundary than would happen in real life. So I think we
should trigger the nearest unfiltered Fiber, not the nearest Instance.
The consequence of this however is that if this instance was filtered,
there's no way to undo it without refreshing or removing the filter.
This is an edge case though since it's unusual you'd filter these in the
first place.

It used to be that Suspense walked the store in the frontend and Error
walked the Fibers in the backend. They also did this somewhat eagerly.
This simplifies and unifies the model by passing the id of what you
clicked in the frontend and then we walk the Fiber tree from there in
the backend to lazily find the boundary. However I also eagerly walk the
tree at first to find whether we have any Suspense or Error boundary
parents at all so we can hide the buttons if not.

This also implements it to work with VirtualInstances using #30865. I
find the nearest Fiber Instance downwards filtered or otherwise. Then
from its parent we find the nearest Error or Suspense boundary. That's
because VirtualInstance will always have their inner Fiber as an
Instance but they might not have their parent since it might be
filtered. Which would potentially cause us to skip over a filtered
parent Suspense boundary.
2024-09-05 15:48:17 -04:00
Sebastian Markbåge
04ec50efa9 [DevTools] Add Filtering of Environment Names (#30850)
Stacked on #30842.

This adds a filter to be able to exclude Components from a certain
environment. Default to Client or Server.

The available options are computed into a dropdown based on the names
that are currently used on the page (or an option that were previously
used). In addition to the hardcoded "Client". Meaning that if you have
Server Components on the page you see "Server" or "Client" as possible
options but it can be anything if there are multiple RSC environments on
the page.

"Client" in this case means Function and Class Components in Fiber -
excluding built-ins.

If a Server Component has two environments (primary and secondary) then
both have to be filtered to exclude it.

We don't show the option at all if there are no Server Components used
in the page to avoid confusing existing users that are just using Client
Components and wouldn't know the difference between Server vs Client.

<img width="815" alt="Screenshot 2024-08-30 at 12 56 42 AM"
src="https://github.com/user-attachments/assets/e06b225a-e85d-4cdc-8707-d4630fede19e">
2024-09-03 12:29:15 -04:00
Sebastian Markbåge
36c04348d7 [DevTools] Make Functions Clickable to Jump to Definition (#30769)
Currently you can jump to definition of a function by right clicking
through the context menu. However, it's pretty difficult to discover.
This makes the functions clickable to jump to definition - like links.

This uses the same styling as we do for links (which are btw only
clickable if they're not editable). Including cursor: pointer.

I added a background on hover which follows the same pattern as the
owners list.

I also dropped the ƒ prefix when displaying functions. This is a cute
short cut and there's precedence in how Chrome prints functions in the
console *if* the function's toString would've had a function prefix like
if it was a function declaration or expression. It does not do this for
arrow functions or object methods.

Elsewhere in the JS ecosystem this isn't really used anywhere. It
invites more questions than it answers.

The parenthesis and curlies are enough. There's no ambiguity here since
strings have quotations. It looks better with just its object method
form. Keeping it simple seems best. To my eyes this flows better because
I'm used to looking at function syntax but not weird "f"s.

Before:

<img width="433" alt="Screenshot 2024-08-20 at 11 55 09 PM"
src="https://github.com/user-attachments/assets/9dd50da6-598f-4291-9e24-1cdc7200dc9e">


After:
<img width="388" alt="Screenshot 2024-08-20 at 11 46 01 PM"
src="https://github.com/user-attachments/assets/dd988e14-412e-4deb-8c8c-26a54be8331f">


After (Hover):
<img width="389" alt="Screenshot 2024-08-20 at 11 46 31 PM"
src="https://github.com/user-attachments/assets/6fb4ebed-5dc1-448a-8e4d-b6d4f3903329">
2024-08-22 12:35:49 -04:00
Sebastian Markbåge
985747f810 [DevTools] Support REACT_LEGACY_ELEMENT_TYPE for formatting JSX (#30779)
DevTools shouldn't use react-is since that's versioned to one version of
React. We don't need to since we use all the symbols from
shared/ReactSymbols anyway and have a fork of typeOf that can cover
both.

Now JSX of old React versions show up with proper JSX formatting when
inspecting.
2024-08-21 18:17:29 -04:00
Sebastian Markbåge
13ddf1084b [DevTools] Find owners from the parent path that matches the Fiber or ReactComponentInfo (#30717)
This enables finding Server Components on the owner path. Server
Components aren't stateful so there's not actually one specific owner
that it necessarily matches. So it can't be a global look up. E.g. the
same Server Component can be rendered in two places or even nested
inside each other.

Therefore we need to find an appropriate instance using a heuristic. We
can do that by traversing the parent path since the owner is likely also
a parent. Not always but almost always.

To simplify things we can also do the same for Fibers. That brings us
one step closer to being able to get rid of the global
fiberToFiberInstance map since we can just use the shadow tree to find
this information.

This does mean that we can't find owners that aren't parents which is
usually ok. However, there is a test case that's interesting where you
have a React ART tree inside a DOM tree. In that case the owners
actually span multiple renderers and roots so the owner is not on the
parent stack. Usually this is fine since you'd just care about the
owners within React ART but ideally we'd support this. However, I think
that really the fix to this is that the React ART tree itself should
actually show up inside the DOM tree in DevTools and in the virtual
shadow tree because that's conceptually where it belongs. That would
then solve this particular issue. We'd just need some way to associate
the root with a DOM parent when it gets mounted.
2024-08-16 19:52:11 -04:00
Sebastian Markbåge
19bd26beb6 [Flight/DevTools] Pass the Server Component's "key" as Part of the ReactComponentInfo (#30703)
Supports showing the key in DevTools on the Server Component that the
key was applied to. We can also use this to reconcile to preserve
instance equality when they're reordered.

One thing that's a bit weird about this is that if you provide an
explicit key on a Server Component that alone doesn't have any
semantics. It's because we pass the key down and let the nearest child
inherit the key or get prefixed by the key.

So you might see the same key as a prefix on the child of the Server
Component too which might be a bit confusing. We could remove the prefix
from children but that might also be a bit confusing if they collide.

The div in this case doesn't have a key explicitly specified. It gets it
from the Server Component parent.

<img width="1107" alt="Screenshot 2024-08-14 at 10 06 36 PM"
src="https://github.com/user-attachments/assets/cfc517cc-e737-44c3-a1be-050049267ee2">

Overall keys get a bit confusing when you apply filter. Especially since
it's so common to actually apply the key on a Host Instance. So you
often don't see the key.
2024-08-15 11:04:53 -04:00