From 269ac62065c304ec39a97dd825c5da532dbeb0e2 Mon Sep 17 00:00:00 2001 From: Josh Story Date: Wed, 8 Nov 2023 10:29:05 -0800 Subject: [PATCH] Previously it was possilbe to postpone in the shell during a prerender and then during a resume the bootstrap scripts would not be emitted leading to no hydration on the client. This change moves the bootstrap configuration to ResumableState where it can be serialized after postponing if it wasn't flushed as part of the static shell. --- .../src/server/ReactFizzConfigDOM.js | 30 ++++++++++++++----- .../src/server/ReactFizzConfigDOMLegacy.js | 2 -- .../src/server/ReactDOMFizzServerBrowser.js | 6 ++-- .../src/server/ReactDOMFizzServerBun.js | 6 ++-- .../src/server/ReactDOMFizzServerEdge.js | 6 ++-- .../src/server/ReactDOMFizzServerNode.js | 6 ++-- .../src/server/ReactDOMFizzStaticBrowser.js | 6 ++-- .../src/server/ReactDOMFizzStaticEdge.js | 6 ++-- .../src/server/ReactDOMFizzStaticNode.js | 6 ++-- .../src/ReactDOMServerFB.js | 6 ++-- packages/react-server/src/ReactFizzServer.js | 6 +++- 11 files changed, 51 insertions(+), 35 deletions(-) diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js index e8f9295d92983..fea9c74959402 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js @@ -245,6 +245,13 @@ export type ResumableState = { nextFormID: number, streamingFormat: StreamingFormat, + // We carry the bootstrap intializers in resumable state in case we postpone in the shell + // of a prerender. On resume we will reinitialize the bootstrap scripts if necessary. + // If we end up flushing the bootstrap scripts we void these on the resumable state + bootstrapScriptContent?: string | void, + bootstrapScripts?: $ReadOnlyArray | void, + bootstrapModules?: $ReadOnlyArray | void, + // state for script streaming format, unused if using external runtime / data instructions: InstructionState, @@ -349,9 +356,6 @@ const DEFAULT_HEADERS_CAPACITY_IN_UTF16_CODE_UNITS = 2000; export function createRenderState( resumableState: ResumableState, nonce: string | void, - bootstrapScriptContent: string | void, - bootstrapScripts: $ReadOnlyArray | void, - bootstrapModules: $ReadOnlyArray | void, externalRuntimeConfig: string | BootstrapScriptDescriptor | void, importMap: ImportMap | void, onHeaders: void | ((headers: HeadersDescriptor) => void), @@ -367,6 +371,8 @@ export function createRenderState( const bootstrapChunks: Array = []; let externalRuntimeScript: null | ExternalRuntimeScript = null; + const {bootstrapScriptContent, bootstrapScripts, bootstrapModules} = + resumableState; if (bootstrapScriptContent !== undefined) { bootstrapChunks.push( inlineScriptWithNonce, @@ -612,9 +618,6 @@ export function resumeRenderState( return createRenderState( resumableState, nonce, - // These should have already been flushed in the prerender. - undefined, - undefined, undefined, undefined, undefined, @@ -625,6 +628,9 @@ export function resumeRenderState( export function createResumableState( identifierPrefix: string | void, externalRuntimeConfig: string | BootstrapScriptDescriptor | void, + bootstrapScriptContent: string | void, + bootstrapScripts: $ReadOnlyArray | void, + bootstrapModules: $ReadOnlyArray | void, ): ResumableState { const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix; @@ -638,6 +644,9 @@ export function createResumableState( idPrefix: idPrefix, nextFormID: 0, streamingFormat, + bootstrapScriptContent, + bootstrapScripts, + bootstrapModules, instructions: NothingSent, hasBody: false, hasHtml: false, @@ -3714,7 +3723,11 @@ export function pushEndInstance( function writeBootstrap( destination: Destination, renderState: RenderState, + resumableState: ResumableState, ): boolean { + resumableState.bootstrapScriptContent = undefined; + resumableState.bootstrapScripts = undefined; + resumableState.bootstrapModules = undefined; const bootstrapChunks = renderState.bootstrapChunks; let i = 0; for (; i < bootstrapChunks.length - 1; i++) { @@ -3731,8 +3744,9 @@ function writeBootstrap( export function writeCompletedRoot( destination: Destination, renderState: RenderState, + resumableState: ResumableState, ): boolean { - return writeBootstrap(destination, renderState); + return writeBootstrap(destination, renderState, resumableState); } // Structural Nodes @@ -4197,7 +4211,7 @@ export function writeCompletedBoundaryInstruction( } else { writeMore = writeChunkAndReturn(destination, completeBoundaryDataEnd); } - return writeBootstrap(destination, renderState) && writeMore; + return writeBootstrap(destination, renderState, resumableState) && writeMore; } const clientRenderScript1Full = stringToPrecomputedChunk( diff --git a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js index 72b8b1e07a759..60cdcc2d96433 100644 --- a/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js +++ b/packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js @@ -92,8 +92,6 @@ export function createRenderState( undefined, undefined, undefined, - undefined, - undefined, ); return { // Keep this in sync with ReactFizzConfigDOM diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js index 14acfcb739b23..e1dc396782265 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js @@ -114,6 +114,9 @@ function renderToReadableStream( const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createRequest( children, @@ -121,9 +124,6 @@ function renderToReadableStream( createRenderState( resumableState, options ? options.nonce : undefined, - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, onHeadersImpl, diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBun.js b/packages/react-dom/src/server/ReactDOMFizzServerBun.js index e125a142c3037..309eb2b8d9d5b 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerBun.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerBun.js @@ -104,6 +104,9 @@ function renderToReadableStream( const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createRequest( children, @@ -111,9 +114,6 @@ function renderToReadableStream( createRenderState( resumableState, options ? options.nonce : undefined, - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, onHeadersImpl, diff --git a/packages/react-dom/src/server/ReactDOMFizzServerEdge.js b/packages/react-dom/src/server/ReactDOMFizzServerEdge.js index 14acfcb739b23..e1dc396782265 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerEdge.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerEdge.js @@ -114,6 +114,9 @@ function renderToReadableStream( const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createRequest( children, @@ -121,9 +124,6 @@ function renderToReadableStream( createRenderState( resumableState, options ? options.nonce : undefined, - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, onHeadersImpl, diff --git a/packages/react-dom/src/server/ReactDOMFizzServerNode.js b/packages/react-dom/src/server/ReactDOMFizzServerNode.js index ca280be5edd7e..a37bea02c8aea 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerNode.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerNode.js @@ -88,6 +88,9 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) { const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); return createRequest( children, @@ -95,9 +98,6 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) { createRenderState( resumableState, options ? options.nonce : undefined, - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, options ? options.onHeaders : undefined, diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js index 52e0b120c7200..7b5200d6183d7 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js @@ -94,6 +94,9 @@ function prerender( const resources = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createPrerenderRequest( children, @@ -101,9 +104,6 @@ function prerender( createRenderState( resources, undefined, // nonce is not compatible with prerendered bootstrap scripts - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, onHeadersImpl, diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js b/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js index 0cef058935bb9..3cac30b1d9ac0 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js @@ -93,6 +93,9 @@ function prerender( const resources = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createPrerenderRequest( children, @@ -100,9 +103,6 @@ function prerender( createRenderState( resources, undefined, // nonce is not compatible with prerendered bootstrap scripts - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, onHeadersImpl, diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js index 604dbdccc4fc6..938749cb6afe0 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js @@ -94,6 +94,9 @@ function prerenderToNodeStream( const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createPrerenderRequest( children, @@ -101,9 +104,6 @@ function prerenderToNodeStream( createRenderState( resumableState, undefined, // nonce is not compatible with prerendered bootstrap scripts - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, options ? options.importMap : undefined, options ? options.onHeaders : undefined, diff --git a/packages/react-server-dom-fb/src/ReactDOMServerFB.js b/packages/react-server-dom-fb/src/ReactDOMServerFB.js index 8e4fcbfb04a48..b610a00566caa 100644 --- a/packages/react-server-dom-fb/src/ReactDOMServerFB.js +++ b/packages/react-server-dom-fb/src/ReactDOMServerFB.js @@ -53,6 +53,9 @@ function renderToStream(children: ReactNodeList, options: Options): Stream { const resumableState = createResumableState( options ? options.identifierPrefix : undefined, options ? options.unstable_externalRuntimeSrc : undefined, + options ? options.bootstrapScriptContent : undefined, + options ? options.bootstrapScripts : undefined, + options ? options.bootstrapModules : undefined, ); const request = createRequest( children, @@ -60,9 +63,6 @@ function renderToStream(children: ReactNodeList, options: Options): Stream { createRenderState( resumableState, undefined, - options ? options.bootstrapScriptContent : undefined, - options ? options.bootstrapScripts : undefined, - options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, ), createRootFormatContext(undefined), diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index 80cfb68c760a3..4c458afd0e2b4 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -3968,7 +3968,11 @@ function flushCompletedQueues( flushSegment(request, destination, completedRootSegment); request.completedRootSegment = null; - writeCompletedRoot(destination, request.renderState); + writeCompletedRoot( + destination, + request.renderState, + request.resumableState, + ); } else { // We haven't flushed the root yet so we don't need to check any other branches further down return;