@samitouri / QOS-React-1 / commits / 7508dcd5cc

[Static][Fizz] Carry forward bootstrap config to resume if postponing in the shell (#27672)

Previously it was possible 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.

Josh Story committed Nov 8, 2023 at 10:43 UTC 7508dcd5cc245e376860d65402972e418199264d
11 files changed +51 -35
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+22 -8
@@ -245,6 +245,13 @@ export type ResumableState = {
245 nextFormID: number,
246 streamingFormat: StreamingFormat,
247
248 + // We carry the bootstrap intializers in resumable state in case we postpone in the shell
249 + // of a prerender. On resume we will reinitialize the bootstrap scripts if necessary.
250 + // If we end up flushing the bootstrap scripts we void these on the resumable state
251 + bootstrapScriptContent?: string | void,
252 + bootstrapScripts?: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
253 + bootstrapModules?: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
254 +
255 // state for script streaming format, unused if using external runtime / data
256 instructions: InstructionState,
257
@@ -349,9 +356,6 @@ const DEFAULT_HEADERS_CAPACITY_IN_UTF16_CODE_UNITS = 2000;
356 export function createRenderState(
357 resumableState: ResumableState,
358 nonce: string | void,
352 - bootstrapScriptContent: string | void,
353 - bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
354 - bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
359 externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
360 importMap: ImportMap | void,
361 onHeaders: void | ((headers: HeadersDescriptor) => void),
@@ -367,6 +371,8 @@ export function createRenderState(
371
372 const bootstrapChunks: Array<Chunk | PrecomputedChunk> = [];
373 let externalRuntimeScript: null | ExternalRuntimeScript = null;
374 + const {bootstrapScriptContent, bootstrapScripts, bootstrapModules} =
375 + resumableState;
376 if (bootstrapScriptContent !== undefined) {
377 bootstrapChunks.push(
378 inlineScriptWithNonce,
@@ -612,9 +618,6 @@ export function resumeRenderState(
618 return createRenderState(
619 resumableState,
620 nonce,
615 - // These should have already been flushed in the prerender.
616 - undefined,
617 - undefined,
621 undefined,
622 undefined,
623 undefined,
@@ -625,6 +628,9 @@ export function resumeRenderState(
628 export function createResumableState(
629 identifierPrefix: string | void,
630 externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
631 + bootstrapScriptContent: string | void,
632 + bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
633 + bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
634 ): ResumableState {
635 const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
636
@@ -638,6 +644,9 @@ export function createResumableState(
644 idPrefix: idPrefix,
645 nextFormID: 0,
646 streamingFormat,
647 + bootstrapScriptContent,
648 + bootstrapScripts,
649 + bootstrapModules,
650 instructions: NothingSent,
651 hasBody: false,
652 hasHtml: false,
@@ -3714,7 +3723,11 @@ export function pushEndInstance(
3723 function writeBootstrap(
3724 destination: Destination,
3725 renderState: RenderState,
3726 + resumableState: ResumableState,
3727 ): boolean {
3728 + resumableState.bootstrapScriptContent = undefined;
3729 + resumableState.bootstrapScripts = undefined;
3730 + resumableState.bootstrapModules = undefined;
3731 const bootstrapChunks = renderState.bootstrapChunks;
3732 let i = 0;
3733 for (; i < bootstrapChunks.length - 1; i++) {
@@ -3731,8 +3744,9 @@ function writeBootstrap(
3744 export function writeCompletedRoot(
3745 destination: Destination,
3746 renderState: RenderState,
3747 + resumableState: ResumableState,
3748 ): boolean {
3735 - return writeBootstrap(destination, renderState);
3749 + return writeBootstrap(destination, renderState, resumableState);
3750 }
3751
3752 // Structural Nodes
@@ -4197,7 +4211,7 @@ export function writeCompletedBoundaryInstruction(
4211 } else {
4212 writeMore = writeChunkAndReturn(destination, completeBoundaryDataEnd);
4213 }
4200 - return writeBootstrap(destination, renderState) && writeMore;
4214 + return writeBootstrap(destination, renderState, resumableState) && writeMore;
4215 }
4216
4217 const clientRenderScript1Full = stringToPrecomputedChunk(
packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js
-2
@@ -92,8 +92,6 @@ export function createRenderState(
92 undefined,
93 undefined,
94 undefined,
95 - undefined,
96 - undefined,
95 );
96 return {
97 // Keep this in sync with ReactFizzConfigDOM
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+3 -3
@@ -114,6 +114,9 @@ function renderToReadableStream(
114 const resumableState = createResumableState(
115 options ? options.identifierPrefix : undefined,
116 options ? options.unstable_externalRuntimeSrc : undefined,
117 + options ? options.bootstrapScriptContent : undefined,
118 + options ? options.bootstrapScripts : undefined,
119 + options ? options.bootstrapModules : undefined,
120 );
121 const request = createRequest(
122 children,
@@ -121,9 +124,6 @@ function renderToReadableStream(
124 createRenderState(
125 resumableState,
126 options ? options.nonce : undefined,
124 - options ? options.bootstrapScriptContent : undefined,
125 - options ? options.bootstrapScripts : undefined,
126 - options ? options.bootstrapModules : undefined,
127 options ? options.unstable_externalRuntimeSrc : undefined,
128 options ? options.importMap : undefined,
129 onHeadersImpl,
packages/react-dom/src/server/ReactDOMFizzServerBun.js
+3 -3
@@ -104,6 +104,9 @@ function renderToReadableStream(
104 const resumableState = createResumableState(
105 options ? options.identifierPrefix : undefined,
106 options ? options.unstable_externalRuntimeSrc : undefined,
107 + options ? options.bootstrapScriptContent : undefined,
108 + options ? options.bootstrapScripts : undefined,
109 + options ? options.bootstrapModules : undefined,
110 );
111 const request = createRequest(
112 children,
@@ -111,9 +114,6 @@ function renderToReadableStream(
114 createRenderState(
115 resumableState,
116 options ? options.nonce : undefined,
114 - options ? options.bootstrapScriptContent : undefined,
115 - options ? options.bootstrapScripts : undefined,
116 - options ? options.bootstrapModules : undefined,
117 options ? options.unstable_externalRuntimeSrc : undefined,
118 options ? options.importMap : undefined,
119 onHeadersImpl,
packages/react-dom/src/server/ReactDOMFizzServerEdge.js
+3 -3
@@ -114,6 +114,9 @@ function renderToReadableStream(
114 const resumableState = createResumableState(
115 options ? options.identifierPrefix : undefined,
116 options ? options.unstable_externalRuntimeSrc : undefined,
117 + options ? options.bootstrapScriptContent : undefined,
118 + options ? options.bootstrapScripts : undefined,
119 + options ? options.bootstrapModules : undefined,
120 );
121 const request = createRequest(
122 children,
@@ -121,9 +124,6 @@ function renderToReadableStream(
124 createRenderState(
125 resumableState,
126 options ? options.nonce : undefined,
124 - options ? options.bootstrapScriptContent : undefined,
125 - options ? options.bootstrapScripts : undefined,
126 - options ? options.bootstrapModules : undefined,
127 options ? options.unstable_externalRuntimeSrc : undefined,
128 options ? options.importMap : undefined,
129 onHeadersImpl,
packages/react-dom/src/server/ReactDOMFizzServerNode.js
+3 -3
@@ -88,6 +88,9 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
88 const resumableState = createResumableState(
89 options ? options.identifierPrefix : undefined,
90 options ? options.unstable_externalRuntimeSrc : undefined,
91 + options ? options.bootstrapScriptContent : undefined,
92 + options ? options.bootstrapScripts : undefined,
93 + options ? options.bootstrapModules : undefined,
94 );
95 return createRequest(
96 children,
@@ -95,9 +98,6 @@ function createRequestImpl(children: ReactNodeList, options: void | Options) {
98 createRenderState(
99 resumableState,
100 options ? options.nonce : undefined,
98 - options ? options.bootstrapScriptContent : undefined,
99 - options ? options.bootstrapScripts : undefined,
100 - options ? options.bootstrapModules : undefined,
101 options ? options.unstable_externalRuntimeSrc : undefined,
102 options ? options.importMap : undefined,
103 options ? options.onHeaders : undefined,
packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+3 -3
@@ -94,6 +94,9 @@ function prerender(
94 const resources = createResumableState(
95 options ? options.identifierPrefix : undefined,
96 options ? options.unstable_externalRuntimeSrc : undefined,
97 + options ? options.bootstrapScriptContent : undefined,
98 + options ? options.bootstrapScripts : undefined,
99 + options ? options.bootstrapModules : undefined,
100 );
101 const request = createPrerenderRequest(
102 children,
@@ -101,9 +104,6 @@ function prerender(
104 createRenderState(
105 resources,
106 undefined, // nonce is not compatible with prerendered bootstrap scripts
104 - options ? options.bootstrapScriptContent : undefined,
105 - options ? options.bootstrapScripts : undefined,
106 - options ? options.bootstrapModules : undefined,
107 options ? options.unstable_externalRuntimeSrc : undefined,
108 options ? options.importMap : undefined,
109 onHeadersImpl,
packages/react-dom/src/server/ReactDOMFizzStaticEdge.js
+3 -3
@@ -93,6 +93,9 @@ function prerender(
93 const resources = createResumableState(
94 options ? options.identifierPrefix : undefined,
95 options ? options.unstable_externalRuntimeSrc : undefined,
96 + options ? options.bootstrapScriptContent : undefined,
97 + options ? options.bootstrapScripts : undefined,
98 + options ? options.bootstrapModules : undefined,
99 );
100 const request = createPrerenderRequest(
101 children,
@@ -100,9 +103,6 @@ function prerender(
103 createRenderState(
104 resources,
105 undefined, // nonce is not compatible with prerendered bootstrap scripts
103 - options ? options.bootstrapScriptContent : undefined,
104 - options ? options.bootstrapScripts : undefined,
105 - options ? options.bootstrapModules : undefined,
106 options ? options.unstable_externalRuntimeSrc : undefined,
107 options ? options.importMap : undefined,
108 onHeadersImpl,
packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+3 -3
@@ -94,6 +94,9 @@ function prerenderToNodeStream(
94 const resumableState = createResumableState(
95 options ? options.identifierPrefix : undefined,
96 options ? options.unstable_externalRuntimeSrc : undefined,
97 + options ? options.bootstrapScriptContent : undefined,
98 + options ? options.bootstrapScripts : undefined,
99 + options ? options.bootstrapModules : undefined,
100 );
101 const request = createPrerenderRequest(
102 children,
@@ -101,9 +104,6 @@ function prerenderToNodeStream(
104 createRenderState(
105 resumableState,
106 undefined, // nonce is not compatible with prerendered bootstrap scripts
104 - options ? options.bootstrapScriptContent : undefined,
105 - options ? options.bootstrapScripts : undefined,
106 - options ? options.bootstrapModules : undefined,
107 options ? options.unstable_externalRuntimeSrc : undefined,
108 options ? options.importMap : undefined,
109 options ? options.onHeaders : undefined,
packages/react-server-dom-fb/src/ReactDOMServerFB.js
+3 -3
@@ -53,6 +53,9 @@ function renderToStream(children: ReactNodeList, options: Options): Stream {
53 const resumableState = createResumableState(
54 options ? options.identifierPrefix : undefined,
55 options ? options.unstable_externalRuntimeSrc : undefined,
56 + options ? options.bootstrapScriptContent : undefined,
57 + options ? options.bootstrapScripts : undefined,
58 + options ? options.bootstrapModules : undefined,
59 );
60 const request = createRequest(
61 children,
@@ -60,9 +63,6 @@ function renderToStream(children: ReactNodeList, options: Options): Stream {
63 createRenderState(
64 resumableState,
65 undefined,
63 - options ? options.bootstrapScriptContent : undefined,
64 - options ? options.bootstrapScripts : undefined,
65 - options ? options.bootstrapModules : undefined,
66 options ? options.unstable_externalRuntimeSrc : undefined,
67 ),
68 createRootFormatContext(undefined),
packages/react-server/src/ReactFizzServer.js
+5 -1
@@ -3968,7 +3968,11 @@ function flushCompletedQueues(
3968
3969 flushSegment(request, destination, completedRootSegment);
3970 request.completedRootSegment = null;
3971 - writeCompletedRoot(destination, request.renderState);
3971 + writeCompletedRoot(
3972 + destination,
3973 + request.renderState,
3974 + request.resumableState,
3975 + );
3976 } else {
3977 // We haven't flushed the root yet so we don't need to check any other branches further down
3978 return;