@@ -106,26 +106,33 @@ export const isPrimaryRenderer = true;
106
106
// Per response, global state that is not contextual to the rendering subtree.
107
107
export type ResponseState = {
108
108
bootstrapChunks : Array < Chunk | PrecomputedChunk > ,
109
- startInlineScript : PrecomputedChunk ,
110
109
placeholderPrefix : PrecomputedChunk ,
111
110
segmentPrefix : PrecomputedChunk ,
112
111
boundaryPrefix : string ,
113
112
idPrefix : string ,
114
113
nextSuspenseID : number ,
114
+ streamingFormat : 'SCRIPT' | 'DATA' ,
115
+ // state for script streaming format, unused if using external runtime / data
116
+ startInlineScript : PrecomputedChunk ,
115
117
sentCompleteSegmentFunction : boolean ,
116
118
sentCompleteBoundaryFunction : boolean ,
117
119
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.
119
122
...
120
123
} ;
121
124
125
+ const dataElementQuotedEnd = stringToPrecomputedChunk ( '"></div>' ) ;
126
+ const dataElementUnquotedEnd = stringToPrecomputedChunk ( '"></div>' ) ;
127
+
122
128
const startInlineScript = stringToPrecomputedChunk ( '<script>' ) ;
123
129
const endInlineScript = stringToPrecomputedChunk ( '</script>' ) ;
124
130
125
131
const startScriptSrc = stringToPrecomputedChunk ( '<script src="' ) ;
126
132
const startModuleSrc = stringToPrecomputedChunk ( '<script type="module" src="' ) ;
127
133
const scriptIntegirty = stringToPrecomputedChunk ( '" integrity="' ) ;
128
134
const endAsyncScript = stringToPrecomputedChunk ( '" async=""></script>' ) ;
135
+ // const endAsyncScript = stringToPrecomputedChunk('"></script>');
129
136
130
137
/**
131
138
* This escaping function is designed to work with bootstrapScriptContent only.
@@ -152,6 +159,8 @@ export type BootstrapScriptDescriptor = {
152
159
integrity ?: string ,
153
160
} ;
154
161
// 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)
155
164
export function createResponseState (
156
165
identifierPrefix : string | void ,
157
166
nonce : string | void ,
@@ -168,6 +177,7 @@ export function createResponseState(
168
177
'<script nonce="' + escapeTextForBrowser ( nonce ) + '">' ,
169
178
) ;
170
179
const bootstrapChunks = [ ] ;
180
+ let streamingFormat = 'SCRIPT' ;
171
181
if ( bootstrapScriptContent !== undefined ) {
172
182
bootstrapChunks . push (
173
183
inlineScriptWithNonce ,
@@ -177,6 +187,7 @@ export function createResponseState(
177
187
}
178
188
if ( enableFizzExternalRuntime ) {
179
189
if ( externalRuntimeConfig !== undefined ) {
190
+ streamingFormat = 'DATA ';
180
191
const src =
181
192
typeof externalRuntimeConfig === 'string'
182
193
? externalRuntimeConfig
@@ -240,12 +251,13 @@ export function createResponseState(
240
251
}
241
252
return {
242
253
bootstrapChunks : bootstrapChunks ,
243
- startInlineScript : inlineScriptWithNonce ,
244
254
placeholderPrefix : stringToPrecomputedChunk ( idPrefix + 'P:' ) ,
245
255
segmentPrefix : stringToPrecomputedChunk ( idPrefix + 'S:' ) ,
246
256
boundaryPrefix : idPrefix + 'B:' ,
247
257
idPrefix : idPrefix ,
248
258
nextSuspenseID : 0 ,
259
+ streamingFormat ,
260
+ startInlineScript : inlineScriptWithNonce ,
249
261
sentCompleteSegmentFunction : false ,
250
262
sentCompleteBoundaryFunction : false ,
251
263
sentClientRenderFunction : false ,
@@ -2081,29 +2093,54 @@ const completeSegmentScript1Full = stringToPrecomputedChunk(
2081
2093
) ;
2082
2094
const completeSegmentScript1Partial = stringToPrecomputedChunk ( '$RS("' ) ;
2083
2095
const 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 ;
2085
2105
2086
2106
export function writeCompletedSegmentInstruction (
2087
2107
destination : Destination ,
2088
2108
responseState : ResponseState ,
2089
2109
contentSegmentID : number ,
2090
2110
) : 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
+ }
2096
2123
} else {
2097
- // Future calls can just reuse the same function.
2098
- writeChunk ( destination , completeSegmentScript1Partial ) ;
2124
+ writeChunk ( destination , completeSegmentData1 ) ;
2099
2125
}
2126
+
2127
+ // Write function arguments, which are string literals
2100
2128
writeChunk ( destination , responseState . segmentPrefix ) ;
2101
2129
const formattedID = stringToChunk ( contentSegmentID . toString ( 16 ) ) ;
2102
2130
writeChunk ( destination , formattedID ) ;
2103
- writeChunk ( destination , completeSegmentScript2 ) ;
2131
+ if ( scriptFormat ) {
2132
+ writeChunk ( destination , completeSegmentScript2 ) ;
2133
+ } else {
2134
+ writeChunk ( destination , completeSegmentData2 ) ;
2135
+ }
2104
2136
writeChunk ( destination , responseState . placeholderPrefix ) ;
2105
2137
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
+ }
2107
2144
}
2108
2145
2109
2146
const completeBoundaryScript1Full = stringToPrecomputedChunk (
@@ -2121,9 +2158,25 @@ const completeBoundaryWithStylesScript1Partial = stringToPrecomputedChunk(
2121
2158
'$RR("' ,
2122
2159
) ;
2123
2160
const 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 ;
2127
2180
2128
2181
export function writeCompletedBoundaryInstruction (
2129
2182
destination : Destination ,
@@ -2136,24 +2189,34 @@ export function writeCompletedBoundaryInstruction(
2136
2189
if ( enableFloat ) {
2137
2190
hasStyleDependencies = hasStyleResourceDependencies ( boundaryResources ) ;
2138
2191
}
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
+ }
2148
2207
} else {
2149
- writeChunk ( destination , completeBoundaryWithStylesScript1Partial ) ;
2208
+ if ( ! responseState . sentCompleteBoundaryFunction ) {
2209
+ responseState . sentCompleteBoundaryFunction = true ;
2210
+ writeChunk ( destination , completeBoundaryScript1Full ) ;
2211
+ } else {
2212
+ writeChunk ( destination , completeBoundaryScript1Partial ) ;
2213
+ }
2150
2214
}
2151
2215
} else {
2152
- if ( ! responseState . sentCompleteBoundaryFunction ) {
2153
- responseState . sentCompleteBoundaryFunction = true ;
2154
- writeChunk ( destination , completeBoundaryScript1Full ) ;
2216
+ if ( enableFloat && hasStyleDependencies ) {
2217
+ writeChunk ( destination , completeBoundaryWithStylesData1 ) ;
2155
2218
} else {
2156
- writeChunk ( destination , completeBoundaryScript1Partial ) ;
2219
+ writeChunk ( destination , completeBoundaryData1 ) ;
2157
2220
}
2158
2221
}
2159
2222
@@ -2163,27 +2226,56 @@ export function writeCompletedBoundaryInstruction(
2163
2226
) ;
2164
2227
}
2165
2228
2229
+ // Write function arguments, which are string and array literals
2166
2230
const formattedContentID = stringToChunk ( contentSegmentID . toString ( 16 ) ) ;
2167
2231
writeChunk ( destination , boundaryID ) ;
2168
- writeChunk ( destination , completeBoundaryScript2 ) ;
2232
+ if ( scriptFormat ) {
2233
+ writeChunk ( destination , completeBoundaryScript2 ) ;
2234
+ } else {
2235
+ writeChunk ( destination , completeBoundaryData2 ) ;
2236
+ }
2169
2237
writeChunk ( destination , responseState . segmentPrefix ) ;
2170
2238
writeChunk ( destination , formattedContentID ) ;
2171
2239
if ( enableFloat && hasStyleDependencies ) {
2172
- writeChunk ( destination , completeBoundaryScript2a ) ;
2240
+ if ( scriptFormat ) {
2241
+ writeChunk ( destination , completeBoundaryScript3a ) ;
2242
+ } else {
2243
+ writeChunk ( destination , completeBoundaryData3aStart ) ;
2244
+ }
2173
2245
writeStyleResourceDependencies ( destination , boundaryResources ) ;
2246
+ if ( ! scriptFormat ) {
2247
+ writeChunk ( destination , completeBoundaryData3aEnd ) ;
2248
+ }
2174
2249
} 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 ) ;
2176
2260
}
2177
- return writeChunkAndReturn ( destination , completeBoundaryScript4 ) ;
2178
2261
}
2179
2262
2180
2263
const clientRenderScript1Full = stringToPrecomputedChunk (
2181
2264
clientRenderFunction + ';$RX("' ,
2182
2265
) ;
2183
2266
const clientRenderScript1Partial = stringToPrecomputedChunk ( '$RX("' ) ;
2184
2267
const clientRenderScript1A = stringToPrecomputedChunk ( '"' ) ;
2185
- const clientRenderScript2 = stringToPrecomputedChunk ( ')</script>' ) ;
2186
2268
const 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 ;
2187
2279
2188
2280
export function writeClientRenderBoundaryInstruction (
2189
2281
destination : Destination ,
@@ -2193,14 +2285,20 @@ export function writeClientRenderBoundaryInstruction(
2193
2285
errorMessage ? : string ,
2194
2286
errorComponentStack ? : string ,
2195
2287
) : 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
+ }
2201
2300
} else {
2202
- // Future calls can just reuse the same function.
2203
- writeChunk ( destination , clientRenderScript1Partial ) ;
2301
+ writeChunk ( destination , clientRenderData1 ) ;
2204
2302
}
2205
2303
2206
2304
if ( boundaryID === null ) {
@@ -2210,29 +2308,51 @@ export function writeClientRenderBoundaryInstruction(
2210
2308
}
2211
2309
2212
2310
writeChunk ( destination , boundaryID ) ;
2213
- writeChunk ( destination , clientRenderScript1A ) ;
2311
+ if ( scriptFormat ) {
2312
+ writeChunk ( destination , clientRenderScript1A ) ;
2313
+ } else {
2314
+ writeChunk ( destination , clientRenderData1A ) ;
2315
+ }
2316
+
2214
2317
if ( errorDigest || errorMessage || errorComponentStack ) {
2215
- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2318
+ if ( scriptFormat ) {
2319
+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2320
+ } else {
2321
+ writeChunk ( destination , clientRenderData2 ) ;
2322
+ }
2216
2323
writeChunk (
2217
2324
destination ,
2218
2325
stringToChunk ( escapeJSStringsForInstructionScripts ( errorDigest || '' ) ) ,
2219
2326
) ;
2220
2327
}
2221
2328
if ( errorMessage || errorComponentStack ) {
2222
- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2329
+ if ( scriptFormat ) {
2330
+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2331
+ } else {
2332
+ writeChunk ( destination , clientRenderData3 ) ;
2333
+ }
2223
2334
writeChunk (
2224
2335
destination ,
2225
2336
stringToChunk ( escapeJSStringsForInstructionScripts ( errorMessage || '' ) ) ,
2226
2337
) ;
2227
2338
}
2228
2339
if ( errorComponentStack ) {
2229
- writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2340
+ if ( scriptFormat ) {
2341
+ writeChunk ( destination , clientRenderErrorScriptArgInterstitial ) ;
2342
+ } else {
2343
+ writeChunk ( destination , clientRenderData4 ) ;
2344
+ }
2230
2345
writeChunk (
2231
2346
destination ,
2232
2347
stringToChunk ( escapeJSStringsForInstructionScripts ( errorComponentStack ) ) ,
2233
2348
) ;
2234
2349
}
2235
- return writeChunkAndReturn ( destination , clientRenderScript2 ) ;
2350
+
2351
+ if ( scriptFormat ) {
2352
+ return writeChunkAndReturn ( destination , clientRenderScriptEnd ) ;
2353
+ } else {
2354
+ return writeChunkAndReturn ( destination , clientRenderDataEnd ) ;
2355
+ }
2236
2356
}
2237
2357
2238
2358
const regexForJSStringsInInstructionScripts = / [ < \u2028\u2029 ] / g ;
0 commit comments