Skip to content

Commit e375e2b

Browse files
committed
ESM implementation of module preinitialization
1 parent ff0dce0 commit e375e2b

File tree

16 files changed

+126
-36
lines changed

16 files changed

+126
-36
lines changed

fixtures/flight-esm/.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v18

fixtures/flight-esm/server/global.js

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const compress = require('compression');
1010
const chalk = require('chalk');
1111
const express = require('express');
1212
const http = require('http');
13+
const React = require('react');
1314

1415
const {renderToPipeableStream} = require('react-dom/server');
1516
const {createFromNodeStream} = require('react-server-dom-esm/client');
@@ -62,20 +63,29 @@ app.all('/', async function (req, res, next) {
6263
if (req.accepts('text/html')) {
6364
try {
6465
const rscResponse = await promiseForData;
65-
6666
const moduleBaseURL = '/src';
6767

6868
// For HTML, we're a "client" emulator that runs the client code,
6969
// so we start by consuming the RSC payload. This needs the local file path
7070
// to load the source files from as well as the URL path for preloads.
71-
const root = await createFromNodeStream(
72-
rscResponse,
73-
moduleBasePath,
74-
moduleBaseURL
75-
);
71+
72+
let root;
73+
let Root = () => {
74+
if (root) {
75+
return React.use(root);
76+
}
77+
78+
return React.use(
79+
(root = createFromNodeStream(
80+
rscResponse,
81+
moduleBasePath,
82+
moduleBaseURL
83+
))
84+
);
85+
};
7686
// Render it into HTML by resolving the client components
7787
res.set('Content-type', 'text/html');
78-
const {pipe} = renderToPipeableStream(root, {
88+
const {pipe} = renderToPipeableStream(React.createElement(Root), {
7989
// TODO: bootstrapModules inserts a preload before the importmap which causes
8090
// the import map to be invalid. We need to fix that in Float somehow.
8191
// bootstrapModules: ['/src/index.js'],
@@ -89,6 +99,7 @@ app.all('/', async function (req, res, next) {
8999
} else {
90100
try {
91101
const rscResponse = await promiseForData;
102+
92103
// For other request, we pass-through the RSC payload.
93104
res.set('Content-type', 'text/x-component');
94105
rscResponse.on('data', data => {

fixtures/flight-esm/server/region.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ async function renderApp(res, returnValue) {
3636
// For client-invoked server actions we refresh the tree and return a return value.
3737
const payload = returnValue ? {returnValue, root} : root;
3838
const {pipe} = renderToPipeableStream(payload, moduleBasePath);
39+
await new Promise(res => {
40+
setTimeout(res, 1000);
41+
});
3942
pipe(res);
4043
}
4144

fixtures/flight/server/global.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,17 @@ app.all('/', async function (req, res, next) {
147147
let root;
148148
let Root = () => {
149149
if (root) {
150-
return root;
150+
return React.use(root);
151151
}
152-
root = createFromNodeStream(rscResponse, ssrBundleConfig.ssrManifest, {
153-
moduleLoading: ssrBundleConfig.moduleLoading,
154-
});
155-
return root;
152+
return React.use(
153+
(root = createFromNodeStream(
154+
rscResponse,
155+
ssrBundleConfig.ssrManifest,
156+
{
157+
moduleLoading: ssrBundleConfig.moduleLoading,
158+
}
159+
))
160+
);
156161
};
157162
// Render it into HTML by resolving the client components
158163
res.set('Content-type', 'text/html');

fixtures/flight/server/region.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ async function renderApp(res, returnValue) {
9595
// For client-invoked server actions we refresh the tree and return a return value.
9696
const payload = returnValue ? {returnValue, root} : root;
9797
const {pipe} = renderToPipeableStream(payload, moduleMap);
98+
await new Promise(res => {
99+
setTimeout(res, 1000);
100+
});
98101
pipe(res);
99102
}
100103

packages/react-client/src/forks/ReactFlightClientConfig.dom-browser-esm.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
export * from 'react-client/src/ReactFlightClientConfigBrowser';
11-
export * from 'react-server-dom-esm/src/ReactFlightClientConfigESMBundler';
11+
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
12+
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMBrowser';
1213
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
1314
export const usedWithSSR = false;

packages/react-client/src/forks/ReactFlightClientConfig.dom-node-esm.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
* @flow
88
*/
99

10-
export * from 'react-client/src/ReactFlightClientConfigBrowser';
11-
export * from 'react-server-dom-esm/src/ReactFlightClientConfigESMBundler';
10+
export * from 'react-client/src/ReactFlightClientConfigNode';
11+
export * from 'react-server-dom-esm/src/ReactFlightClientConfigBundlerESM';
12+
export * from 'react-server-dom-esm/src/ReactFlightClientConfigTargetESMServer';
1213
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
1314
export const usedWithSSR = true;

packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5228,6 +5228,7 @@ function preinit(href: string, options: PreinitOptions): void {
52285228
}
52295229
return;
52305230
}
5231+
case 'module':
52315232
case 'script': {
52325233
const src = href;
52335234
const key = getResourceKey(as, src);

packages/react-dom-bindings/src/shared/ReactFlightClientConfigDOM.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,18 @@ export function dispatchHint(code: string, model: HintModel): void {
6464
}
6565
}
6666

67-
export function preinitModulesForSSR(href: string, crossOrigin: ?string) {
67+
export function preinitModuleForSSR(href: string, crossOrigin: ?string) {
68+
const dispatcher = ReactDOMCurrentDispatcher.current;
69+
if (dispatcher) {
70+
if (typeof crossOrigin === 'string') {
71+
dispatcher.preinitModule(href, {crossOrigin});
72+
} else {
73+
dispatcher.preinitModule(href);
74+
}
75+
}
76+
}
77+
78+
export function preinitScriptForSSR(href: string, crossOrigin: ?string) {
6879
const dispatcher = ReactDOMCurrentDispatcher.current;
6980
if (dispatcher) {
7081
if (typeof crossOrigin === 'string') {

packages/react-server-dom-esm/src/ReactFlightClientConfigESMBundler.js renamed to packages/react-server-dom-esm/src/ReactFlightClientConfigBundlerESM.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ import type {
1212
FulfilledThenable,
1313
RejectedThenable,
1414
} from 'shared/ReactTypes';
15+
import type {ModuleLoading} from 'react-client/src/ReactFlightClientConfig';
1516

1617
export type SSRManifest = string; // Module root path
1718

1819
export type ServerManifest = string; // Module root path
1920

2021
export type ServerReferenceId = string;
2122

23+
import {prepareDestinationForModuleImpl} from 'react-client/src/ReactFlightClientConfig';
24+
2225
export opaque type ClientReferenceMetadata = [
2326
string, // module path
2427
string, // export name
@@ -30,6 +33,19 @@ export opaque type ClientReference<T> = {
3033
name: string,
3134
};
3235

36+
// The reason this function needs to defined here in this file instead of just
37+
// being exported directly from the WebpackDestination... file is because the
38+
// ClientReferenceMetadata is opaque and we can't unwrap it there.
39+
// This should get inlined and we could also just implement an unwrapping function
40+
// though that risks it getting used in places it shouldn't be. This is unfortunate
41+
// but currently it seems to be the best option we have.
42+
export function prepareDestinationForModule(
43+
moduleLoading: ModuleLoading,
44+
metadata: ClientReferenceMetadata,
45+
) {
46+
prepareDestinationForModuleImpl(moduleLoading, metadata[0]);
47+
}
48+
3349
export function resolveClientReference<T>(
3450
bundlerConfig: SSRManifest,
3551
metadata: ClientReferenceMetadata,

0 commit comments

Comments
 (0)