Skip to content

[Flight] Build node-webstreams version of bundled webpack server #33456

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

Merged
merged 2 commits into from
Jun 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export {default as rendererVersion} from 'shared/ReactVersion';
export const rendererPackageName = 'react-server-dom-webpack';

export * from 'react-client/src/ReactFlightClientStreamConfigWeb';
export * from 'react-client/src/ReactClientConsoleConfigServer';
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpack';
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigBundlerWebpackServer';
export * from 'react-server-dom-webpack/src/client/ReactFlightClientConfigTargetWebpackServer';
export * from 'react-dom-bindings/src/shared/ReactFlightClientConfigDOM';
export const usedWithSSR = true;
25 changes: 23 additions & 2 deletions packages/react-server-dom-webpack/npm/client.node.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
'use strict';

var n, w;
if (process.env.NODE_ENV === 'production') {
module.exports = require('./cjs/react-server-dom-webpack-client.node.production.js');
n = require('./cjs/react-server-dom-webpack-client.node.production.js');
w = require('./cjs/react-server-dom-webpack-client.node-webstreams.production.js');
} else {
module.exports = require('./cjs/react-server-dom-webpack-client.node.development.js');
n = require('./cjs/react-server-dom-webpack-client.node.development.js');
w = require('./cjs/react-server-dom-webpack-client.node-webstreams.development.js');
}

exports.registerServerReference = function (r, i, e) {
return w.registerServerReference(n.registerServerReference(r, i, e), i, e);
};
exports.createServerReference = function (i, c, e, d, f) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will this wrapper mess with source mapping? I remember you mentioned in the past that module indirection is ok, but wrapper is not, because React doesn't let you customize how many stack frames to pop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That only matters on the server entry point and that one isn't wrapped because they are not using module state but instead attaches meta data on the function. I was hoping to change that to be the shared state model as the client but if we do that we'll just also have to hoist that shared state into a separate internal bundle.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually I'm wrong about this. It doesn't matter for registerServerReference but it does matter for createServerReference.

return w.registerServerReference(
n.createServerReference(i, c, e, d, f),
Copy link
Collaborator

Choose a reason for hiding this comment

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

I missed this in #33442, but c, e, d, f are all ignored for the edge and node builds, aren't they? The original function with all params only seems be exported in the browser build, if I'm not mistaken.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

They are now but I'm not sure they always will so for now just passing along the fully signature in case we change it.

We really should have one of these builds for SSR and a different one for RSC because they have different constraints. Calling a Server Reference from RSC should maybe be ok.

Also, we should really be passing the DEV only findSourceMapURL and functionName through since they can be useful for DEV features.

So we should really accept all args.

i,
e
);
};

exports.createFromNodeStream = n.createFromNodeStream;
exports.createFromFetch = w.createFromFetch;
exports.createFromReadableStream = w.createFromReadableStream;

exports.createTemporaryReferenceSet = w.createTemporaryReferenceSet;
exports.encodeReply = w.encodeReply;
11 changes: 8 additions & 3 deletions packages/react-server-dom-webpack/npm/server.node.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use strict';

var s;
var s, w;
if (process.env.NODE_ENV === 'production') {
s = require('./cjs/react-server-dom-webpack-server.node.production.js');
s = require('./cjs/react-server-dom-webpack-server.node.unbundled.production.js');
w = require('./cjs/react-server-dom-webpack-server.node-webstreams.unbundled.production.js');
} else {
s = require('./cjs/react-server-dom-webpack-server.node.development.js');
s = require('./cjs/react-server-dom-webpack-server.node.unbundled.development.js');
w = require('./cjs/react-server-dom-webpack-server.node-webstreams.unbundled.development.js');
}

exports.renderToPipeableStream = s.renderToPipeableStream;
Expand All @@ -16,3 +18,6 @@ exports.registerServerReference = s.registerServerReference;
exports.registerClientReference = s.registerClientReference;
exports.createClientModuleProxy = s.createClientModuleProxy;
exports.createTemporaryReferenceSet = s.createTemporaryReferenceSet;

exports.renderToReadableStream = w.renderToReadableStream;
exports.decodeReplyFromAsyncIterable = w.decodeReplyFromAsyncIterable;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export * from './ReactFlightDOMClientEdge';
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

export {
renderToReadableStream,
prerender as unstable_prerender,
decodeReply,
decodeReplyFromAsyncIterable,
decodeAction,
decodeFormState,
registerServerReference,
registerClientReference,
createClientModuleProxy,
createTemporaryReferenceSet,
} from './ReactFlightDOMServerEdge';
9 changes: 7 additions & 2 deletions scripts/jest/setupTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,14 +296,19 @@ if (process.env.REACT_CLASS_EQUIVALENCE_TEST) {

// We mock createHook so that we can automatically clean it up.
let installedHook = null;
let outgoingHook = null;
jest.mock('async_hooks', () => {
const actual = jest.requireActual('async_hooks');
return {
...actual,
createHook(config) {
if (installedHook) {
installedHook.disable();
// We unmount when there's more than two hooks installed.
// We use two because the build of server.node actually installs two hooks.
// One in each build.
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This reveals that we're actually installing two async_hooks when you import server.node. One in the Node build and one in the Node Web Streams build. 😬 Since it's a side-effect of requiring these.

Basically you have to deep link to make it optimized.

if (outgoingHook) {
outgoingHook.disable();
}
outgoingHook = installedHook;
return (installedHook = actual.createHook(config));
},
};
Expand Down
23 changes: 23 additions & 0 deletions scripts/rollup/bundles.js
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,18 @@ const bundles = [
wrapWithModuleBoundaries: false,
externals: ['react', 'util', 'crypto', 'async_hooks', 'react-dom'],
},
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: RENDERER,
entry:
'react-server-dom-webpack/src/server/react-flight-dom-server.node-webstreams',
name: 'react-server-dom-webpack-server.node-webstreams',
condition: 'react-server',
global: 'ReactServerDOMServer',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'util', 'crypto', 'async_hooks', 'react-dom'],
},
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: RENDERER,
Expand Down Expand Up @@ -530,6 +542,17 @@ const bundles = [
wrapWithModuleBoundaries: false,
externals: ['react', 'react-dom', 'util', 'crypto'],
},
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: RENDERER,
entry:
'react-server-dom-webpack/src/client/react-flight-dom-client.node-webstreams',
name: 'react-server-dom-webpack-client.node-webstreams',
global: 'ReactServerDOMClient',
minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'react-dom', 'util', 'crypto'],
},
{
bundleTypes: [NODE_DEV, NODE_PROD],
moduleType: RENDERER,
Expand Down
13 changes: 13 additions & 0 deletions scripts/shared/inlinedHostConfigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ module.exports = [
isFlowTyped: true,
isServerSupported: true,
},
{
shortName: 'dom-node-webstreams-webpack',
entryPoints: [
'react-server-dom-webpack/src/client/react-flight-dom-client.node-webstreams',
'react-server-dom-webpack/src/server/react-flight-dom-server.node-webstreams',
],
paths: [
'react-server-dom-webpack/src/client/react-flight-dom-client.node-webstreams',
'react-server-dom-webpack/src/server/react-flight-dom-server.node-webstreams',
],
isFlowTyped: false,
isServerSupported: true,
},
{
shortName: 'dom-node-turbopack',
entryPoints: [
Expand Down
Loading