Commit Graph

236 Commits

Author SHA1 Message Date
Sam Zhou
e210d08180 [flow] Upgrade Flow to 0.245.2 (#30919)
## 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
2024-09-09 08:41:44 -07:00
Sebastian Markbåge
eb7570bc16 [Flight] Preserve location of bound server actions (#30778)
Follow up to #30741.

We need to keep the location of the action when it's bound so we can
still jump to it.
2024-08-22 12:36:05 -04:00
Sebastian Markbåge
dd9117e313 [Flight] Source Map Actions in Reference Node Loader Transforms (#30755)
Follow up to #30741.

This is just for the reference Webpack implementation.

If there is a source map associated with a Node ESM loader, we generate
new source map entries for every `registerServerReference` call.

To avoid messing too much with it, this doesn't rewrite the original
mappings. It just reads them while finding each of the exports in the
original mappings. We need to read all since whatever we append at the
end is relative. Then we just generate new appended entries at the end.

For the location I picked the location of the local name identifier.
Since that's the name of the function and that gives us a source map
name index. It means it jumps to the name rather than the beginning of
the function declaration. It could be made more clever like finding a
local function definition if it is reexported. We could also point to
the line/column of the function declaration rather than the identifier
but point to the name index of the identifier name.

Now jumping to definition works in the fixture.

<img width="574" alt="Screenshot 2024-08-20 at 2 49 07 PM"
src="https://github.com/user-attachments/assets/7710f0e6-2cee-4aad-8d4c-ae985f8289eb">

Unfortunately this technique doesn't seem to work in Firefox nor Safari.
They don't apply the source map for jumping to the definition.
2024-08-21 09:52:17 -04:00
Josh Story
dc32c7f35e [Flight] use microtask for scheduling during prerenders (#30768)
In https://github.com/facebook/react/pull/29491 I updated the work
scheduler for Flight to use microtasks to perform work when something
pings. This is useful but it does have some downsides in terms of our
ability to do task prioritization. Additionally the initial work is not
instantiated using a microtask which is inconsistent with how pings
work.

In this change I update the scheduling logic to use microtasks
consistently for prerenders and use regular tasks for renders both for
the initial work and pings.
2024-08-20 21:43:21 -07:00
Josh Story
92d26c8e93 [Flight] When halting omit any reference rather than refer to a shared missing chunk (#30750)
When aborting a prerender we should leave references unfulfilled, not
share a common unfullfilled reference. functionally today this doesn't
matter because we don't have resuming but the semantic is that the row
was not available when the abort happened and in a resume the row should
fill in. But by pointing each task to a common unfulfilled chunk we lose
the ability for these references to resolves to distinct values on
resume.
2024-08-20 10:22:39 -07:00
Josh Story
a960b92cb9 [Flight] model halting as never delivered chunks (#30740)
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.
2024-08-19 19:34:20 -07:00
Josh Story
9d082b5500 [Flight] model halted references explicitly (#30731)
using infinitely suspending promises isn't right because this will parse
as a promise which is only appropriate if the value we're halting at is
a promise. Instead we need to have a special marker type that says this
reference will never resolve. Additionally flight client needs to not
error any halted references when the stream closes because they will
otherwise appear as an error

addresses:
https://github.com/facebook/react/pull/30705#discussion_r1720479974
2024-08-19 11:24:41 -07:00
Sebastian Markbåge
6ebfd5b082 [Flight] Source Map Server Actions to their Server Location (#30741)
This uses a similar technique to what we use to generate fake stack
frames for server components. This generates an eval:ed wrapper function
around the Server Reference proxy we create on the client. This wrapper
function gets the original `name` of the action on the server and I also
add a source map if `findSourceMapURL` is defined that points back to
the source of the server function.

For `"use server"` on the server, there's no new API. It just uses the
callsite of `registerServerReference()` on the Server. We can infer the
function name from the actual function on the server and we already have
the `findSourceMapURL` on the client receiving it.

For `"use server"` imported from the client, there's two new options
added to `createServerReference()` (in addition to the optional
[`encodeFormAction`](#27563)). These are only used in DEV mode. The
[`findSourceMapURL`](#29708) option is the same one added in #29708. We
need to pass this these references aren't created in the context of any
specific request but globally. The other weird thing about this case is
that this is actually a case where the compiled environment is the
client so any source maps are the same as for the client layer, so the
environment name here is just `"Client"`.

```diff
  createServerReference(
    id: string,
    callServer: CallServerCallback,
    encodeFormAction?: EncodeFormActionCallback,
+   findSourceMapURL?: FindSourceMapURLCallback, // DEV-only
+   functionName?: string, // DEV-only
  )
```

The key is that we use the location of the
`registerServerReference()`/`createServerReference()` call as the
location of the function. A compiler can either emit those at the same
locations as the original functions or use source maps to have those
segments refer to the original location of the function (or in the case
of a re-export the original location of the re-export is also a fine
approximate). The compiled output must call these directly without a
wrapper function because the wrapper adds a stack frame. I decided
against complicated and fragile dev-only options to skip n number of
frames that would just end up in prod code. The implementation just
skips one frame - our own. Otherwise it'll just point all source mapping
to the wrapper.

We don't have a `"use server"` imported from the client implementation
in the reference implementation/fixture so it's a bit tricky to test
that. In the case of CJS on the server, we just use a runtime instead of
compiler so it's tricky to source map those appropriately. We can
implement it for ESM on the server which is the main thing we're testing
in the fixture. It's easier in a real implementation where all the
compilation is just one pass. It's a little tricky since we have to
parse and append to other source maps but I'd like to do that as a
follow up. Or maybe that's just an exercise for the reader.

You can right click an action and click "Go to Definition".

<img width="1323" alt="Screenshot 2024-08-17 at 6 04 27 PM"
src="https://github.com/user-attachments/assets/94d379b3-8871-4671-a20d-cbf9cfbc2c6e">

For now they simply don't point to the right place but you can still
jump to the right file in the fixture:

<img width="1512" alt="Screenshot 2024-08-17 at 5 58 40 PM"
src="https://github.com/user-attachments/assets/1ea5d665-e25a-44ca-9515-481dd3c5c2fe">

In Firefox/Safari given that the location doesn't exist in the source
map yet, the browser refuses to open the file. Where as Chrome does
nearest (last) line.
2024-08-18 12:31:45 -04:00
Josh Story
7b41cdc093 [Flight][Static] Implement halting a prerender behind enableHalt (#30705)
enableHalt turns on a mode for flight prerenders where aborts are
treated like infinitely stalled outcomes while still completing the
prerender. For regular tasks we simply serialize the slot as a promise
that never settles. For ReadableStream, Blob, and Async Iterators we
just never advance the serialization so they remain unfinished when
consumed on the client.

When enableHalt is turned on aborts of prerenders will halt rather than
error. The abort reason is forwarded to the upstream produces of the
aforementioned async iterators, blobs, and ReadableStreams. In the
future if we expose a signal that you can consume from within a render
to cancel additional work the abort reason will also be forwarded there
2024-08-16 14:21:57 -07:00
Josh Story
fa6eab5854 [Flight] Implement prerender (#30686)
Prerendering in flight is similar to prerendering in Fizz. Instead of
receiving a result (the stream) immediately a promise is returned which
resolves to the stream when the prerender is complete. The promise will
reject if the flight render fatally errors otherwise it will resolve
when the render is completed or is aborted.
2024-08-15 14:28:28 -07: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
Josh Story
8b08ee08a1 [Flight] reorganize code for forked entrypoints (#30702)
This commit updates the file locations and bulid configurations for
flight in preparation for new static entrypoints. This follows a
structure similar to Fizz which has a unified build but exports methods
from different top level entrypoints. This PR doesn't actually add the
new top level entrypoints however, that will arrive in a later update.
2024-08-14 18:10:40 -07:00
Josh Story
2a540194ad [Flight] do not emit error after abort (#30683)
When synchronously aborting in a non-async Function Component if you
throw after aborting the task would error rather than abort because
React never observed the AbortSignal.

Using a sigil to throw after aborting during render isn't effective b/c
the user code itself could throw so insteead we just read the request
status. This is ok b/c we don't expect any tasks to still be pending
after the currently running task finishes.

However I found one instance where that wasn't true related to
serializing thenables which I've fixed so we may find other cases. If we
do, though it's almost certainly a bug in our task bookkeeping so we
should just fix it if it comes up.

I also updated `abort` to not set the status to ABORTING unless the
status was OPEN. we don't want to ever leave CLOSED or CLOSING status
2024-08-13 20:59:45 -07:00
Josh Story
f6d1df6648 [Flight] erroring after abort should not result in unhandled rejection (#30675)
When I implemented the ability to abort synchronoulsy in flight I made
it possible for erroring async server components to cause an unhandled
rejection error. In the current implementation if you abort during the
synchronous phase of a Function Component and then throw an error in the
synchronous phase React will not attach any promise handlers because it
short circuits the thenable treatment and throws an AbortSigil instead.
This change updates the rendering logic to ignore the rejecting
component.
2024-08-13 13:42:10 -07:00
Greg Brimble
c0ee8e94b0 Fix typos in Turbopack configuration and in Node.js loader error messages (#30593)
<!--
  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

Just fixing some copy-paste typos.

## How did you test this change?

Untested.
2024-08-05 10:11:39 -04:00
Hendrik Liebau
3208e73e82 Assign resolved outlined props to element object (and not only tuple) (#30528)
Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
2024-07-30 21:31:32 +02:00
Sebastian Markbåge
c2d103594d Configure the requested environment and annotate tasks at boundary between environments (#30455)
This enables configuring the name of the requested environment.

When we currently use createTask, we start with a `"use server"`
annotation. This option basically configures that string.

I now also deal with the case when switching environments along the
owner path. If you go from `"Third Party"` to `"Server"` to `"Client"`,
it'll have a task named `"use third party"` at the root, then `"use
server"` and then finally `"use client"`.

We don't really have the concept of a Server Component making a request
during render to then create another Server Component. Really the inner
one should conceptually have the first one as its owner in that case. So
currently the inner one will always have a null owner. We could somehow
connect them in this server-to-server case.

We don't currently have a way to configure the `"use client"` option but
I figured maybe that could be inferred by the server environment that
the Flight Client is executed within.

Note: We did talk before about annotating each stack frame with the
environment. You can effectively do that manually when parsing
`rsc://React/{environment}/` from `captureOwnerStack`. However, we can't
do that natively. At least not without deeper integration. Because it's
the source map that's responsible for the actual function name of each
stack frame - not what we give it at runtime. So for the native stacks,
the task showing the change in environment is more practical.
2024-07-25 11:46:58 -04:00
Sebastian Markbåge
4b62400765 [Flight] Add filterStackFrame options to the RSC Server (#30447)
This lets you customize the filter, for example allowing node_modules or
filter out additional functions that you don't want to include when
sending the stack to the client.

Notably this doesn't filter out Server Components out of the parent
stack. Those are just like a view of the tree by name. Not virtual stack
frames.
2024-07-25 10:50:56 -04:00
Hendrik Liebau
76002254b7 Fix resolving of references to deduped props in lazy elements (#30441)
When a model references a deduped object of a blocked element that has
subsequently been turned into a lazy element, we need to wait for the
lazy element's chunk to resolve before resolving the reference.

Without the fix, the new test failed with the following runtime error:

```
TypeError: Cannot read properties of undefined (reading 'children')
  1003 |       let value = chunk.value;
  1004 |       for (let i = 1; i < path.length; i++) {
> 1005 |         value = value[path[i]];
       |                          ^
  1006 |       }
  1007 |       const chunkValue = map(response, value);
  1008 |       if (__DEV__ && chunk._debugInfo) {

  at getOutlinedModel (packages/react-client/src/ReactFlightClient.js:1005:26)
```

The bug was uncovered after updating React in Next.js in
https://github.com/vercel/next.js/pull/66711.
2024-07-24 19:34:41 -04:00
Jan Kassens
b7e7f1a3fa [BE] upgrade prettier to 3.3.3 (#30420)
Mostly just changes in ternary formatting.
2024-07-22 16:09:01 -04:00
Sebastian Markbåge
b73dcdc04f [Fizz] Refactor Component Stack Nodes (#30298)
Component stacks have a similar problem to the problem with keyPath
where we had to move it down and set it late right before recursing.
Currently we work around that by popping exactly one off when something
suspends. That doesn't work with the new server stacks being added which
are more than one. It also meant that we kept having add a single frame
that could be popped when there shouldn't need to be one.

Unlike keyPath component stacks has this weird property that once
something throws we might need the stack that was attempted for errors
or the previous stack if we're going to retry and just recreate it.

I've tried a few different approaches and I didn't like either but this
is the one that seems least problematic.

I first split out renderNodeDestructive into a retryNode helper. During
retries only retryNode is called. When we first discover a node, we pass
through renderNodeDestructive.

Instead of add a component stack frame deep inside renderNodeDestructive
after we've already refined a node, we now add it before in
renderNodeDestructive. That way it's only added once before being
attempted. This is similar to how Fiber works where in ChildFiber we
match the node once to create the instance and then later do we attempt
to actually render it and it's only the second part that's ever retried.

This unfortunately means that we now have to refine the node down to
element/lazy/thenables twice. To avoid refining the type too I move that
to be done lazily.
2024-07-09 15:44:01 -04:00
Sebastian Markbåge
274c980c53 Warn for useFormState on initial render (#30292)
This was missed in the mount dev dispatcher. It was only in the rerender
dispatcher which means that it was only logged during the rerender.
Since DevTools can hide logs during rerenders, this hid the warning in
StrictMode.
2024-07-08 16:45:24 -04:00
Sebastian Markbåge
0b5835a46f [Flight] Implement captureStackTrace and owner stacks on the Server (#30197)
Wire up owner stacks in Flight to the shared internals. This exposes it
to `captureOwnerStack()`.

In this case we install it permanently as we only allow one RSC renderer
which then supports async contexts. Same thing we do for owner.

This also ends up adding it to errors logged by React through
`consoleWithStackDev`. The plan is to eventually remove that but this is
inline with what we do in Fizz and Fiber already.

However, at the same time we've instrumented the console so we need to
strip them back out before sending to the client. This lets the client
control whether to add the stack back in or allowing
`console.createTask` to control it.

This is another reason we shouldn't append them from React but for now
we hack it by removing them after the fact.
2024-07-04 12:15:51 -04:00
Sebastian Markbåge
8e9de898d3 [Flight] Add option to replay console logs or not (#30207)
Defaults to true in browser builds, otherwise defaults to false. The
assumption is that the server logs will already contain a log from the
original Flight server.

We currently always replay console logs but this leads to duplicates on
the server by default when you use SSR, because the Flight Client on the
server replays the logs. This can be nice since those logs gets badged.
It can also be nice if they're running in separate servers but when
they're logging to the same stream it's annoying. Which is really the
typical set up so we should just make that the default but leave it
configurable.
2024-07-04 12:15:35 -04:00
Sebastian Markbåge
6e169fc65d [Flight] Allow String Chunks to Passthrough in Node streams and renderToMarkup (#30131)
It can be efficient to accept raw string chunks to pass through a stream
instead of encoding them into a binary copy first.

Previously our Flight parsers didn't accept receiving string chunks.
That's partly because we sometimes need to encode binary chunks anyway
so string only transport isn't enough but some chunks can be strings.
This adds a partial ability for chunks to be received as strings.

However, accepting strings comes with some downsides. E.g. if the
strings are split up we need to buffer it which compromises the perf for
the common case. If the chunk represents binary data, then we'd need to
encode it back into a typed array which would require a TextEncoder
dependency in the parser. If the string chunk represents a byte length
encoded string we don't know how many unicode characters to read without
measuring them in terms of binary - also requiring a TextEncoder.

This PR is mainly intended for use for pass-through within the same
memory. We can simplify the implementation by assuming that any string
chunk is passed as the original chunk. This requires that the server
stream config doesn't arbitrarily concatenate strings (e.g. large
strings should not be concatenated which is probably a good heuristic
anyway). It also means that this is not suitable to be used with for
example receiving string chunks on the client by passing them through
SSR hydration data - except if the encoding that way was only used with
chunks that were already encoded as strings by Flight.

Web streams mostly just work on binary data anyway so they can't use
this.

In Node.js streams we concatenate precomputed and small strings into
larger buffers. It might make sense to do that using string ropes
instead. However, in the meantime we can at least pass large strings
that are outside our buffer view size as raw strings. There's no benefit
to us eagerly encoding those.

Also, let Node accept string chunks as long as they're following our
expected constraints. This lets us test the mixed protocol using
pass-throughs. This can also be useful when the RSC server is in the
same environment as the SSR server as they don't have to go from strings
to typed arrays back to strings.

Now we can also use this in the pass-through used in renderToMarkup.
This lets us avoid the dependency on TextDecoder/TextEncoder in that
package.
2024-07-03 13:25:04 -04:00
Hendrik Liebau
9c6806964f Add regression test for #30172 (#30198)
The issue reported in #30172 was fixed with #29823. The PR also added
the test [`should resolve deduped objects that are themselves
blocked`](6d2a97a711/packages/react-server-dom-webpack/src/__tests__/ReactFlightDOMBrowser-test.js (L348-L393)),
which tests a similar scenario. However, the existing test would have
also succeeded before applying the changes from #29823. Therefore, I
believe it makes sense to add an additional test `should resolve deduped
objects in nested children of blocked models`, which does not succeed
without #29823, to prevent regressions.
2024-07-03 07:10:23 -04:00
Jiachi Liu
609d0cc3c8 Add new error message for awaiting the client export (#29853)
<!--
  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

`Cannot access .then on server` is not an ideal message when you try to
await or do promise chain to the properties of client reference. The
below example will let `.then` get accessed by native code while
handling the promise chain but the access is not clearly visible in user
code.

```
import('./client-module').then((mod) => mod.Component)
```

This PR chnage the error message of module reference proxy '.then'
property to show more kinds of usage, then it can be pretty clearly for
helping users to avoid the bad usage

<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->

## How did you test this change?

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.
-->
2024-06-26 13:47:39 -04:00
Jan Kassens
b565373afd lint: enable reportUnusedDisableDirectives and remove unused suppressions (#28721)
This enables linting against unused suppressions and removes the ones
that were unused.
2024-06-21 12:24:32 -04:00
Sebastian Markbåge
55c9d45f3b [Flight] Let environmentName vary over time by making it a function of string (#29867)
This lets the environment name vary within a request by the context a
component, log or error being executed in.

A potentially different API would be something like
`setEnvironmentName()` but we'd have to extend the `ReadableStream` or
something to do that like we do for `.allReady`. As a function though it
has some expansion possibilities, e.g. we could potentially also pass
some information to it for context about what is being asked for.

If it changes before completing a task, we also emit the change so that
we have the debug info for what the environment was before entering a
component and what it was after completing it.
2024-06-12 10:55:42 -04:00
Sebastian Markbåge
fb57fc5a8a [Flight] Let Errored/Blocked Direct References Turn Nearest Element Lazy (#29823)
Stacked on #29807.

This lets the nearest Suspense/Error Boundary handle it even if that
boundary is defined by the model itself.

It also ensures that when we have an error during serialization of
properties, those can be associated with the nearest JSX element and
since we have a stack/owner for that element we can use it to point to
the source code of that line. We can't track the source of any nested
arbitrary objects deeper inside since objects don’t track their stacks
but close enough. Ideally we have the property path but we don’t have
that right now. We have a partial in the message itself.

<img width="813" alt="Screenshot 2024-06-09 at 10 08 27 PM"
src="https://github.com/facebook/react/assets/63648/917fbe0c-053c-4204-93db-d68a66e3e874">

Note: The component name (Counter) is lost in the first message because
we don't print it in the Task. We use `"use client"` instead because we
expect the next stack frame to have the name. We also don't include it
in the actual error message because the Server doesn't know the
component name yet. Ideally Client References should be able to have a
name. If the nearest is a Host Component then we do use the name though.
However, it's not actually inside that Component that the error happens
it's in App and that points to the right line number.

An interesting case is that if something that's actually going to be
consumed by the props to a Suspense/Error Boundary or the Client
Component that wraps them fails, then it can't be handled by the
boundary. However, a counter intuitive case might be when that's on the
`children` props. E.g.
`<ErrorBoundary>{clientReferenceOrInvalidSerialization}</ErrorBoundary>`.
This value can be inspected by the boundary so it's not safe to pass it
so if it's errored it is not caught.

## Implementation

The first insight is that this is best solved on the Client rather than
in the Server because that way it also covers Client References that end
up erroring.

The key insight is that while we don't have a true stack when using
`JSON.parse` and therefore no begin/complete we can still infer these
phases for Elements because the first child of an Element is always
`'$'` which is also a leaf. In depth first that's our begin phase. When
the Element itself completes, we have the complete phase. Anything in
between is within the Element.

Using this idea I was able to refactor the blocking tracking mechanism
to stash the blocked information on `initializingHandler` and then on
the way up do we let whatever is nearest handle it - whether that's an
Element or the root Chunk. It's kind of like an Algebraic Effect.

cc @unstubbable This is something you might want to deep dive into to
find more edge cases. I'm sure I've missed something.

---------

Co-authored-by: eps1lon <sebastian.silbermann@vercel.com>
2024-06-11 19:12:39 -04:00
Sebastian Markbåge
2774208039 Remove Warning: prefix and toString on console Arguments (#29839)
Basically make `console.error` and `console.warn` behave like normal -
when a component stack isn't appended. I need this because I need to be
able to print rich logs with the component stack option and to be able
to disable instrumentation completely in `console.createTask`
environments that don't need it.

Currently we can't print logs with richer objects because they're
toString:ed first. In practice, pretty much all arguments we log are
already toString:ed so it's not necessary anyway. Some might be like a
number. So it would only be a problem if some environment can't handle
proper consoles but then it's up to that environment to toString it
before logging.

The `Warning: ` prefix is historic and is both noisy and confusing. It's
mostly unnecessary since the UI surrounding `console.error` and
`console.warn` tend to have visual treatment around it anyway. However,
it's actively misleading when `console.error` gets prefixed with a
Warning that we consider an error level. There's an argument to be made
that some of our `console.error` don't make the bar for an error but
then the argument is to downgrade each of those to `console.warn` - not
to brand all our actual error logging with `Warning: `.

Apparently something needs to change in React Native before landing this
because it depends on the prefix somehow which probably doesn't make
sense already.
2024-06-10 18:41:56 -04:00
Ricky
d172bdaf95 Add jest lint rules (#29760)
## Overview

Updates `eslint-plugin-jest` and enables the recommended rules with some
turned off that are unhelpful.

The main motivations is:
a) we have a few duplicated tests, which this found an I deleted 
b) making sure we don't accidentally commit skipped tests
2024-06-10 14:31:37 -04:00
Josh Story
c4b433f8cb [Flight] Allow aborting during render (#29764)
Stacked on #29491

Previously if you aborted during a render the currently rendering task
would itself be aborted which will cause the entire model to be replaced
by the aborted error rather than just the slot currently being rendered.

This change updates the abort logic to mark currently rendering tasks as
aborted but allowing the current render to emit a partially serialized
model with an error reference in place of the current model.

The intent is to support aborting from rendering synchronously, in
microtasks (after an await or in a .then) and in lazy initializers. We
don't specifically support aborting from things like proxies that might
be triggered during serialization of props
2024-06-06 14:41:27 -07:00
Josh Story
b526a0a419 [Flight][Fizz] schedule work async (#29551)
While most builds of Flight and Fizz schedule work in new tasks some do
execute work synchronously. While this is necessary for legacy APIs like
renderToString for modern APIs there really isn't a great reason to do
this synchronously.

We could schedule works as microtasks but we actually want to yield so
the runtime can run events and other things that will unblock additional
work before starting the next work loop.

This change updates all non-legacy uses to be async using the best
availalble macrotask scheduler.

Browser now uses postMessage
Bun uses setTimeout because while it also supports setImmediate the
scheduling is not as eager as the same API in node
the FB build also uses setTimeout

This change required a number of changes to tests which were utilizing
the sync nature of work in the Browser builds to avoid having to manage
timers and tasks. I added a patch to install MessageChannel which is
required by the browser builds and made this patched version integrate
with the Scheduler mock. This way we can effectively use `act` to flush
flight and fizz work similar to how we do this on the client.
2024-06-06 10:07:24 -07:00
Sebastian Markbåge
ba099e442b [Flight] Add findSourceMapURL option to get a URL to load Server source maps from (#29708)
This lets you click a stack frame on the client and see the Server
source code inline.

<img width="871" alt="Screenshot 2024-06-01 at 11 44 24 PM"
src="https://github.com/facebook/react/assets/63648/581281ce-0dce-40c0-a084-4a6d53ba1682">

<img width="840" alt="Screenshot 2024-06-01 at 11 43 37 PM"
src="https://github.com/facebook/react/assets/63648/00dc77af-07c1-4389-9ae0-cf1f45199efb">

We could do some logic on the server that sends a source map url for
every stack frame in the RSC payload. That would make the client
potentially config free. However regardless we need the config to
describe what url scheme to use since that’s not built in to the bundler
config. In practice you likely have a common pattern for your source
maps so no need to send data over and over when we can just have a
simple function configured on the client.

The server must return a source map, even if the file is not actually
compiled since the fake file is still compiled.

The source mapping strategy can be one of two models depending on if the
server’s stack traces (`new Error().stack`) are source mapped back to
the original (`—enable-source-maps`) or represents the location in
compiled code (like in the browser).

If it represents the location in compiled code it’s actually easier. You
just serve the source map generated for that file by the tooling.

If it is already source mapped it has to generate a source map where
everything points to the same location (as if not compiled) ideally with
a segment per logical ast node.
2024-06-02 22:58:24 -04:00
Sebastian Markbåge
18164761b1 [Flight] Check if a return value is a client reference before introspecting (#29611)
This didn't actually fail before but I'm just adding an extra check.

Currently Client References are always "function" proxies so they never
fall into this branch. However, we do in theory support objects as
client references too depending on environment. We have checks
elsewhere. So this just makes that consistent.
2024-05-28 19:07:30 -04:00
Sebastian Markbåge
84239da896 Move createElement/JSX Warnings into the Renderer (#29088)
This is necessary to simplify the component stack handling to make way
for owner stacks. It also solves some hacks that we used to have but
don't quite make sense. It also solves the problem where things like key
warnings get silenced in RSC because they get deduped. It also surfaces
areas where we were missing key warnings to begin with.

Almost every type of warning is issued from the renderer. React Elements
are really not anything special themselves. They're just lazily invoked
functions and its really the renderer that determines there semantics.

We have three types of warnings that previously fired in
JSX/createElement:

- Fragment props validation.
- Type validation.
- Key warning.

It's nice to be able to do some validation in the JSX/createElement
because it has a more specific stack frame at the callsite. However,
that's the case for every type of component and validation. That's the
whole point of enableOwnerStacks. It's also not sufficient to do it in
JSX/createElement so we also have validation in the renderers too. So
this validation is really just an eager validation but also happens
again later.

The problem with these is that we don't really know what types are valid
until we get to the renderer. Additionally, by placing it in the
isomorphic code it becomes harder to do deduping of warnings in a way
that makes sense for that renderer. It also means we can't reuse logic
for managing stacks etc.

Fragment props validation really should just be part of the renderer
like any other component type. This also matters once we add Fragment
refs and other fragment features. So I moved this into Fiber. However,
since some Fragments don't have Fibers, I do the validation in
ChildFiber instead of beginWork where it would normally happen.

For `type` validation we already do validation when rendering. By
leaving it to the renderer we don't have to hard code an extra list.
This list also varies by context. E.g. class components aren't allowed
in RSC but client references are but we don't have an isomorphic way to
identify client references because they're defined by the host config so
the current logic is flawed anyway. I kept the early validation for now
without the `enableOwnerStacks` since it does provide a nicer stack
frame but with that flag on it'll be handled with nice stacks anyway. I
normalized some of the errors to ensure tests pass.

For `key` validation it's the same principle. The mechanism for the
heuristic is still the same - if it passes statically through a parent
JSX/createElement call then it's considered validated. We already did
print the error later from the renderer so this also disables the early
log in the `enableOwnerStacks` flag.

I also added logging to Fizz so that key warnings can print in SSR logs.

Flight is a bit more complex. For elements that end up on the client we
just pass the `validated` flag along to the client and let the client
renderer print the error once rendered. For server components we log the
error from Flight with the server component as the owner on the stack
which will allow us to print the right stack for context. The factoring
of this is a little tricky because we only want to warn if it's in an
array parent but we want to log the error later to get the right debug
info.

Fiber/Fizz has a similar factoring problem that causes us to create a
fake Fiber for the owner which means the logs won't be associated with
the right place in DevTools.
2024-05-23 12:48:57 -04:00
Sebastian Silbermann
f994737d14 [Flight] Allow temporary references in decodeReplyFromBusboy (#29219) 2024-05-22 21:15:14 +02:00
Janka Uryga
9b3f909cc1 [Flight] don't overwrite existing chunk listeners in 'wakeChunkIfInitialized' (#29204)
Follow up to https://github.com/facebook/react/pull/29201. If a chunk
had listeners attached already (e.g. because `.then` was called on the
chunk returned from `createFromReadableStream`),
`wakeChunkIfInitialized` would overwrite any listeners added during
chunk initialization. This caused cyclic [path
references](https://github.com/facebook/react/pull/28996) within that
chunk to never resolve. Fixed by merging the two arrays of listeners.
2024-05-21 14:04:46 -07:00
Sebastian Markbåge
8f3c0525f9 [Flight / Flight Reply] Don't clear pending listeners when entering blocked state (#29201)
Fixes #29200

The cyclic state might have added listeners that will still need to be
invoked. This happens if we have a cyclic reference AND end up blocked.

We have already cleared these before entering the parsing when we enter
the CYCLIC state so we they already have the right type. If listeners
are added during this phase they should carry over to the blocked state.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2024-05-21 14:12:20 -04:00
Sebastian Markbåge
af3a55e67f Lazily freeze in case anything in the currently initializing chunk is blocked (#29139)
Fixed #29129.

---------

Co-authored-by: Hendrik Liebau <mail@hendrik-liebau.de>
2024-05-17 13:58:42 -04:00
Sebastian Markbåge
6c409acefd [Flight Reply] Encode Objects Returned to the Client by Reference (#29010)
Stacked on #28997.

We can use the technique of referencing an object by its row + property
name path for temporary references - like we do for deduping. That way
we don't need to generate an ID for temporary references. Instead, they
can just be an opaque marker in the slot and it has the implicit ID of
the row + path.

Then we can stash all objects, even the ones that are actually available
to read on the server, as temporary references. Without adding anything
to the payload since the IDs are implicit. If the same object is
returned to the client, it can be referenced by reference instead of
serializing it back to the client. This also helps preserve object
identity.

We assume that the objects are immutable when they pass the boundary.

I'm not sure if this is worth it but with this mechanism, if you return
the `FormData` payload from a `useActionState` it doesn't have to be
serialized on the way back to the client. This is a common pattern for
having access to the last submission as "default value" to the form
fields. However you can still control it by replacing it with another
object if you want. In MPA mode, the temporary references are not
configured and so it needs to be serialized in that case. That's
required anyway for hydration purposes.

I'm not sure if people will actually use this in practice though or if
FormData will always be destructured into some other object like with a
library that turns it into typed data, and back. If so, the object
identity is lost.
2024-05-09 20:00:56 -04:00
Sebastian Markbåge
38d9f156b8 [Flight Reply] Dedupe Objects and Support Cyclic References (#28997)
Uses the same technique as in #28996 to encode references to already
emitted objects. This now means that Reply can support cyclic objects
too for parity.
2024-05-09 19:24:37 -04:00
Sebastian Markbåge
7a78d03028 [Flight] Encode references to existing objects by property path (#28996)
Instead of forcing an object to be outlined to be able to refer to it
later we can refer to it by the property path inside another parent
object.

E.g. this encodes such a reference as `'$123:props:children:foo:bar'`.

That way we don't have to preemptively outline object and we can dedupe
after the first time we've found it.

There's no cost on the client if it's not used because we're not storing
any additional information preemptively.

This works mainly because we only have simple JSON objects from the root
reference. Complex objects like Map, FormData etc. are stored as their
entries array in the look up and not the complex object. Other complex
objects like TypedArrays or imports don't have deeply nested objects in
them that can be referenced.

This solves the problem that we only dedupe after the third instance.
This dedupes at the second instance. It also solves the problem where
all nested objects inside deduped instances also are outlined.

The property paths can get pretty large. This is why a test on payload
size increased. We could potentially outline the reference itself at the
first dupe. That way we get a shorter ID to refer to in the third
instance.
2024-05-09 19:16:14 -04:00
Sebastian Markbåge
151cce3740 Track Stack of JSX Calls (#29032)
This is the first step to experimenting with a new type of stack traces
behind the `enableOwnerStacks` flag - in DEV only.

The idea is to generate stacks that are more like if the JSX was a
direct call even though it's actually a lazy call. Not only can you see
which exact JSX call line number generated the erroring component but if
that's inside an abstraction function, which function called that
function and if it's a component, which component generated that
component. For this to make sense it really need to be the "owner" stack
rather than the parent stack like we do for other component stacks. On
one hand it has more precise information but on the other hand it also
loses context. For most types of problems the owner stack is the most
useful though since it tells you which component rendered this
component.

The problem with the platform in its current state is that there's two
ways to deal with stacks:

1) `new Error().stack` 
2) `console.createTask()`

The nice thing about `new Error().stack` is that we can extract the
frames and piece them together in whatever way we want. That is great
for constructing custom UIs like error dialogs. Unfortunately, we can't
take custom stacks and set them in the native UIs like Chrome DevTools.

The nice thing about `console.createTask()` is that the resulting stacks
are natively integrated into the Chrome DevTools in the console and the
breakpoint debugger. They also automatically follow source mapping and
ignoreLists. The downside is that there's no way to extract the async
stack outside the native UI itself so this information cannot be used
for custom UIs like errors dialogs. It also means we can't collect this
on the server and then pass it to the client for server components.

The solution here is that we use both techniques and collect both an
`Error` object and a `Task` object for every JSX call.

The main concern about this approach is the performance so that's the
main thing to test. It's certainly too slow for production but it might
also be too slow even for DEV.

This first PR doesn't actually use the stacks yet. It just collects them
as the first step. The next step is to start utilizing this information
in error printing etc.

For RSC we pass the stack along across over the wire. This can be
concatenated on the client following the owner path to create an owner
stack leading back into the server. We'll later use this information to
restore fake frames on the client for native integration. Since this
information quickly gets pretty heavy if we include all frames, we strip
out the top frame. We also strip out everything below the functions that
call into user space in the Flight runtime. To do this we need to figure
out the frames that represents calling out into user space. The
resulting stack is typically just the one frame inside the owner
component's JSX callsite. I also eagerly strip out things we expect to
be ignoreList:ed anyway - such as `node_modules` and Node.js internals.
2024-05-09 12:23:05 -04:00
Sebastian Markbåge
ec15267a00 [Flight Reply] Resolve outlined models async in Reply just like in Flight Client (#28988)
This is the same change as #28780 but for the Flight Reply receiver.

While it's not possible to create an "async module" reference in this
case - resolving a server reference can still be async if loading it
requires loading chunks like in a new server instance.

Since extracting a typed array from a Blob is async, that's also a case
where a dependency can be async.
2024-05-07 22:19:59 -04:00
Sebastian Markbåge
6bac4f2f31 [Fizz] Fallback to client replaying actions if we're trying to serialize a Blob (#28987)
This follows the same principle as in #28611.

We cannot serialize Blobs of a form data into HTML because you can't
initialize a file input to some value. However the serialization of
state in an Action can contain blobs. In this case we do error but
outside the try/catch that recovers to error to client replaying instead
of MPA mode. This errors earlier to ensure that this works.

Testing this is a bit annoying because JSDOM doesn't have any of the
Blob methods but the Blob needs to be compatible with FormData and the
FormData needs to be compatible with `<form>` nodes in these tests. So I
polyfilled those in JSDOM with some hacks.

A possible future enhancement would be to encode these blobs in a base64
mode instead and have some way to receive them on the server. It's just
a matter of layering this. I think the RSC layer's `FORM_DATA`
implementation can pass some flag to encode as base64 and then have
decodeAction include some way to parse them. That way this case would
work in MPA mode too.
2024-05-07 21:53:10 -04:00
Sebastian Markbåge
ec9400dc41 [Flight Reply] Encode ReadableStream and AsyncIterables (#28893)
Same as #28847 but in the other direction.

Like other promises, this doesn't actually stream in the outgoing
direction. It buffers until the stream is done. This is mainly due to
our protocol remains compatible with Safari's lack of outgoing streams
until recently.

However, the stream chunks are encoded as separate fields and so does
support the busboy streaming on the receiving side.
2024-05-03 17:23:55 -04:00
Sebastian Markbåge
5fcfd71638 Use undici polyfill for tests in old Node versions (#28887)
We currently don't test FormData / File dependent features in CI because
we use an old Node.js version in CI. We should probably upgrade to 18
since that's really the minimum version that supports all the features
out of the box.

JSDOM is not a faithful/compatible implementation of these APIs. The
recommended way to use Flight together with FormData/Blob/File in older
Node.js versions, is to polyfill using the `undici` library.

However, even in these versions the Blob implementation isn't quite
faithful so the Reply client needs a slight tweak for multi-byte typed
arrays.
2024-05-03 16:29:09 -04:00
Sebastian Markbåge
d5c303427e [Flight] Track Owner on AsyncLocalStorage When Available (#28807)
Stacked on #28798.

Add another AsyncLocalStorage to the FlightServerConfig. This context
tracks data on a per component level. Currently the only thing we track
is the owner in DEV.

AsyncLocalStorage around each component comes with a performance cost so
we only do it DEV. It's not generally a particularly safe operation
because you can't necessarily associate side-effects with a component
based on execution scope. It can be a lazy initializer or cache():ed
code etc. We also don't support string refs anymore for a reason.

However, it's good enough for optional dev only information like the
owner.
2024-05-03 16:29:00 -04:00