Commit Graph
28 Commits
Author SHA1 Message Date
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 Silbermann f994737d14 [Flight] Allow temporary references in decodeReplyFromBusboy (#29219) 2024-05-22 21:15:14 +02: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
Andrew Clark 857ee8cdf9 Don't minify symbols in production builds (#28881)
This disables symbol renaming in production builds. The original
variable and function names are preserved. All other forms of
compression applied by Closure (dead code elimination, inlining, etc)
are unchanged — the final program is identical to what we were producing
before, just in a more readable form.

The motivation is to make it easier to debug React issues that only
occur in production — the same reason we decided to start shipping
sourcemaps in #28827 and #28827.

However, because most apps run their own minification step on their npm
dependencies, it's not necessary for us to minify the symbols before
publishing — it'll be handled the app, if desired.

This is the same strategy Meta has used to ship React for years. The
React build itself has unminified symbols, but they get minified as part
of Meta's regular build pipeline.

Even if an app does not minify their npm dependencies, gzip covers most
of the cost of symbol renaming anyway.

This saves us from having to ship sourcemaps, which means even apps that
don't have sourcemaps configured will be able to debug the React build
as easily as they would any other npm dependency.
2024-04-20 11:23:46 -04:00
Josh Story da6ba53b10 [UMD] Remove umd builds (#28735)
In React 19 React will finally stop publishing UMD builds. This is
motivated primarily by the lack of use of UMD format and the added
complexity of maintaining build infra for these releases. Additionally
with ESM becoming more prevalent in browsers and services like esm.sh
which can host React as an ESM module there are other options for doing
script tag based react loading.

This PR removes all the UMD build configs and forks.

There are some fixtures that still have references to UMD builds however
many of them already do not work (for instance they are using legacy
features like ReactDOM.render) and rather than block the removal on
these fixtures being brought up to date we'll just move forward and fix
or removes fixtures as necessary in the future.
2024-04-17 11:15:27 -07:00
Sebastian Markbåge d50323eb84 Flatten ReactSharedInternals (#28783)
This is similar to #28771 but for isomorphic. We need a make over for
these dispatchers anyway so this is the first step. Also helps flush out
some internals usage that will break anyway.

It flattens the inner mutable objects onto the ReactSharedInternals.
2024-04-08 19:23:23 -04:00
Timothy Yung 13f35433bc Mock modules in tests using Module (#28678)
## Summary

Fixes a type validation error introduced in newer versions of Node.js
when calling `Module.prototype._compile` in our unit tests. (I tried but
have yet to pinpoint the precise change in Node.js that introduced this
vaildation.)

The specific error that currently occurs when running unit tests with
Node.js v18.16.1:

```
TypeError: The "mod" argument must be an instance of Module. Received an instance of Object

      80 |
      81 |     if (useServer) {
    > 82 |       originalCompile.apply(this, arguments);
         |                       ^
      83 |
      84 |       const moduleId: string = (url.pathToFileURL(filename).href: any);
      85 |
```

This fixes the type validation error by mocking modules using `new
Module()` instead of plain objects.

## How did you test this change?

Ran the unit tests successfully:

```
$ node --version
v18.16.1

$ yarn test
```
2024-03-29 13:19:54 -07:00
Andrew Clark 56efb2e227 Bump canary versions to v19-canary (#28646)
This bumps the canary versions to v19 to communicate that the next
release will be a major. Once this lands, we can start merging breaking
changes into `main`.
2024-03-26 15:31:57 -04:00
Jan Kassens 527ed72bfd Cleanup enableFormActions flag (#28614)
Cleanup enableFormActions flag
2024-03-25 13:25:14 -04:00
Sebastian Markbåge 83409a1fdd [Flight] Encode React Elements in Replies as Temporary References (#28564)
Currently you can accidentally pass React Element to a Server Action. It
warns but in prod it actually works because we can encode the symbol and
otherwise it's mostly a plain object. It only works if you only pass
host components and no function props etc. which makes it potentially
error later. The first thing this does it just early hard error for
elements.

I made Lazy work by unwrapping though since that will be replaced by
Promises later which works.

Our protocol is not fully symmetric in that elements flow from Server ->
Client. Only the Server can resolve Components and only the client
should really be able to receive host components. It's not intended that
a Server can actually do something with them other than passing them to
the client.

In the case of a Reply, we expect the client to be stateful. It's
waiting for a response. So anything we can't serialize we can still pass
by reference to an in memory object. So I introduce the concept of a
TemporaryReferenceSet which is an opaque object that you create before
encoding the reply. This then stashes any unserializable values in this
set and encode the slot by id. When a new response from the Action then
returns we pass the same temporary set into the parser which can then
restore the objects. This lets you pass a value by reference to the
server and back into another slot.

For example it can be used to render children inside a parent tree from
a server action:

```
export async function Component({ children }) {
  "use server";
  return <div>{children}</div>;
}
```

(You wouldn't normally do this due to the waterfalls but for advanced
cases.)

A common scenario where this comes up accidentally today is in
`useActionState`.

```
export function action(state, formData) {
  "use server";
   if (errored) {
     return <div>This action <strong>errored</strong></div>;
   }
   return null;
}
```

```
const [errors, formAction] = useActionState(action);
return <div>{errors}<div>;
```

It feels like I'm just passing the JSX from server to client. However,
because `useActionState` also sends the previous state *back* to the
server this should not actually be valid. Before this PR this actually
worked accidentally. You get a DEV warning but it used to work in prod.
Once you do something like pass a client reference it won't work tho. We
could perhaps make client references work by stashing where we got them
from but it wouldn't work with all possible JSX.

By adding temporary references to the action implementation this will
work again - on the client. It'll also be more efficient since we don't
send back the JSX content that you shouldn't introspect on the server
anyway.

However, a flaw here is that the progressive enhancement of this case
won't work because we can't use temporary references for progressive
enhancement since there's no in memory stash. What is worse is that it
won't error if you hydrate. ~It also will error late in the example
above because the first state is "undefined" so invoking the form once
works - it errors on the second attempt when it tries to send the error
state back again.~ It actually errors on the first invocation because we
need to eagerly serialize "previous state" into the form. So at least
that's better.

I think maybe the solution to this particular pattern would be to allow
JSX to serialize if you have no temporary reference set, and remember
client references so that client references can be returned back to the
server as client references. That way anything you could send from the
server could also be returned to the server. But it would only deopt to
serializing it for progressive enhancement. The consequence of that
would be that there's a lot of JSX that might accidentally seem like it
should work but it's only if you've gotten it from the server before
that it works. This would have to have pair them somehow though since
you can't take a client reference from one implementation of Flight and
use it with another.
2024-03-19 16:59:52 -04:00
dan 11828bf529 Remove loose-envify dep and browserify configs (#28480)
Browserify isn't widely used, and at this point I don't think it makes
sense to have a special config just for the sake of it. Let's remove it?
2024-03-01 20:49:51 +00:00
Ricky 5f2c6b74db Update homepage URLs to react.dev (#28478)
Updates the package.json "homepage" entry to react.dev
2024-03-01 14:35:18 -05:00
Sebastian Markbåge 9444c51c7e [Flight] Allow a Server Reference to be registered twice (#28343)
It's possible for the same function instance to appear more than once in
the same graph or even the same file.

Currently this errors on trying to reconfigure the property but it
really doesn't matter which one wins. First or last.

Regardless there will be an entry point generated that can get them.
2024-02-19 11:49:38 -05:00
Sebastian Markbåge c323f8290c Encode better error messages when part of the context is a client reference (#28355)
Alternative to #28354.

If a client reference is one of the props being describes as part of
another error, we call toString on it, which errors.

We should error explicitly when a Symbol prop is extracted.

However, pragmatically I added the toString symbol tag even though we
don't know what the real tostring will be but we also lie about the
typeof.

We can however in addition to this give it a different description
because describing this property as an object isn't quite right.

We probably could extract the export name but that's kind of renderer
specific and I just added this shared module to Fizz which doesn't have
that which is unfortunate an consequence.

For default exports we don't have a good name of what the alias was in
the receiver. Could maybe call it "default" but for now I just call it
"client".
2024-02-19 11:49:25 -05:00
Hendrik Liebau 92686722ca [Flight] Fix reference param type in registerServerReference (#27688)
The `reference` that is passed into `registerServerReference` can be a
plain function. It does not need to have the three additonal properties
of a `ServerRefeference`. In fact, adding these properties (plus `bind`)
is precisely what `registerServerReference` does.
2024-02-15 16:09:25 -05:00
Andrew Clark 015ff2ed66 Revert "[Tests] Reset modules by default" (#28318)
This was causing a slowdown in one of the tests
ESLintRuleExhaustiveDeps-test.js. Reverting until we figure out why.
2024-02-13 11:39:45 -05:00
Sebastian Markbåge 8d48183291 [Flight] Allow custom encoding of the form action (#27563)
There are three parts to an RSC set up:

- React
- Bundler
- Endpoints

Most customizability is in the bundler configs. We deal with those as
custom builds.

To create a full set up, you need to also configure ways to expose end
points for example to call a Server Action. That's typically not
something the bundler is responsible for even though it's responsible
for gathering the end points that needs generation. Exposing which
endpoints to generate is a responsibility for the bundler.

Typically a meta-framework is responsible for generating the end points.

There's two ways to "call" a Server Action. Through JS and through a
Form. Through JS we expose the `callServer` callback so that the
framework can call the end point.

Forms by default POST back to the current page with an action serialized
into form data, which we have a decoder helper for. However, this is not
something that React is really opinionated about just like we're not
opinionated about the protocol used by callServer.

This exposes an option to configure the encoding of the form props.
`encodeFormAction` is to the SSR is what `callServer` is to the Browser.
2024-02-12 20:02:42 -05:00
Sebastian Markbåge 629541bcc0 [Flight] Transfer Debug Info in Server-to-Server Flight Requests (#28275)
A Flight Server can be a consumer of a stream from another Server. In
this case the meta data is attached to debugInfo properties on lazy,
Promises, Arrays or Elements that might in turn get forwarded to the
next stream. In this case we want to forward this debug information to
the client in the stream.

I also added a DEV only `environmentName` option to the Flight Server.
This lets you name the server that is producing the debug info so that
you can trace the origin of where that component is executing. This
defaults to `"server"`. DevTools could use this for badges or different
colors.
2024-02-12 13:38:14 -05:00
Ricky 30e2938e04 [Tests] Reset modules by default (#28254)
## Overview

Sets `resetModules: true` in the base Jest config, and deletes all the
`jest.resetModule()` calls we don't need.
2024-02-06 12:43:27 -05:00
dan 472854820b [Flight] Delete Server Context (#28225)
Server Context was never documented, and has been deprecated in
https://github.com/facebook/react/pull/27424.

This PR removes it completely, including the implementation code.

Notably, `useContext` is removed from the shared subset, so importing it
from a React Server environment would now should be a build error in
environments that are able to enforce that.
2024-02-05 22:39:15 +00:00
Andrew Clark 5d1b15a4f0 Rename "shared subset" to "server" (#27939)
The internal file ReactSharedSubset is what the `react` module resolves
to when imported from a Server Component environment. We gave it this
name because, originally, the idea was that Server Components can access
a subset of the APIs available on the client.

However, since then, we've also added APIs that can _only_ by accessed
on the server and not the client. In other words, it's no longer a
subset, it's a slightly different overlapping set.

So this commit renames ReactSharedSubet to ReactServer and updates all
the references. This does not affect the public API, only our internal
implementation.
2024-01-16 19:58:11 -05:00
Sebastian Markbåge 07cc4a0002 [Flight] Bound server references should be able to be bound again (#27695)
Wasn't consistent. Probably should use a shared helper maybe.
2023-11-13 22:45:40 -05:00
Josh StoryandZack Tanner 08a39539fc [Flight][Reply] Close Response after creating root chunk (#27634)
creating the root after closing the response can lead to a promise that
never rejects. This is not intended use of the decodeReply API but if
pathalogical cases where you pass a raw FormData into this fucntion with
no zero chunk it can hang forever. This reordering causes a connection
error instead

---------

Co-authored-by: Zack Tanner <zacktanner@gmail.com>
2023-10-31 18:31:10 -07:00
Josh Story 90172d12e8 [Flight] Cleanup turbopack tests (#27552)
Renames turbopack flight test files. removes tests which are primarily
testing internal implemenation behavior of Flight Client itself
2023-10-20 23:21:31 -07:00
Josh Story 3a1967b7d5 [Flight] Expose encodeReply from ReactFlightDOMClientEdge (#27551)
Adds encodeReply to ReactFlightDOMClientEdge in webpack and turbopack
bindings
2023-10-19 17:24:47 -07:00
Sebastian Markbåge c7ba8c0988 Enforce that the "react-server" build of "react" is used (#27436)
I do this by simply renaming the secret export name in the "subset"
bundle and this renamed version is what the FlightServer uses.

This requires us to be more diligent about always using the correct
instance of "react" in our tests so there's a bunch of clean up for
that.
2023-09-29 18:24:05 -04:00
Andrew Clark 62512bafc6 Test fix: Add missing warning assertion (#27434)
Adds a missing test assertion for Server Context deprecation.

The PR that added this warning was based on an older revision than the
PR that added the test.
2023-09-28 12:21:11 -04:00
Josh Story f81c0f1ed9 [Flight] Implement react-server-dom-turbopack (#27315)
stacked on #27314 

Turbopack requires a different module loading strategy than Webpack and
as such this PR implements a new package `react-server-dom-turbopack`
which largely follows the `react-server-dom-webpack` but is implemented
for this new bundler
2023-09-27 10:03:57 -07:00