From b0e511cc9d793c0772442ad1667dcd685a0a0d66 Mon Sep 17 00:00:00 2001 From: Hendrik Liebau Date: Sat, 7 Jun 2025 21:16:55 +0200 Subject: [PATCH] [Flight] Use Web Streams APIs for 3rd-party component in Flight fixture Now that the Node bundles for Webpack also expose the Web Streams APIs, we can can use those in the Flight fixture for caching the 3rd-party component, without needing to convert the streams back and forth. This new approach of how we're exposing those APIs also allows us to mix and match them with the Node Streams APIs (e.g. in the outer rendering in `fixtures/flight/server/region.js`). This was not possibly with the previous approach of using separate bundles, because React only allows a single RSC renderer to be active at a time. --- fixtures/flight/src/App.js | 52 +++++++++++++++----------------------- 1 file changed, 21 insertions(+), 31 deletions(-) diff --git a/fixtures/flight/src/App.js b/fixtures/flight/src/App.js index d244ec8d39402..8dacafd92310a 100644 --- a/fixtures/flight/src/App.js +++ b/fixtures/flight/src/App.js @@ -1,6 +1,6 @@ import * as React from 'react'; -import {renderToPipeableStream} from 'react-server-dom-webpack/server'; -import {createFromNodeStream} from 'react-server-dom-webpack/client'; +import {renderToReadableStream} from 'react-server-dom-webpack/server'; +import {createFromReadableStream} from 'react-server-dom-webpack/client'; import {PassThrough, Readable} from 'stream'; import Container from './Container.js'; @@ -46,43 +46,33 @@ async function ThirdPartyComponent() { return delay('hello from a 3rd party', 30); } -// Using Web streams for tee'ing convenience here. -let cachedThirdPartyReadableWeb; +let cachedThirdPartyStream; // We create the Component outside of AsyncLocalStorage so that it has no owner. // That way it gets the owner from the call to createFromNodeStream. const thirdPartyComponent = ; function fetchThirdParty(noCache) { - if (cachedThirdPartyReadableWeb && !noCache) { - const [readableWeb1, readableWeb2] = cachedThirdPartyReadableWeb.tee(); - cachedThirdPartyReadableWeb = readableWeb1; - - return createFromNodeStream(Readable.fromWeb(readableWeb2), { + // We're using the Web Streams APIs for tee'ing convenience. + const stream = + cachedThirdPartyStream && !noCache + ? cachedThirdPartyStream + : renderToReadableStream( + thirdPartyComponent, + {}, + {environmentName: 'third-party'} + ); + + const [stream1, stream2] = stream.tee(); + cachedThirdPartyStream = stream1; + + return createFromReadableStream(stream2, { + serverConsumerManifest: { moduleMap: {}, - moduleLoading: {}, - }); - } - - const stream = renderToPipeableStream( - thirdPartyComponent, - {}, - {environmentName: 'third-party'} - ); - - const readable = new PassThrough(); - // React currently only supports piping to one stream, so we convert, tee, and - // convert back again. - // TODO: Switch to web streams without converting when #33442 has landed. - const [readableWeb1, readableWeb2] = Readable.toWeb(readable).tee(); - cachedThirdPartyReadableWeb = readableWeb1; - const result = createFromNodeStream(Readable.fromWeb(readableWeb2), { - moduleMap: {}, - moduleLoading: {}, + serverModuleMap: null, + moduleLoading: null, + }, }); - stream.pipe(readable); - - return result; } async function ServerComponent({noCache}) {