Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flight] Add bundler-less version of RSC using plain ESM #26889

Merged
merged 1 commit into from
Jun 3, 2023

Conversation

sebmarkbage
Copy link
Collaborator

@sebmarkbage sebmarkbage commented Jun 2, 2023

This isn't really meant to be actually used, there are many issues with this approach, but it shows the capabilities as a proof-of-concept.

It's a new reference implementation package react-server-dom-esm as well as a fixture in fixtures/flight-esm (fork of fixtures/flight). This works pretty much the same as pieces we already have in the Webpack implementation but instead of loading modules using Webpack on the client it uses native browser ESM.

To really show it off, I don't use any JSX in the fixture and so it also doesn't use Babel or any compilation of the files.

This works because we don't actually bundle the server in the reference implementation in the first place. We instead use Node.js Loaders to intercept files that contain "use client" and "use server" and replace them. There's a simple check for those exact bytes, and no parsing, so this is very fast.

Since the client isn't actually bundled, there's no module map needed. We can just send the file path to the file we want to load in the RSC payload for client references.

Since the existing reference implementation for Node.js already used ESM to load modules on the server, that all works the same, including Server Actions. No bundling.

There is one case that isn't implemented here. Importing a "use server" file from a Client Component. We don't have that implemented in the Webpack reference implementation neither - only in Next.js atm. In Webpack it would be implemented as a Webpack loader.

There are a few ways this can be implemented without a bundler:

  • We can intercept the request from the browser importing this file in the HTTP server, and do a quick scan for "use server" in the file and replace it just like we do with loaders in Node.js. This is effectively how Vite works and likely how anyone using this technique would have to support JSX anyway.
  • We can use native browser "loaders" once that's eventually available in the same way as in Node.js.
  • We can generate import maps for each file and replace it with a pointer to a placeholder file. This requires scanning these ahead of time which defeats the purposes.

Another case that's not implemented is the inline "use server" closure in a Server Component. That would require the existing loader to be a bit smarter but would still only "compile" files that contains those bytes in the fast path check. This would also happen in the loader that already exists so wouldn't do anything substantially different than what we currently have here.

@sebmarkbage sebmarkbage requested review from gaearon, gnoff and acdlite June 2, 2023 03:55
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Jun 2, 2023
@react-sizebot
Copy link

react-sizebot commented Jun 2, 2023

Comparing: e1ad4aa...acad290

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.min.js = 164.23 kB 164.23 kB = 51.73 kB 51.72 kB
oss-experimental/react-dom/cjs/react-dom.production.min.js = 171.67 kB 171.67 kB = 53.97 kB 53.97 kB
facebook-www/ReactDOM-prod.classic.js = 570.12 kB 570.12 kB = 100.57 kB 100.58 kB
facebook-www/ReactDOM-prod.modern.js = 553.90 kB 553.90 kB = 97.75 kB 97.75 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-experimental/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-experimental/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-experimental/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-experimental/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-experimental/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-experimental/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-stable-semver/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-stable-semver/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-stable-semver/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-stable-semver/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-stable-semver/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-stable/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-stable/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-stable/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-stable/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-stable/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-experimental/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-experimental/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-experimental/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-experimental/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-experimental/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-experimental/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-experimental/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-experimental/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-stable-semver/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-stable-semver/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-stable-semver/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-stable-semver/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-stable-semver/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-stable-semver/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-stable-semver/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-stable-semver/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.31 kB +∞% 0.00 kB 11.66 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 9.51 kB +∞% 0.00 kB 3.72 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.development.js +∞% 0.00 kB 46.70 kB +∞% 0.00 kB 11.58 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-client.node.production.min.js +∞% 0.00 kB 9.39 kB +∞% 0.00 kB 3.68 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.development.js +∞% 0.00 kB 92.75 kB +∞% 0.00 kB 22.11 kB
oss-stable/react-server-dom-esm/cjs/react-server-dom-esm-server.node.production.min.js +∞% 0.00 kB 22.89 kB +∞% 0.00 kB 8.03 kB
oss-stable/react-server-dom-esm/client.browser.js +∞% 0.00 kB 0.25 kB +∞% 0.00 kB 0.17 kB
oss-stable/react-server-dom-esm/client.js +∞% 0.00 kB 0.06 kB +∞% 0.00 kB 0.08 kB
oss-stable/react-server-dom-esm/client.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.development.js +∞% 0.00 kB 47.15 kB +∞% 0.00 kB 11.61 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-client.browser.production.min.js +∞% 0.00 kB 35.02 kB +∞% 0.00 kB 8.91 kB
oss-stable/react-server-dom-esm/esm/react-server-dom-esm-node-loader.production.min.js +∞% 0.00 kB 12.52 kB +∞% 0.00 kB 3.16 kB
oss-stable/react-server-dom-esm/index.js +∞% 0.00 kB 0.28 kB +∞% 0.00 kB 0.22 kB
oss-stable/react-server-dom-esm/server.js +∞% 0.00 kB 0.19 kB +∞% 0.00 kB 0.16 kB
oss-stable/react-server-dom-esm/server.node.js +∞% 0.00 kB 0.24 kB +∞% 0.00 kB 0.16 kB

Generated by 🚫 dangerJS against acad290

@dai-shi
Copy link
Contributor

dai-shi commented Jun 2, 2023

This isn't really meant to be actually used

I'm currently using react-server-dom-webpack reference implementation with hacks with Vite. I was hoping to have a non-bundler-specific implementation, something like react-server-dom-esm.

there are many issues with this approach

Are those issues specific to the -esm ref impl, or do they also apply to -webpack one?
So, my question is, what are things -esm one can't do, but -webpack one can?

The general question should be: How strongly would you suggest to avoid using -esm or even -webpack?

@sebmarkbage
Copy link
Collaborator Author

It’s not so much that you can’t use it, after all the -webpack on is what Next.js uses, but more that you probably need a lot more in addition in practice for efficiency and compatibility.

@sebmarkbage
Copy link
Collaborator Author

Btw we’re happy to host a Vite version upstream without hacks if a reasonably reusable and compatible version that follows the patterns of the others.

@dai-shi
Copy link
Contributor

dai-shi commented Jun 2, 2023

more that you probably need a lot more in addition in practice for efficiency and compatibility.

That makes sense. (that's something I'm interested in.)

happy to host a Vite version upstream without hacks

It would be great! I'd first explore if react-server-dom-esm works for my case, and will see.
cc @cyco130

@nksaraf
Copy link

nksaraf commented Jun 2, 2023

Btw we’re happy to host a Vite version upstream without hacks if a reasonably reusable and compatible version that follows the patterns of the others.

I have been working on something. I didn't know if the react team is down for an integration there but ill assemble the pieces and put out a PR if the interest is there

const action = (await import(filepath))[name];
// Validate that this is actually a function we intended to expose and
// not the client trying to invoke arbitrary functions. In a real app,
// you'd have a manifest verifying this before even importing it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would require a bundler, right? Just to confirm. Or does the loader do this somehow

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's pretty simple because it'll have been tagged as a server reference by the loader. So verifying is trivial. The decodeReply/decodeAction also has this issue atm.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The part I'm missing is, importing a file calls its top-level side effects, so isn't this a possible vector if the filename is arbitrary?

RejectedThenable,
} from 'shared/ReactTypes';

export type SSRManifest = string; // Module root path
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

woah


let stashedGetSource: null | GetSourceFunction = null;
let stashedResolve: null | ResolveFunction = null;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all of these exports needed or is some targeting older Nodes? Should we start dropping those?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some are targeting older Node yea.

Copy link
Collaborator

@gaearon gaearon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very cool

@sebmarkbage sebmarkbage merged commit f181ba8 into facebook:main Jun 3, 2023
github-actions bot pushed a commit that referenced this pull request Jun 3, 2023
This isn't really meant to be actually used, there are many issues with
this approach, but it shows the capabilities as a proof-of-concept.

It's a new reference implementation package `react-server-dom-esm` as
well as a fixture in `fixtures/flight-esm` (fork of `fixtures/flight`).
This works pretty much the same as pieces we already have in the Webpack
implementation but instead of loading modules using Webpack on the
client it uses native browser ESM.

To really show it off, I don't use any JSX in the fixture and so it also
doesn't use Babel or any compilation of the files.

This works because we don't actually bundle the server in the reference
implementation in the first place. We instead use [Node.js
Loaders](https://nodejs.org/api/esm.html#loaders) to intercept files
that contain `"use client"` and `"use server"` and replace them. There's
a simple check for those exact bytes, and no parsing, so this is very
fast.

Since the client isn't actually bundled, there's no module map needed.
We can just send the file path to the file we want to load in the RSC
payload for client references.

Since the existing reference implementation for Node.js already used ESM
to load modules on the server, that all works the same, including Server
Actions. No bundling.

There is one case that isn't implemented here. Importing a `"use
server"` file from a Client Component. We don't have that implemented in
the Webpack reference implementation neither - only in Next.js atm. In
Webpack it would be implemented as a Webpack loader.

There are a few ways this can be implemented without a bundler:

- We can intercept the request from the browser importing this file in
the HTTP server, and do a quick scan for `"use server"` in the file and
replace it just like we do with loaders in Node.js. This is effectively
how Vite works and likely how anyone using this technique would have to
support JSX anyway.
- We can use native browser "loaders" once that's eventually available
in the same way as in Node.js.
- We can generate import maps for each file and replace it with a
pointer to a placeholder file. This requires scanning these ahead of
time which defeats the purposes.

Another case that's not implemented is the inline `"use server"` closure
in a Server Component. That would require the existing loader to be a
bit smarter but would still only "compile" files that contains those
bytes in the fast path check. This would also happen in the loader that
already exists so wouldn't do anything substantially different than what
we currently have here.

DiffTrain build for [f181ba8](f181ba8)
@dai-shi
Copy link
Contributor

dai-shi commented Jun 10, 2023

Could you please publish react-server-dom-esm@canary?

(btw, found this: https://www.npmjs.com/package/@jacob-ebey/react-server-dom-esm )

EdisonVan pushed a commit to EdisonVan/react that referenced this pull request Apr 15, 2024
)

This isn't really meant to be actually used, there are many issues with
this approach, but it shows the capabilities as a proof-of-concept.

It's a new reference implementation package `react-server-dom-esm` as
well as a fixture in `fixtures/flight-esm` (fork of `fixtures/flight`).
This works pretty much the same as pieces we already have in the Webpack
implementation but instead of loading modules using Webpack on the
client it uses native browser ESM.

To really show it off, I don't use any JSX in the fixture and so it also
doesn't use Babel or any compilation of the files.

This works because we don't actually bundle the server in the reference
implementation in the first place. We instead use [Node.js
Loaders](https://nodejs.org/api/esm.html#loaders) to intercept files
that contain `"use client"` and `"use server"` and replace them. There's
a simple check for those exact bytes, and no parsing, so this is very
fast.

Since the client isn't actually bundled, there's no module map needed.
We can just send the file path to the file we want to load in the RSC
payload for client references.

Since the existing reference implementation for Node.js already used ESM
to load modules on the server, that all works the same, including Server
Actions. No bundling.

There is one case that isn't implemented here. Importing a `"use
server"` file from a Client Component. We don't have that implemented in
the Webpack reference implementation neither - only in Next.js atm. In
Webpack it would be implemented as a Webpack loader.

There are a few ways this can be implemented without a bundler:

- We can intercept the request from the browser importing this file in
the HTTP server, and do a quick scan for `"use server"` in the file and
replace it just like we do with loaders in Node.js. This is effectively
how Vite works and likely how anyone using this technique would have to
support JSX anyway.
- We can use native browser "loaders" once that's eventually available
in the same way as in Node.js.
- We can generate import maps for each file and replace it with a
pointer to a placeholder file. This requires scanning these ahead of
time which defeats the purposes.

Another case that's not implemented is the inline `"use server"` closure
in a Server Component. That would require the existing loader to be a
bit smarter but would still only "compile" files that contains those
bytes in the fast path check. This would also happen in the loader that
already exists so wouldn't do anything substantially different than what
we currently have here.
bigfootjon pushed a commit that referenced this pull request Apr 18, 2024
This isn't really meant to be actually used, there are many issues with
this approach, but it shows the capabilities as a proof-of-concept.

It's a new reference implementation package `react-server-dom-esm` as
well as a fixture in `fixtures/flight-esm` (fork of `fixtures/flight`).
This works pretty much the same as pieces we already have in the Webpack
implementation but instead of loading modules using Webpack on the
client it uses native browser ESM.

To really show it off, I don't use any JSX in the fixture and so it also
doesn't use Babel or any compilation of the files.

This works because we don't actually bundle the server in the reference
implementation in the first place. We instead use [Node.js
Loaders](https://nodejs.org/api/esm.html#loaders) to intercept files
that contain `"use client"` and `"use server"` and replace them. There's
a simple check for those exact bytes, and no parsing, so this is very
fast.

Since the client isn't actually bundled, there's no module map needed.
We can just send the file path to the file we want to load in the RSC
payload for client references.

Since the existing reference implementation for Node.js already used ESM
to load modules on the server, that all works the same, including Server
Actions. No bundling.

There is one case that isn't implemented here. Importing a `"use
server"` file from a Client Component. We don't have that implemented in
the Webpack reference implementation neither - only in Next.js atm. In
Webpack it would be implemented as a Webpack loader.

There are a few ways this can be implemented without a bundler:

- We can intercept the request from the browser importing this file in
the HTTP server, and do a quick scan for `"use server"` in the file and
replace it just like we do with loaders in Node.js. This is effectively
how Vite works and likely how anyone using this technique would have to
support JSX anyway.
- We can use native browser "loaders" once that's eventually available
in the same way as in Node.js.
- We can generate import maps for each file and replace it with a
pointer to a placeholder file. This requires scanning these ahead of
time which defeats the purposes.

Another case that's not implemented is the inline `"use server"` closure
in a Server Component. That would require the existing loader to be a
bit smarter but would still only "compile" files that contains those
bytes in the fast path check. This would also happen in the loader that
already exists so wouldn't do anything substantially different than what
we currently have here.

DiffTrain build for commit f181ba8.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants