@@ -106,26 +106,33 @@ export const isPrimaryRenderer = true;
106106// Per response, global state that is not contextual to the rendering subtree.
107107export type ResponseState = {
108108 bootstrapChunks : Array < Chunk | PrecomputedChunk > ,
109- startInlineScript : PrecomputedChunk ,
110109 placeholderPrefix : PrecomputedChunk ,
111110 segmentPrefix : PrecomputedChunk ,
112111 boundaryPrefix : string ,
113112 idPrefix : string ,
114113 nextSuspenseID : number ,
114+ streamingFormat : 'SCRIPT' | 'DATA' ,
115+ // state for script streaming format, unused if using external runtime / data
116+ startInlineScript : PrecomputedChunk ,
115117 sentCompleteSegmentFunction : boolean ,
116118 sentCompleteBoundaryFunction : boolean ,
117119 sentClientRenderFunction : boolean ,
118- sentStyleInsertionFunction : boolean , // We allow the legacy renderer to extend this object.
120+ sentStyleInsertionFunction : boolean ,
121+ // We allow the legacy renderer to extend this object.
119122 ...
120123} ;
121124
125+ const dataElementQuotedEnd = stringToPrecomputedChunk ( '"></div>' ) ;
126+ const dataElementUnquotedEnd = stringToPrecomputedChunk ( '"></div>' ) ;
127+
122128const startInlineScript = stringToPrecomputedChunk ( '<script>' ) ;
123129const endInlineScript = stringToPrecomputedChunk ( '</script>' ) ;
124130
125131const startScriptSrc = stringToPrecomputedChunk ( '<script src="' ) ;
126132const startModuleSrc = stringToPrecomputedChunk ( '<script type="module" src="' ) ;
127133const scriptIntegirty = stringToPrecomputedChunk ( '" integrity="' ) ;
128134const endAsyncScript = stringToPrecomputedChunk ( '" async=""></script>' ) ;
135+ // const endAsyncScript = stringToPrecomputedChunk('"></script>');
129136
130137/**
131138 * This escaping function is designed to work with bootstrapScriptContent only.
@@ -152,6 +159,8 @@ export type BootstrapScriptDescriptor = {
152159 integrity ?: string ,
153160} ;
154161// Allows us to keep track of what we've already written so we can refer back to it.
162+ // if passed externalRuntimeConfig and the enableFizzExternalRuntime feature flag
163+ // is set, the server will send instructions via data attributes (instead of inline scripts)
155164export function createResponseState (
156165 identifierPrefix : string | void ,
157166 nonce : string | void ,
@@ -168,6 +177,7 @@ export function createResponseState(
168177 '<script nonce="' + escapeTextForBrowser ( nonce ) + '">' ,
169178 ) ;
170179 const bootstrapChunks = [ ] ;
180+ let streamingFormat = 'SCRIPT' ;
171181 if ( bootstrapScriptContent !== undefined ) {
172182 bootstrapChunks . push (
173183 inlineScriptWithNonce ,
@@ -177,6 +187,7 @@ export function createResponseState(
177187 }
178188 if ( enableFizzExternalRuntime ) {
179189 if ( externalRuntimeConfig !== undefined ) {
190+ streamingFormat = 'DATA ';
180191 const src =
181192 typeof externalRuntimeConfig === 'string'
182193 ? externalRuntimeConfig
@@ -240,12 +251,13 @@ export function createResponseState(
240251 }
241252 return {
242253 bootstrapChunks : bootstrapChunks ,
243- startInlineScript : inlineScriptWithNonce ,
244254 placeholderPrefix : stringToPrecomputedChunk ( idPrefix + 'P:' ) ,
245255 segmentPrefix : stringToPrecomputedChunk ( idPrefix + 'S:' ) ,
246256 boundaryPrefix : idPrefix + 'B:' ,
247257 idPrefix : idPrefix ,
248258 nextSuspenseID : 0 ,
259+ streamingFormat ,
260+ startInlineScript : inlineScriptWithNonce ,
249261 sentCompleteSegmentFunction : false ,
250262 sentCompleteBoundaryFunction : false ,
251263 sentClientRenderFunction : false ,
@@ -2081,29 +2093,54 @@ const completeSegmentScript1Full = stringToPrecomputedChunk(
20812093) ;
20822094const completeSegmentScript1Partial = stringToPrecomputedChunk ( '$RS("' ) ;
20832095const completeSegmentScript2 = stringToPrecomputedChunk ( '","' ) ;
2084- const completeSegmentScript3 = stringToPrecomputedChunk ( '")</script>' ) ;
2096+ const completeSegmentScriptEnd = stringToPrecomputedChunk ( '")</script>' ) ;
2097+
2098+ const completeSegmentData1 = stringToPrecomputedChunk (
2099+ '<div hidden data-react-server-instruction="$RS" data-react-server-arg0="' ,
2100+ ) ;
2101+ const completeSegmentData2 = stringToPrecomputedChunk (
2102+ '" data-react-server-arg1="' ,
2103+ ) ;
2104+ const completeSegmentDataEnd = dataElementQuotedEnd ;
20852105
20862106export function writeCompletedSegmentInstruction (
20872107 destination : Destination ,
20882108 responseState : ResponseState ,
20892109 contentSegmentID : number ,
20902110) : boolean {
2091- writeChunk ( destination , responseState . startInlineScript ) ;
2092- if ( ! responseState . sentCompleteSegmentFunction ) {
2093- // The first time we write this, we'll need to include the full implementation.
2094- responseState . sentCompleteSegmentFunction = true ;
2095- writeChunk ( destination , completeSegmentScript1Full ) ;
2111+ const scriptFormat =
2112+ ! enableFizzExternalRuntime || responseState . streamingFormat === 'SCRIPT' ;
2113+ if ( scriptFormat ) {
2114+ writeChunk ( destination , responseState . startInlineScript ) ;
2115+ if ( ! responseState . sentCompleteSegmentFunction ) {
2116+ // The first time we write this, we'll need to include the full implementation.
2117+ responseState . sentCompleteSegmentFunction = true ;
2118+ writeChunk ( destination , completeSegmentScript1Full ) ;
2119+ } else {
2120+ // Future calls can just reuse the same function.
2121+ writeChunk ( destination , completeSegmentScript1Partial ) ;
2122+ }
20962123 } else {
2097- // Future calls can just reuse the same function.
2098- writeChunk ( destination , completeSegmentScript1Partial ) ;
2124+ writeChunk ( destination , completeSegmentData1 ) ;
20992125 }
2126+
2127+ // Write function arguments, which are string literals
21002128 writeChunk ( destination , responseState . segmentPrefix ) ;
21012129 const formattedID = stringToChunk ( contentSegmentID . toString ( 16 ) ) ;
21022130 writeChunk ( destination , formattedID ) ;
2103- writeChunk ( destination , completeSegmentScript2 ) ;
2131+ if ( scriptFormat ) {
2132+ writeChunk ( destination , completeSegmentScript2 ) ;
2133+ } else {
2134+ writeChunk ( destination , completeSegmentData2 ) ;
2135+ }
21042136 writeChunk ( destination , responseState . placeholderPrefix ) ;
21052137 writeChunk ( destination , formattedID ) ;
2106- return writeChunkAndReturn ( destination , completeSegmentScript3 ) ;
2138+
2139+ if ( scriptFormat ) {
2140+ return writeChunkAndReturn ( destination , completeSegmentScriptEnd ) ;
2141+ } else {
2142+ return writeChunkAndReturn ( destination , completeSegmentDataEnd ) ;
2143+ }
21072144}
21082145
21092146const completeBoundaryScript1Full = stringToPrecomputedChunk (
@@ -2121,9 +2158,25 @@ const completeBoundaryWithStylesScript1Partial = stringToPrecomputedChunk(
21212158 '$RR("' ,
21222159) ;
21232160const completeBoundaryScript2 = stringToPrecomputedChunk ( '","' ) ;
2124- const completeBoundaryScript2a = stringToPrecomputedChunk ( '",' ) ;
2125- const completeBoundaryScript3 = stringToPrecomputedChunk ( '"' ) ;
2126- const completeBoundaryScript4 = stringToPrecomputedChunk ( ')</script>' ) ;
2161+ const completeBoundaryScript3a = stringToPrecomputedChunk ( '",' ) ;
2162+ const completeBoundaryScript3b = stringToPrecomputedChunk ( '"' ) ;
2163+ const completeBoundaryScriptEnd = stringToPrecomputedChunk ( ')</script>' ) ;
2164+
2165+ const completeBoundaryData1 = stringToPrecomputedChunk (
2166+ '<div hidden data-react-server-instruction="$RC" data-react-server-arg0="' ,
2167+ ) ;
2168+ const completeBoundaryWithStylesData1 = stringToPrecomputedChunk (
2169+ '<div hidden data-react-server-instruction="$RR" data-react-server-arg0="' ,
2170+ ) ;
2171+ const completeBoundaryData2 = stringToPrecomputedChunk (
2172+ '" data-react-server-arg1="' ,
2173+ ) ;
2174+ const completeBoundaryData3aStart = stringToPrecomputedChunk (
2175+ '" data-react-server-arg2=\'' ,
2176+ ) ;
2177+ const completeBoundaryData3aEnd = stringToPrecomputedChunk ( "'" ) ;
2178+ const completeBoundaryData3b = completeBoundaryScript3b ;
2179+ const completeBoundaryDataEnd = dataElementUnquotedEnd ;
21272180
21282181export function writeCompletedBoundaryInstruction (
21292182 destination : Destination ,
@@ -2136,24 +2189,34 @@ export function writeCompletedBoundaryInstruction(
21362189 if ( enableFloat ) {
21372190 hasStyleDependencies = hasStyleResourceDependencies ( boundaryResources ) ;
21382191 }
2139- writeChunk ( destination , responseState . startInlineScript ) ;
2140- if ( enableFloat && hasStyleDependencies ) {
2141- if ( ! responseState . sentCompleteBoundaryFunction ) {
2142- responseState . sentCompleteBoundaryFunction = true ;
2143- responseState . sentStyleInsertionFunction = true ;
2144- writeChunk ( destination , completeBoundaryWithStylesScript1FullBoth ) ;
2145- } else if ( ! responseState . sentStyleInsertionFunction ) {
2146- responseState . sentStyleInsertionFunction = true ;
2147- writeChunk ( destination , completeBoundaryWithStylesScript1FullPartial ) ;
2192+ const scriptFormat =
2193+ ! enableFizzExternalRuntime || responseState . streamingFormat === 'SCRIPT' ;
2194+ if ( scriptFormat ) {
2195+ writeChunk ( destination , responseState . startInlineScript ) ;
2196+ if ( enableFloat && hasStyleDependencies ) {
2197+ if ( ! responseState . sentCompleteBoundaryFunction ) {
2198+ responseState . sentCompleteBoundaryFunction = true ;
2199+ responseState . sentStyleInsertionFunction = true ;
2200+ writeChunk ( destination , completeBoundaryWithStylesScript1FullBoth ) ;
2201+ } else if ( ! responseState . sentStyleInsertionFunction ) {
2202+ responseState . sentStyleInsertionFunction = true ;
2203+ writeChunk ( destination , completeBoundaryWithStylesScript1FullPartial ) ;
2204+ } else {
2205+ writeChunk ( destination , completeBoundaryWithStylesScript1Partial ) ;
2206+ }
21482207 } else {
2149- writeChunk ( destination , completeBoundaryWithStylesScript1Partial ) ;
2208+ if ( ! responseState . sentCompleteBoundaryFunction ) {
2209+ responseState . sentCompleteBoundaryFunction = true ;
2210+ writeChunk ( destination , completeBoundaryScript1Full ) ;
2211+ } else {
2212+ writeChunk ( destination , completeBoundaryScript1Partial ) ;
2213+ }
21502214 }
21512215 } else {
2152- if ( ! responseState . sentCompleteBoundaryFunction ) {
2153- responseState . sentCompleteBoundaryFunction = true ;
2154- writeChunk ( destination , completeBoundaryScript1Full ) ;
2216+ if ( enableFloat && hasStyleDependencies ) {
2217+ writeChunk ( destination , completeBoundaryWithStylesData1 ) ;
21552218 } else {
2156- writeChunk ( destination , completeBoundaryScript1Partial ) ;
2219+ writeChunk ( destination , completeBoundaryData1 ) ;
21572220 }
21582221 }
21592222
@@ -2163,27 +2226,56 @@ export function writeCompletedBoundaryInstruction(
21632226 ) ;
21642227 }
21652228
2229+ // Write function arguments, which are string and array literals
21662230 const formattedContentID = stringToChunk ( contentSegmentID . toString ( 16 ) ) ;
21672231 writeChunk ( destination , boundaryID ) ;
2168- writeChunk ( destination , completeBoundaryScript2 ) ;
2232+ if ( scriptFormat ) {
2233+ writeChunk ( destination , completeBoundaryScript2 ) ;
2234+ } else {
2235+ writeChunk ( destination , completeBoundaryData2 ) ;
2236+ }
21692237 writeChunk ( destination , responseState . segmentPrefix ) ;
21702238 writeChunk ( destination , formattedContentID ) ;
21712239 if ( enableFloat && hasStyleDependencies ) {
2172- writeChunk ( destination , completeBoundaryScript2a ) ;
2240+ if ( scriptFormat ) {
2241+ writeChunk ( destination , completeBoundaryScript3a ) ;
2242+ } else {
2243+ writeChunk ( destination , completeBoundaryData3aStart ) ;
2244+ }
21732245 writeStyleResourceDependencies ( destination , boundaryResources ) ;
2246+ if ( ! scriptFormat ) {
2247+ writeChunk ( destination , completeBoundaryData3aEnd ) ;
2248+ }
21742249 } else {
2175- writeChunk ( destination , completeBoundaryScript3 ) ;
2250+ if ( scriptFormat ) {
2251+ writeChunk ( destination , completeBoundaryScript3b ) ;
2252+ } else {
2253+ writeChunk ( destination , completeBoundaryData3b ) ;
2254+ }
2255+ }
2256+ if ( scriptFormat ) {
2257+ return writeChunkAndReturn ( destination , completeBoundaryScriptEnd ) ;
2258+ } else {
2259+ return writeChunkAndReturn ( destination , completeBoundaryDataEnd ) ;
21762260 }
2177- return writeChunkAndReturn ( destination , completeBoundaryScript4 ) ;
21782261}
21792262
21802263const clientRenderScript1Full = stringToPrecomputedChunk (
21812264 clientRenderFunction + ';$RX("' ,
21822265) ;
21832266const clientRenderScript1Partial = stringToPrecomputedChunk ( '$RX("' ) ;
21842267const clientRenderScript1A = stringToPrecomputedChunk ( '"' ) ;
2185- const clientRenderScript2 = stringToPrecomputedChunk ( ')</script>' ) ;
21862268const clientRenderErrorScriptArgInterstitial = stringToPrecomputedChunk ( ',' ) ;
2269+ const clientRenderScriptEnd = stringToPrecomputedChunk ( ')</script>' ) ;
2270+
2271+ const clientRenderData1 = stringToPrecomputedChunk (
2272+ '<div hidden data-react-server-instruction="$RX" data-react-server-arg0="' ,
2273+ ) ;
2274+ const clientRenderData1A = clientRenderScript1A ;
2275+ const clientRenderData2 = stringToPrecomputedChunk ( ' data-react-server-arg1=' ) ;
2276+ const clientRenderData3 = stringToPrecomputedChunk ( ' data-react-server-arg2=' ) ;
2277+ const clientRenderData4 = stringToPrecomputedChunk ( ' data-react-server-arg3=' ) ;
2278+ const clientRenderDataEnd = dataElementUnquotedEnd ;
21872279
21882280export function writeClientRenderBoundaryInstruction (
21892281 destination : Destination ,
@@ -2193,14 +2285,20 @@ export function writeClientRenderBoundaryInstruction(
21932285 errorMessage ? : string ,
21942286 errorComponentStack ? : string ,
21952287) : boolean {
2196- writeChunk ( destination , responseState . startInlineScript ) ;
2197- if ( ! responseState . sentClientRenderFunction ) {
2198- // The first time we write this, we'll need to include the full implementation.
2199- responseState . sentClientRenderFunction = true ;
2200- writeChunk ( destination , clientRenderScript1Full ) ;
2288+ const scriptFormat =
2289+ ! enableFizzExternalRuntime || responseState . streamingFormat === 'SCRIPT' ;
2290+ if ( scriptFormat ) {
2291+ writeChunk ( destination , responseState . startInlineScript ) ;
2292+ if ( ! responseState . sentClientRenderFunction ) {
2293+ // The first time we write this, we'll need to include the full implementation.
2294+ responseState . sentClientRenderFunction = true ;
2295+ writeChunk ( destination , clientRenderScript1Full ) ;
2296+ } else {
2297+ // Future calls can just reuse the same function.
2298+ writeChunk ( destination , clientRenderScript1Partial ) ;
2299+ }
22012300 } else {
2202- // Future calls can just reuse the same function.
2203- writeChunk ( destination , clientRenderScript1Partial ) ;
2301+ writeChunk ( destination , clientRenderData1 ) ;
22042302 }
22052303
22062304 if ( boundaryID === null ) {
@@ -2210,29 +2308,51 @@ export function writeClientRenderBoundaryInstruction(
22102308 }
22112309
22122310 writeChunk ( destination , boundaryID ) ;
2213- writeChunk ( destination , clientRenderScript1A ) ;
2311+ if ( scriptFormat ) {
2312+ writeChunk ( destination , clientRenderScript1A ) ;
2313+ } else {
2314+ writeChunk ( destination , clientRenderData1A ) ;
2315+ }
2316+
22142317 if ( errorDigest || errorMessage || errorComponentStack ) {
2215- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2318+ if ( scriptFormat ) {
2319+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2320+ } else {
2321+ writeChunk ( destination , clientRenderData2 ) ;
2322+ }
22162323 writeChunk (
22172324 destination ,
22182325 stringToChunk ( escapeJSStringsForInstructionScripts ( errorDigest || '' ) ) ,
22192326 ) ;
22202327 }
22212328 if ( errorMessage || errorComponentStack ) {
2222- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2329+ if ( scriptFormat ) {
2330+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2331+ } else {
2332+ writeChunk ( destination , clientRenderData3 ) ;
2333+ }
22232334 writeChunk (
22242335 destination ,
22252336 stringToChunk ( escapeJSStringsForInstructionScripts ( errorMessage || '' ) ) ,
22262337 ) ;
22272338 }
22282339 if ( errorComponentStack ) {
2229- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2340+ if ( scriptFormat ) {
2341+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2342+ } else {
2343+ writeChunk ( destination , clientRenderData4 ) ;
2344+ }
22302345 writeChunk (
22312346 destination ,
22322347 stringToChunk ( escapeJSStringsForInstructionScripts ( errorComponentStack ) ) ,
22332348 ) ;
22342349 }
2235- return writeChunkAndReturn ( destination , clientRenderScript2 ) ;
2350+
2351+ if ( scriptFormat ) {
2352+ return writeChunkAndReturn ( destination , clientRenderScriptEnd ) ;
2353+ } else {
2354+ return writeChunkAndReturn ( destination , clientRenderDataEnd ) ;
2355+ }
22362356}
22372357
22382358const regexForJSStringsInInstructionScripts = / [ < \u2028\u2029 ] / g ;
0 commit comments