diff --git a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js index a7f1a20cc1f15..2dfecabfe0f93 100644 --- a/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js +++ b/packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js @@ -2421,40 +2421,65 @@ export function writeCompletedSegmentInstruction( responseState: ResponseState, contentSegmentID: number, ): boolean { - const scriptFormat = + if ( !enableFizzExternalRuntime || - responseState.streamingFormat === ScriptStreamingFormat; - if (scriptFormat) { - writeChunk(destination, responseState.startInlineScript); - if (!responseState.sentCompleteSegmentFunction) { - // The first time we write this, we'll need to include the full implementation. - responseState.sentCompleteSegmentFunction = true; - writeChunk(destination, completeSegmentScript1Full); - } else { - // Future calls can just reuse the same function. - writeChunk(destination, completeSegmentScript1Partial); - } + responseState.streamingFormat === ScriptStreamingFormat + ) { + return writeCompletedSegmentInstructionScript( + destination, + responseState, + contentSegmentID, + ); + } else { + return writeCompletedSegmentInstructionData( + destination, + responseState, + contentSegmentID, + ); + } +} +function writeCompletedSegmentInstructionScript( + destination: Destination, + responseState: ResponseState, + contentSegmentID: number, +): boolean { + writeChunk(destination, responseState.startInlineScript); + if (!responseState.sentCompleteSegmentFunction) { + // The first time we write this, we'll need to include the full implementation. + responseState.sentCompleteSegmentFunction = true; + writeChunk(destination, completeSegmentScript1Full); } else { - writeChunk(destination, completeSegmentData1); + // Future calls can just reuse the same function. + writeChunk(destination, completeSegmentScript1Partial); } // Write function arguments, which are string literals writeChunk(destination, responseState.segmentPrefix); const formattedID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, formattedID); - if (scriptFormat) { - writeChunk(destination, completeSegmentScript2); - } else { - writeChunk(destination, completeSegmentData2); - } + writeChunk(destination, completeSegmentScript2); + writeChunk(destination, responseState.placeholderPrefix); writeChunk(destination, formattedID); + return writeChunkAndReturn(destination, completeSegmentScriptEnd); +} - if (scriptFormat) { - return writeChunkAndReturn(destination, completeSegmentScriptEnd); - } else { - return writeChunkAndReturn(destination, completeSegmentDataEnd); - } +function writeCompletedSegmentInstructionData( + destination: Destination, + responseState: ResponseState, + contentSegmentID: number, +): boolean { + writeChunk(destination, completeSegmentData1); + + // Write function arguments, which are string literals + writeChunk(destination, responseState.segmentPrefix); + const formattedID = stringToChunk(contentSegmentID.toString(16)); + writeChunk(destination, formattedID); + writeChunk(destination, completeSegmentData2); + + writeChunk(destination, responseState.placeholderPrefix); + writeChunk(destination, formattedID); + return writeChunkAndReturn(destination, completeSegmentDataEnd); } const completeBoundaryScript1Full = stringToPrecomputedChunk( @@ -2492,43 +2517,62 @@ export function writeCompletedBoundaryInstruction( boundaryID: SuspenseBoundaryID, contentSegmentID: number, boundaryResources: BoundaryResources, +): boolean { + if ( + !enableFizzExternalRuntime || + responseState.streamingFormat === ScriptStreamingFormat + ) { + return writeCompletedBoundaryInstructionScript( + destination, + responseState, + boundaryID, + contentSegmentID, + boundaryResources, + ); + } else { + return writeCompletedBoundaryInstructionData( + destination, + responseState, + boundaryID, + contentSegmentID, + boundaryResources, + ); + } +} + +function writeCompletedBoundaryInstructionScript( + destination: Destination, + responseState: ResponseState, + boundaryID: SuspenseBoundaryID, + contentSegmentID: number, + boundaryResources: BoundaryResources, ): boolean { let hasStyleDependencies; if (enableFloat) { hasStyleDependencies = hasStyleResourceDependencies(boundaryResources); } - const scriptFormat = - !enableFizzExternalRuntime || - responseState.streamingFormat === ScriptStreamingFormat; - if (scriptFormat) { - writeChunk(destination, responseState.startInlineScript); - if (enableFloat && hasStyleDependencies) { - if (!responseState.sentCompleteBoundaryFunction) { - responseState.sentCompleteBoundaryFunction = true; - responseState.sentStyleInsertionFunction = true; - writeChunk( - destination, - clonePrecomputedChunk(completeBoundaryWithStylesScript1FullBoth), - ); - } else if (!responseState.sentStyleInsertionFunction) { - responseState.sentStyleInsertionFunction = true; - writeChunk(destination, completeBoundaryWithStylesScript1FullPartial); - } else { - writeChunk(destination, completeBoundaryWithStylesScript1Partial); - } + + writeChunk(destination, responseState.startInlineScript); + if (enableFloat && hasStyleDependencies) { + if (!responseState.sentCompleteBoundaryFunction) { + responseState.sentCompleteBoundaryFunction = true; + responseState.sentStyleInsertionFunction = true; + writeChunk( + destination, + clonePrecomputedChunk(completeBoundaryWithStylesScript1FullBoth), + ); + } else if (!responseState.sentStyleInsertionFunction) { + responseState.sentStyleInsertionFunction = true; + writeChunk(destination, completeBoundaryWithStylesScript1FullPartial); } else { - if (!responseState.sentCompleteBoundaryFunction) { - responseState.sentCompleteBoundaryFunction = true; - writeChunk(destination, completeBoundaryScript1Full); - } else { - writeChunk(destination, completeBoundaryScript1Partial); - } + writeChunk(destination, completeBoundaryWithStylesScript1Partial); } } else { - if (enableFloat && hasStyleDependencies) { - writeChunk(destination, completeBoundaryWithStylesData1); + if (!responseState.sentCompleteBoundaryFunction) { + responseState.sentCompleteBoundaryFunction = true; + writeChunk(destination, completeBoundaryScript1Full); } else { - writeChunk(destination, completeBoundaryData1); + writeChunk(destination, completeBoundaryScript1Partial); } } @@ -2541,37 +2585,61 @@ export function writeCompletedBoundaryInstruction( // Write function arguments, which are string and array literals const formattedContentID = stringToChunk(contentSegmentID.toString(16)); writeChunk(destination, boundaryID); - if (scriptFormat) { - writeChunk(destination, completeBoundaryScript2); - } else { - writeChunk(destination, completeBoundaryData2); - } + writeChunk(destination, completeBoundaryScript2); + writeChunk(destination, responseState.segmentPrefix); writeChunk(destination, formattedContentID); + if (enableFloat && hasStyleDependencies) { - // Script and data writers must format this differently: // - script writer emits an array literal, whose string elements are // escaped for javascript e.g. ["A", "B"] - // - data writer emits a string literal, which is escaped as html - // e.g. ["A", "B"] - if (scriptFormat) { - writeChunk(destination, completeBoundaryScript3a); - // boundaryResources encodes an array literal - writeStyleResourceDependenciesInJS(destination, boundaryResources); - } else { - writeChunk(destination, completeBoundaryData3a); - writeStyleResourceDependenciesInAttr(destination, boundaryResources); - } + writeChunk(destination, completeBoundaryScript3a); + // boundaryResources encodes an array literal + writeStyleResourceDependenciesInJS(destination, boundaryResources); } else { - if (scriptFormat) { - writeChunk(destination, completeBoundaryScript3b); - } + writeChunk(destination, completeBoundaryScript3b); } - if (scriptFormat) { - return writeChunkAndReturn(destination, completeBoundaryScriptEnd); + return writeChunkAndReturn(destination, completeBoundaryScriptEnd); +} + +function writeCompletedBoundaryInstructionData( + destination: Destination, + responseState: ResponseState, + boundaryID: SuspenseBoundaryID, + contentSegmentID: number, + boundaryResources: BoundaryResources, +): boolean { + let hasStyleDependencies; + if (enableFloat) { + hasStyleDependencies = hasStyleResourceDependencies(boundaryResources); + } + if (enableFloat && hasStyleDependencies) { + writeChunk(destination, completeBoundaryWithStylesData1); } else { - return writeChunkAndReturn(destination, completeBoundaryDataEnd); + writeChunk(destination, completeBoundaryData1); + } + + if (boundaryID === null) { + throw new Error( + 'An ID must have been assigned before we can complete the boundary.', + ); + } + + // Write function arguments, which are string and array literals + const formattedContentID = stringToChunk(contentSegmentID.toString(16)); + writeChunk(destination, boundaryID); + writeChunk(destination, completeBoundaryData2); + + writeChunk(destination, responseState.segmentPrefix); + writeChunk(destination, formattedContentID); + + if (enableFloat && hasStyleDependencies) { + // - data writer emits a string literal, which is escaped as html + // e.g. ["A", "B"] + writeChunk(destination, completeBoundaryData3a); + writeStyleResourceDependenciesInAttr(destination, boundaryResources); } + return writeChunkAndReturn(destination, completeBoundaryDataEnd); } const clientRenderScript1Full = stringToPrecomputedChunk( @@ -2598,22 +2666,46 @@ export function writeClientRenderBoundaryInstruction( errorMessage?: string, errorComponentStack?: string, ): boolean { - const scriptFormat = + if ( !enableFizzExternalRuntime || - responseState.streamingFormat === ScriptStreamingFormat; - if (scriptFormat) { - writeChunk(destination, responseState.startInlineScript); - if (!responseState.sentClientRenderFunction) { - // The first time we write this, we'll need to include the full implementation. - responseState.sentClientRenderFunction = true; - writeChunk(destination, clientRenderScript1Full); - } else { - // Future calls can just reuse the same function. - writeChunk(destination, clientRenderScript1Partial); - } + responseState.streamingFormat === ScriptStreamingFormat + ) { + return writeClientRenderBoundaryInstructionScript( + destination, + responseState, + boundaryID, + errorDigest, + errorMessage, + errorComponentStack, + ); } else { - // - return writeChunkAndReturn(destination, clientRenderDataEnd); +function writeClientRenderBoundaryInstructionData( + destination: Destination, + responseState: ResponseState, + boundaryID: SuspenseBoundaryID, + errorDigest: ?string, + errorMessage?: string, + errorComponentStack?: string, +): boolean { + // + return writeChunkAndReturn(destination, clientRenderDataEnd); } const regexForJSStringsInInstructionScripts = /[<\u2028\u2029]/g; diff --git a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js index 79455a7225df8..38705e7d843ae 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFizzServer-test.js @@ -10,7 +10,6 @@ 'use strict'; import { replaceScriptsAndMove, - mergeOptions, stripExternalRuntimeInNodes, withLoadingReadyState, } from '../test-utils/FizzTestUtils'; @@ -300,10 +299,10 @@ describe('ReactDOMFizzServer', () => { } function renderToPipeableStream(jsx, options) { // Merge options with renderOptions, which may contain featureFlag specific behavior - return ReactDOMFizzServer.renderToPipeableStream( - jsx, - mergeOptions(options, renderOptions), - ); + return ReactDOMFizzServer.renderToPipeableStream(jsx, { + ...renderOptions, + ...options, + }); } it('should asynchronously load a lazy component', async () => { diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index b216700671cca..74e2417f998a6 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -10,7 +10,6 @@ 'use strict'; import { replaceScriptsAndMove, - mergeOptions, withLoadingReadyState, } from '../test-utils/FizzTestUtils'; @@ -251,10 +250,10 @@ describe('ReactDOMFloat', () => { function renderToPipeableStream(jsx, options) { // Merge options with renderOptions, which may contain featureFlag specific behavior - return ReactDOMFizzServer.renderToPipeableStream( - jsx, - mergeOptions(options, renderOptions), - ); + return ReactDOMFizzServer.renderToPipeableStream(jsx, { + ...renderOptions, + ...options, + }); } // @gate enableFloat diff --git a/packages/react-dom/src/test-utils/FizzTestUtils.js b/packages/react-dom/src/test-utils/FizzTestUtils.js index 1df5ca947e7c0..c27cb396fdb66 100644 --- a/packages/react-dom/src/test-utils/FizzTestUtils.js +++ b/packages/react-dom/src/test-utils/FizzTestUtils.js @@ -133,13 +133,6 @@ async function replaceScriptsAndMove( } } -function mergeOptions(options: Object, defaultOptions: Object): Object { - return { - ...defaultOptions, - ...options, - }; -} - function stripExternalRuntimeInNodes( nodes: HTMLElement[] | HTMLCollection, externalRuntimeSrc: string | null, @@ -192,7 +185,6 @@ async function withLoadingReadyState( export { replaceScriptsAndMove, - mergeOptions, stripExternalRuntimeInNodes, withLoadingReadyState, };