This is used to register Server References that exist in the current
environment but also exists in the server it might call into. Such as a
remote server.
If the value comes from the remote server in the first place then this
is called automatically to ensure that you can pass a reference back to
where it came from - even if the `serverModuleMap` option is used. This
was already the case when `serverModuleMap` wasn't passed. This is how
you can pass server references back to the server. However, when we
added `serverModuleMap` that pass was skipped because we were getting
real functions instead of proxies.
For functions that wasn't yet passed from the remote server to the
current server, we can register them eagerly just like we do for
`import('/server').registerServerReference()`. You can now also do this
with `import('/client').registerServerReference()`. We could make them
shared so you only have to do this once but it might not be possible to
pass to the remote server and the remote server might not even be the
same RSC renderer. Therefore I split them. It's up to the compiler
whether it should do that or not. It has to know that any function you
might call might be able to receive it. This is currently global to a
specific RSC renderer.
Typed errors is not a feature that Flight currently supports. However,
for presentation purposes, serializing a custom error name is something
we could support today.
With this PR, we're now transporting custom error names through the
server-client boundary, so that they are available in the client e.g.
for console replaying. One example where this can be useful is when you
want to print debug information while leveraging the fact that
`console.warn` displays the error stack, including handling of hiding
and source mapping stack frames. In this case you may want to show
`Warning: ...` or `Debug: ...` instead of `Error: ...`.
In prod mode, we still transport an obfuscated error that uses the
default `Error` name, to not leak any sensitive information from the
server to the client. This also means that you must not rely on the
error name to discriminate errors, e.g. when handling them in an error
boundary.
Followup to #31725
This implements `prepareDestinationForModule` in the Parcel Flight
client. On the Parcel side, the `<Resources>` component now only inserts
`<link>` elements for stylesheets (along with a bootstrap script when
needed), and React is responsible for inserting scripts. This ensures
that components that are conditionally dynamic imported during render
are also preloaded.
CSS must be added to the RSC tree using `<Resources>` to avoid FOUC.
This must be manually rendered in both the top-level page, and in any
component that is dynamic imported. It would be nice if there was a way
for React to automatically insert CSS as well, but unfortunately
`prepareDestinationForModule` only knows about client components and not
CSS for server components. Perhaps there could be a way we could
annotate components at code splitting boundaries with the resources they
need? More thoughts in this thread:
https://github.com/facebook/react/pull/31725#discussion_r1884867607
This is similar to #31876 but for Server Components.
It marks them as errored and puts the error message in the Summary
properties.
<img width="1511" alt="Screenshot 2024-12-20 at 5 05 35 PM"
src="https://github.com/user-attachments/assets/92f11e42-0e23-41c7-bfd4-09effb25e024"
/>
This only looks at the current chunk for rejections. That means that
there might still be promises deeper that rejected but it's only the
immediate return value of the Server Component that's considered a
rejection of the component itself.
Stacked on #31737.
<img width="987" alt="Screenshot 2024-12-11 at 8 41 15 PM"
src="https://github.com/user-attachments/assets/438379a9-0138-4d02-a53a-419402839558"
/>
When mixing environments (like "use cache" or third party RSC) it's
useful to color and badge those components differently to differentiate.
I'm not putting them in separate tracks because when they do actually
execute, like cache misses or third party RSCs, they behave like they're
part of the same tree.
Stacked on #31736.
<img width="1223" alt="Screenshot 2024-12-11 at 8 21 12 PM"
src="https://github.com/user-attachments/assets/a7cbc04b-c831-476b-aa2f-baddec9461c9"
/>
This emits a placeholder when we're deduping a component. This starts
when the parent's self time ends, where we would've started rendering
this component if it wasn't already started. The end time is when the
actual render ends since the parent is also blocked by it.
Stacked on #31735.
This ensures that Server Components Track comes first. Since it's
typically rendered first on the server for initial load and then flows
into scheduler and client components work. Also puts it closer to the
Network and further away from "Main" JS.
<img width="769" alt="Screenshot 2024-12-11 at 5 31 41 PM"
src="https://github.com/user-attachments/assets/7198db0f-075e-4a78-8ea4-3bfbf06727cb"
/>
Same trick as in #31615.
Stacked on https://github.com/facebook/react/pull/31729
<img width="1436" alt="Screenshot 2024-12-11 at 3 36 41 PM"
src="https://github.com/user-attachments/assets/0a201913-0076-4bbf-be18-8f1df6c58313"
/>
The Server Components visualization is currently a tree flame graph
where parent spans the child. This makes it equivalent to the Client
Components visualization.
However, since Server Components can be async and therefore parallel, we
need to do something when two children are executed in parallel. This PR
bumps parallel children into a separate track and then within that track
if that child has more children it can grow within that track.
I currently just cut off more than 10 parallel tracks.
Synchronous Server Components are still in sequence but it's unlikely
because even a simple microtasky Async Component is still parallel.
<img width="959" alt="Screenshot 2024-12-11 at 4 31 17 PM"
src="https://github.com/user-attachments/assets/5ad6a7f8-7fa0-46dc-af51-78caf9849176"
/>
I think this is probably not a very useful visualization for Server
Components but we can try it out.
I'm also going to try a different visualization where parent-child
relationship is horizontal and parallel vertical instead, but it might
not be possible to make that line up in this tool. It makes it a little
harder to see how much different components (including their children)
impact the overall tree. If that's the only visualization it's also
confusing why it's different dimensions than the Client Component
version.
Follow up to #31725.
I diffed against the Turbopack one to find any unexpected discrepancies.
Some parts are forked enough that it's hard to diff but I think I got
most of it.
<img width="966" alt="Screenshot 2024-12-10 at 10 49 19 PM"
src="https://github.com/user-attachments/assets/27a21bdf-86b9-4203-893b-89523e698138">
This emits a tree view visualization of the timing information for each
Server Component provided in the RSC payload.
The unique thing about this visualization is that the end time of each
Server Component spans the end of the last child. Now what is
conceptually a blocking child is kind of undefined in RSC. E.g. if
you're not using a Promise on the client, or if it is wrapped in
Suspense, is it really blocking the parent?
Here I reconstruct parent-child relationship by which chunks reference
other chunks. A child can belong to more than one parent like when we
dedupe the result of a Server Component.
Then I wait until the whole RSC payload has streamed in, and then I
traverse the tree collecting the end time from children as I go and emit
the `performance.measure()` calls on the way up.
There's more work for this visualization in follow ups but this is the
basics. For example, since the Server Component time span includes async
work it's possible for siblings to execute their span in parallel (Foo
and Bar in the screenshot are parallel siblings). To deal with this we
need to spawn parallel work into separate tracks. Each one can be deep
due to large trees. This can makes this type of visualization unwieldy
when you have a lot of parallelism. Therefore I also plan another
flatter Timeline visualization in a follow up.
This adds a new `react-server-dom-parcel-package`, which is an RSC
integration for the Parcel bundler. It is mostly copied from the
existing webpack/turbopack integrations, with some changes to utilize
Parcel runtime APIs for loading and executing bundles/modules.
See https://github.com/parcel-bundler/parcel/pull/10043 for the Parcel
side of this, which includes the plugin needed to generate client and
server references. https://github.com/parcel-bundler/rsc-examples also
includes examples of various ways to use RSCs with Parcel.
Differences from other integrations:
* Client and server modules are all part of the same graph, and we use
Parcel's
[environments](https://parceljs.org/plugin-system/transformer/#the-environment)
to distinguish them. The server is the Parcel build entry point, and it
imports and renders server components in route handlers. When a `"use
client"` directive is seen, the environment changes and Parcel creates a
new client bundle for the page, combining all client modules together.
CSS from both client and server components are also combined
automatically.
* There is no separate manifest file that needs to be passed around by
the user. A [Runtime](https://parceljs.org/plugin-system/runtime/)
plugin injects client and server references as needed into the relevant
bundles, and registers server action ids using `react-server-dom-parcel`
automatically.
* A special `<Resources>` component is also generated by Parcel to
render the `<script>` and `<link rel="stylesheet">` elements needed for
a page, using the relevant info from the bundle graph.
Note: I've already published a 0.0.x version of this package to npm for
testing purposes but happy to add whoever needs access to it as well.
### Questions
* How to test this in the React repo. I'll have integration tests in
Parcel, but setting up all the different mocks and environments to
simulate that here seems challenging. I could try to copy how
Webpack/Turbopack do it but it's a bit different.
* Where to put TypeScript types. Right now I have some ambient types in
my [example
repo](https://github.com/parcel-bundler/rsc-examples/blob/main/types.d.ts)
but it would be nice for users not to copy and paste these. Can I
include them in the package or do they need to maintained separately in
definitelytyped? I would really prefer not to have to maintain code in
three different repos ideally.
---------
Co-authored-by: Sebastian Markbage <sebastian@calyptus.eu>
Stacked on #31715.
This adds profiling data for Server Components to the RSC stream (but
doesn't yet use it for anything). This is on behind
`enableProfilerTimer` which is on for Dev and Profiling builds. However,
for now there's no Profiling build of Flight so in practice only in DEV.
It's gated on `enableComponentPerformanceTrack` which is experimental
only for now.
We first emit a timeOrigin in the beginning of the stream. This provides
us a relative time to emit timestamps against for cross environment
transfer so that we can log it in terms of absolute times. Using this as
a separate field allows the actual relative timestamps to be a bit more
compact representation and preserves floating point precision.
We emit a timestamp before emitting a Server Component which represents
the start time of the Server Component. The end time is either when the
next Server Component starts or when we finish the task.
We omit the end time for simple tasks that are outlined without Server
Components.
By encoding this as part of the debugInfo stream, this information can
be forwarded between Server to Server RSC.
This clarifies a few things by ensuring that there is always at least
one required field. This can be used to refine the object to one of the
specific types. However, it's probably just a matter of time until we
make this tagged unions instead. E.g. it would be nice to rename the
`name` field `ReactComponentInfo` to `type` and tag it with the React
Element symbol because then it's just the same as a React Element.
I also extract a time field. The idea is that this will advance (or
rewind) the time to the new timestamp and then anything below would be
defined as happening within that time stamp. E.g. to model the start and
end for a server component you'd do something like:
```
[
{time: 123},
{name: 'Component', ... },
{time: 124},
]
```
The reason this needs to be in the `ReactDebugInfo` is so that timing
information from one environment gets transferred into the next
environment. It lets you take a Promise from one world and transfer it
into another world and its timing information is preserved without
everything else being preserved.
I've gone back and forth on if this should be part of each other Info
object like `ReactComponentInfo` but since those can be deduped and can
change formats (e.g. this should really just be a React Element) it's
better to store this separately.
The time format is relative to a `timeOrigin` which is the current
environment's `timeOrigin`. When it's serialized between environments
this needs to be considered.
Emitting these timings is not yet implemented in this PR.
---------
Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
## Overview
Changes the error message to say "Server Functions" instead of "Server
Actions" since this error can fire in cases like:
```
<button onClick={serverFunction} />
```
Which is calling a server function, not a server action.
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development --persistent, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development --persistent, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development --persistent, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development --persistent, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development --persistent, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=development, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=production, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=production, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=production, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=production, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=experimental --env=production, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=stable --env=development --persistent, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=stable --env=development --persistent, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=stable --env=development --persistent, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=stable --env=development --persistent, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=stable --env=development --persistent, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=development --variant=true, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-classic --env=production --variant=true, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=development --variant=true, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=www-modern --env=production --variant=true, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=development --variant=true, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=false, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=false, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=false, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=false, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=false, 5/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=true, 1/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=true, 2/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=true, 3/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=true, 4/5) (push) Has been cancelled
(Runtime) Build and Test / yarn test ${{ matrix.params }} (Shard ${{ matrix.shard }}) (-r=xplat --env=production --variant=true, 5/5) (push) Has been cancelled
When we serialize debug info we should never error even though we don't
currently support everything being serialized. Since it's non-essential
dev information.
We already handle errors in the replacer but not when errors happen in
the JSON algorithm itself - such as cyclic errors.
We should ideally support cyclic objects but regardless we should
gracefully handle the errors.
When parsing stacks from third parties they may include invalid url
characters. So we need to encode them. Since these are expected to be
urls though we use just encodeURI instead of encodeURIComponent.
Normally we filter out stack frames with missing `filename` because they
can be noisy and not ignore listed. However, it's up to the
filterStackFrame function to determine whether to do it. This lets us
match `<anonymous>` stack frames in V8 parsing (they don't have line
numbers).
Stacked on #31299.
We already have an option for resolving Client References to other
Client References when consuming an RSC payload on the server.
This lets you resolve Server References on the consuming side when the
environment where you're consuming the RSC payload also has access to
those Server References. Basically they becomes like Client References
for this consumer but for another consumer they wouldn't be.
This was gated behind `enableOwnerStacks` since they share some code
paths but it's really part of `enableServerComponentLogs`.
This just includes the server-side regular stack on Error/replayed logs
but doesn't use console.createTask and doesn't include owner stacks.
We can't make a special getter to mark the boundary of deep
serialization (which can be used for lazy loading in the future) when
the parent object is a special object that we parse with
getOutlinedModel. Such as Map/Set and JSX.
This marks the objects that are direct children of those as not possible
to limit.
I don't love this solution since ideally it would maybe be more local to
the serialization of a specific object.
It also means that very deep trees of only Map/Set never get cut off.
Maybe we should instead override the `get()` and enumeration methods on
these instead somehow.
It's important to have it be a getter though because that's the
mechanism that lets us lazy-load more depth in the future.
renderModelDesctructive can sometimes be called direclty on Date values.
When this happens we don't first call toJSON on the Date value so we
need to explicitly handle the case where where the rendered value is a
Date instance as well. This change updates renderModelDesctructive to
account for sometimes receiving Date instances directly.
Allow aborting encoding arguments to a Server Action if a Promise
doesn't resolve. That way at least part of the arguments can be used on
the receiving side. This leaves it unresolved in the stream rather than
encoding an error.
This should error on the receiving side when the stream closes but it
doesn't right now in the Edge/Browser versions because closing happens
immediately before we've had a chance to call `.then()` so the Chunks
are still in pending state. This is an existing bug also in
FlightClient.
This allows us to show props in React DevTools when inspecting a Server
Component.
I currently drastically limit the object depth that's serialized since
this is very implicit and you can have heavy objects on the server.
We previously was using the general outlineModel to outline
ReactComponentInfo but we weren't consistently using it everywhere which
could cause some bugs with the parsing when it got deduped on the
client. It also lead to the weird feature detect of `isReactComponent`.
It also meant that this serialization was using the plain serialization
instead of `renderConsoleValue` which means we couldn't safely serialize
arbitrary debug info that isn't serializable there.
So the main change here is to call `outlineComponentInfo` and have that
always write every "Server Component" instance as outlined and in a way
that lets its props be serialized using `renderConsoleValue`.
<img width="1150" alt="Screenshot 2024-10-01 at 1 25 05 AM"
src="https://github.com/user-attachments/assets/f6e7811d-51a3-46b9-bbe0-1b8276849ed4">
The idea is that the RSC protocol is a superset of Structured Clone.
#25687 One exception that we left out was serializing Error objects as
values. We serialize "throws" or "rejections" as Error (regardless of
their type) but not Error values.
This fixes that by serializing `Error` objects. We don't include digest
in this case since we don't call `onError` and it's not really expected
that you'd log it on the server with some way to look it up.
In general this is not super useful outside throws. Especially since we
hide their values in prod. However, there is one case where it is quite
useful. When you replay console logs in DEV you might often log an Error
object within the scope of a Server Component. E.g. the default RSC
error handling just console.error and error object.
Before this would just be an empty object due to our lax console log
serialization:
<img width="1355" alt="Screenshot 2024-09-30 at 2 24 03 PM"
src="https://github.com/user-attachments/assets/694b3fd3-f95f-4863-9321-bcea3f5c5db4">
After:
<img width="1348" alt="Screenshot 2024-09-30 at 2 36 48 PM"
src="https://github.com/user-attachments/assets/834b129d-220d-43a2-a2f4-2eb06921747d">
TODO for a follow up: Flight Reply direction. This direction doesn't
actually serialize thrown errors because they always reject the
serialization.
This is a follow-up from #30528 to not only handle props (the critical
change), but also the owner ~and stack~ of a referenced element.
~Handling stacks here is rather academic because the Flight Server
currently does not deduplicate owner stacks. And if they are really
identical, we should probably just dedupe the whole element.~ EDIT:
Removed from the PR.
Handling owner objects on the other hand is an actual requirement as
reported in https://github.com/vercel/next.js/issues/69545. This problem
only affects the stable release channel, as the absence of owner stacks
allows for the specific kind of shared owner deduping as demonstrated in
the unit test.
This means that the owner of a Component rendered on the remote server
becomes the Component on this server.
Ideally we'd support this for the Client side too. In particular Fiber
but currently ReactComponentInfo's owner is typed as only supporting
other ReactComponentInfo and it's a bigger lift to support that.
This is important if the lazy is at the root of the chunk. I don't have
a unit test for it but @gnoff has a repro.
It also shouldn't unwrap the last value since that's the one we're
referencing.
This was already done correctly by @unstubbable in waitForReference so
this just aligns with that.
Any time we're creating a stack trace we should have a
react-stack-bottom-frame so we know what to filter out.
This is the same thing we already do for createFakeJSXCallStackInDEV but
we should do that when replaying logs too.
Stacked on #30906.
Injects the Flight Client into the DevTools hook if it `supportsFlight`.
This only injects in DEV. We could inject it in prod too but so far the
only feature this exposes is only available in DEV anyway. I also only
call `injectIntoDevTools` in the browser builds since we don't really
support DevTools on the server anyway.
The main purpose of this for now is so that DevTools can track the
Server Component owner of replayed logs. This lets us add owner stacks
where `console.createTask` is not natively supported (like Firefox). It
also lets us associate the log with the Server Component in the
Component tree #30905.
## Summary
This PR bumps Flow all the way to the latest 0.245.2.
Most of the suppressions comes from Flow v0.239.0's change to include
undefined in the return of `Array.pop`.
I also enabled `react.custom_jsx_typing=true` and added custom jsx
typing to match the old behavior that `React.createElement` is
effectively any typed. This is necessary since various builtin
components like `React.Fragment` is actually symbol in the React repo
instead of `React.AbstractComponent<...>`. It can be made more accurate
by customizing the `React$CustomJSXFactory` type, but I will leave it to
the React team to decide.
## How did you test this change?
`yarn flow` for all the renderers
The unbundled form is just a way to show case a prototype for how an
unbundled version of RSC can work. It's not really intended for every
bundler combination to provide such a configuration.
There's no configuration of Turbopack that supports this mode atm and
possibly never will be since it's more of an integrated server/client
experience.
This removes the unbundled form and node register/loaders from the
turbopack build.
stacked on: #30731
We've refined the model of halting a prerender. Now when you abort
during a prerender we simply omit the rows that would complete the
flight render. This is analagous to prerendering in Fizz where you must
resume the prerender to actually result in errors propagating in the
postponed holes. We don't have a resume yet for flight and it's not
entirely clear how that will work however the key insight here is that
deciding whether the never resolving rows are an error or not should
really be done on the consuming side rather than in the producer.
This PR also reintroduces the logs for the abort error/postpone when
prerendering which will give you some indication that something wasn't
finished when the prerender was aborted.