@samitouri / QOS-React-2 / commits / 31034b6de7

[Fizz] Split ResponseState/Resources into RenderState/ResumableState (#27268)

This exposes a `resume()` API to go with the `prerender()` (only in experimental). It doesn't work yet since we don't yet emit the postponed state so not yet tested. The main thing this does is rename ResponseState->RenderState and Resources->ResumableState. We separated out resources into a separate concept preemptively since it seemed like separate enough but probably doesn't warrant being a separate concept. The result is that we have a per RenderState in the Config which is really just temporary state and things that must be flushed completely in the prerender. Most things should be ResumableState. Most options are specified in the `prerender()` and transferred into the `resume()` but certain options that are unique per request can't be. Notably `nonce` is special. This means that bootstrap scripts and external runtime can't use `nonce` in this mode. They need to have a CSP configured to deal with external scripts, but not inline. We need to be able to restore state of things that we've already emitted in the prerender. We could have separate snapshot/restore methods that does this work when it happens but that means we have to explicitly do that work. This design is trying to keep to the principle that we just work with resumable data structures instead so that we're designing for it with every feature. It also makes restoring faster since it's just straight into the data structure. This is not yet a serializable format. That can be done in a follow up. We also need to vet that each step makes sense. Notably stylesToHoist is a bit unclear how it'll work.

Sebastian Markbåge committed Aug 22, 2023 at 15:21 UTC 31034b6de73c7cd9093e92a34e384d84c082aa4e
38 files changed +1050 -633
.eslintrc.js
-1
@@ -492,7 +492,6 @@ module.exports = {
492 ReadableStreamController: 'readonly',
493 RequestInfo: 'readonly',
494 RequestOptions: 'readonly',
495 - ResponseState: 'readonly',
495 StoreAsGlobal: 'readonly',
496 symbol: 'readonly',
497 SyntheticEvent: 'readonly',
packages/react-dom-bindings/src/server/ReactFizzConfigDOM.js
+387 -324
@@ -50,7 +50,7 @@ import {
50 } from 'react-server/src/ReactServerStreamConfig';
51 import {
52 resolveRequest,
53 - getResources,
53 + getResumableState,
54 flushResources,
55 } from 'react-server/src/ReactFizzServer';
56
@@ -117,27 +117,24 @@ const SentClientRenderFunction /* */ = 0b00100;
117 const SentStyleInsertionFunction /* */ = 0b01000;
118 const SentFormReplayingRuntime /* */ = 0b10000;
119
120 -// Per response, global state that is not contextual to the rendering subtree.
121 -export type ResponseState = {
122 - bootstrapChunks: Array<Chunk | PrecomputedChunk>,
120 +// Per request, global state that is not contextual to the rendering subtree.
121 +// This cannot be resumed and therefore should only contain things that are
122 +// temporary working state or are never used in the prerender pass.
123 +export type RenderState = {
124 + // These can be recreated from resumable state.
125 placeholderPrefix: PrecomputedChunk,
126 segmentPrefix: PrecomputedChunk,
127 boundaryPrefix: string,
126 - idPrefix: string,
127 - nextSuspenseID: number,
128 - streamingFormat: StreamingFormat,
128
130 - // state for script streaming format, unused if using external runtime / data
129 + // inline script streaming format, unused if using external runtime / data
130 startInlineScript: PrecomputedChunk,
132 - instructions: InstructionState,
131
134 - // state for data streaming format
135 - externalRuntimeScript: null | ExternalRuntimeScript,
132 + // the preamble must always flush before resuming, so all these chunks must
133 + // be null or empty when resuming.
134
137 - // preamble and postamble chunks and state
135 + // preamble chunks
136 htmlChunks: null | Array<Chunk | PrecomputedChunk>,
137 headChunks: null | Array<Chunk | PrecomputedChunk>,
140 - hasBody: boolean,
138
139 // Hoistable chunks
140 charsetChunks: Array<Chunk | PrecomputedChunk>,
@@ -145,6 +142,9 @@ export type ResponseState = {
142 preloadChunks: Array<Chunk | PrecomputedChunk>,
143 hoistableChunks: Array<Chunk | PrecomputedChunk>,
144
145 + // Module-global-like reference for current boundary resources
146 + boundaryResources: ?BoundaryResources,
147 +
148 // Module-global-like reference for flushing/hoisting state of style resources
149 // We need to track whether the current request has flushed any style resources
150 // without sending an instruction to hoist them. we do that here
@@ -155,6 +155,43 @@ export type ResponseState = {
155 ...
156 };
157
158 +// Per response, global state that is not contextual to the rendering subtree.
159 +// This is resumable and therefore should be serializable.
160 +export type ResumableState = {
161 + // external runtime script chunks
162 + externalRuntimeScript: null | ExternalRuntimeScript, // TODO: Move to a serializable format
163 + bootstrapChunks: Array<Chunk | PrecomputedChunk>, // TODO: Move to a serializable format.
164 + idPrefix: string,
165 + nextSuspenseID: number,
166 + streamingFormat: StreamingFormat,
167 +
168 + // state for script streaming format, unused if using external runtime / data
169 + instructions: InstructionState,
170 +
171 + // postamble state
172 + hasBody: boolean,
173 + hasHtml: boolean,
174 +
175 + // Resources
176 +
177 + // Request local cache
178 + preloadsMap: Map<string, PreloadResource>,
179 + preconnectsMap: Map<string, PreconnectResource>,
180 + stylesMap: Map<string, StyleResource>,
181 + scriptsMap: Map<string, ScriptResource>,
182 +
183 + // Flushing queues for Resource dependencies
184 + preconnects: Set<PreconnectResource>,
185 + fontPreloads: Set<PreloadResource>,
186 + highImagePreloads: Set<PreloadResource>,
187 + // usedImagePreloads: Set<PreloadResource>,
188 + precedences: Map<string, Set<StyleResource>>,
189 + stylePrecedences: Map<string, StyleTagResource>,
190 + bootstrapScripts: Set<PreloadResource>,
191 + scripts: Set<ScriptResource>,
192 + bulkPreloads: Set<PreloadResource>,
193 +};
194 +
195 const dataElementQuotedEnd = stringToPrecomputedChunk('"></template>');
196
197 const startInlineScript = stringToPrecomputedChunk('<script>');
@@ -203,26 +240,54 @@ export type ExternalRuntimeScript = {
240 // Allows us to keep track of what we've already written so we can refer back to it.
241 // if passed externalRuntimeConfig and the enableFizzExternalRuntime feature flag
242 // is set, the server will send instructions via data attributes (instead of inline scripts)
206 -export function createResponseState(
207 - resources: Resources,
208 - identifierPrefix: string | void,
243 +export function createRenderState(
244 + resumableState: ResumableState,
245 nonce: string | void,
210 - bootstrapScriptContent: string | void,
211 - bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
212 - bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
213 - externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
214 -): ResponseState {
215 - const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
246 +): RenderState {
247 const inlineScriptWithNonce =
248 nonce === undefined
249 ? startInlineScript
250 : stringToPrecomputedChunk(
251 '<script nonce="' + escapeTextForBrowser(nonce) + '">',
252 );
253 + const idPrefix = resumableState.idPrefix;
254 + return {
255 + placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'),
256 + segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
257 + boundaryPrefix: idPrefix + 'B:',
258 + startInlineScript: inlineScriptWithNonce,
259 + htmlChunks: null,
260 + headChunks: null,
261 + charsetChunks: [],
262 + preconnectChunks: [],
263 + preloadChunks: [],
264 + hoistableChunks: [],
265 + nonce,
266 + // like a module global for currently rendering boundary
267 + boundaryResources: null,
268 + stylesToHoist: false,
269 + };
270 +}
271 +
272 +export function createResumableState(
273 + identifierPrefix: string | void,
274 + nonce: string | void,
275 + bootstrapScriptContent: string | void,
276 + bootstrapScripts: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
277 + bootstrapModules: $ReadOnlyArray<string | BootstrapScriptDescriptor> | void,
278 + externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
279 +): ResumableState {
280 + const idPrefix = identifierPrefix === undefined ? '' : identifierPrefix;
281 const bootstrapChunks: Array<Chunk | PrecomputedChunk> = [];
223 - let externalRuntimeScript: null | ExternalRuntimeScript = null;
282 let streamingFormat = ScriptStreamingFormat;
283 + let externalRuntimeScript: null | ExternalRuntimeScript = null;
284 if (bootstrapScriptContent !== undefined) {
285 + const inlineScriptWithNonce =
286 + nonce === undefined
287 + ? startInlineScript
288 + : stringToPrecomputedChunk(
289 + '<script nonce="' + escapeTextForBrowser(nonce) + '">',
290 + );
291 bootstrapChunks.push(
292 inlineScriptWithNonce,
293 stringToChunk(escapeBootstrapScriptContent(bootstrapScriptContent)),
@@ -262,6 +327,37 @@ export function createResponseState(
327 }
328 }
329 }
330 +
331 + const resumableState: ResumableState = {
332 + externalRuntimeScript: externalRuntimeScript,
333 + bootstrapChunks: bootstrapChunks,
334 + idPrefix: idPrefix,
335 + nextSuspenseID: 0,
336 + streamingFormat,
337 + instructions: NothingSent,
338 + hasBody: false,
339 + hasHtml: false,
340 +
341 + // @TODO add bootstrap script to implicit preloads
342 +
343 + // persistent
344 + preloadsMap: new Map(),
345 + preconnectsMap: new Map(),
346 + stylesMap: new Map(),
347 + scriptsMap: new Map(),
348 +
349 + // cleared on flush
350 + preconnects: new Set(),
351 + fontPreloads: new Set(),
352 + highImagePreloads: new Set(),
353 + // usedImagePreloads: new Set(),
354 + precedences: new Map(),
355 + stylePrecedences: new Map(),
356 + bootstrapScripts: new Set(),
357 + scripts: new Set(),
358 + bulkPreloads: new Set(),
359 + };
360 +
361 if (bootstrapScripts !== undefined) {
362 for (let i = 0; i < bootstrapScripts.length; i++) {
363 const scriptConfig = bootstrapScripts[i];
@@ -276,7 +372,13 @@ export function createResponseState(
372 ? 'use-credentials'
373 : '';
374
279 - preloadBootstrapScript(resources, src, nonce, integrity, crossOrigin);
375 + preloadBootstrapScript(
376 + resumableState,
377 + src,
378 + nonce,
379 + integrity,
380 + crossOrigin,
381 + );
382
383 bootstrapChunks.push(
384 startScriptSrc,
@@ -317,7 +419,13 @@ export function createResponseState(
419 ? 'use-credentials'
420 : '';
421
320 - preloadBootstrapModule(resources, src, nonce, integrity, crossOrigin);
422 + preloadBootstrapModule(
423 + resumableState,
424 + src,
425 + nonce,
426 + integrity,
427 + crossOrigin,
428 + );
429
430 bootstrapChunks.push(
431 startModuleSrc,
@@ -345,27 +453,8 @@ export function createResponseState(
453 bootstrapChunks.push(endAsyncScript);
454 }
455 }
348 - return {
349 - bootstrapChunks: bootstrapChunks,
350 - placeholderPrefix: stringToPrecomputedChunk(idPrefix + 'P:'),
351 - segmentPrefix: stringToPrecomputedChunk(idPrefix + 'S:'),
352 - boundaryPrefix: idPrefix + 'B:',
353 - idPrefix: idPrefix,
354 - nextSuspenseID: 0,
355 - streamingFormat,
356 - startInlineScript: inlineScriptWithNonce,
357 - instructions: NothingSent,
358 - externalRuntimeScript,
359 - htmlChunks: null,
360 - headChunks: null,
361 - hasBody: false,
362 - charsetChunks: [],
363 - preconnectChunks: [],
364 - preloadChunks: [],
365 - hoistableChunks: [],
366 - stylesToHoist: false,
367 - nonce,
368 - };
456 +
457 + return resumableState;
458 }
459
460 // Constants for the insertion mode we're currently writing in. We don't encode all HTML5 insertion
@@ -506,20 +595,21 @@ export type SuspenseBoundaryID = null | PrecomputedChunk;
595 export const UNINITIALIZED_SUSPENSE_BOUNDARY_ID: SuspenseBoundaryID = null;
596
597 export function assignSuspenseBoundaryID(
509 - responseState: ResponseState,
598 + renderState: RenderState,
599 + resumableState: ResumableState,
600 ): SuspenseBoundaryID {
511 - const generatedID = responseState.nextSuspenseID++;
601 + const generatedID = resumableState.nextSuspenseID++;
602 return stringToPrecomputedChunk(
513 - responseState.boundaryPrefix + generatedID.toString(16),
603 + renderState.boundaryPrefix + generatedID.toString(16),
604 );
605 }
606
607 export function makeId(
518 - responseState: ResponseState,
608 + resumableState: ResumableState,
609 treeId: string,
610 localId: number,
611 ): string {
522 - const idPrefix = responseState.idPrefix;
612 + const idPrefix = resumableState.idPrefix;
613
614 let id = ':' + idPrefix + 'R' + treeId;
615
@@ -542,7 +632,7 @@ const textSeparator = stringToPrecomputedChunk('<!-- -->');
632 export function pushTextInstance(
633 target: Array<Chunk | PrecomputedChunk>,
634 text: string,
545 - responseState: ResponseState,
635 + renderState: RenderState,
636 textEmbedded: boolean,
637 ): boolean {
638 if (text === '') {
@@ -560,7 +650,7 @@ export function pushTextInstance(
650 // emit a text separator when we don't know for sure it is safe to omit
651 export function pushSegmentFinale(
652 target: Array<Chunk | PrecomputedChunk>,
563 - responseState: ResponseState,
653 + renderState: RenderState,
654 lastPushedText: boolean,
655 textEmbedded: boolean,
656 ): void {
@@ -700,11 +790,11 @@ function pushStringAttribute(
790 }
791 }
792
703 -function makeFormFieldPrefix(responseState: ResponseState): string {
793 +function makeFormFieldPrefix(resumableState: ResumableState): string {
794 // I'm just reusing this counter. It's not really the same namespace as "name".
795 // It could just be its own counter.
706 - const id = responseState.nextSuspenseID++;
707 - return responseState.idPrefix + id;
796 + const id = resumableState.nextSuspenseID++;
797 + return resumableState.idPrefix + id;
798 }
799
800 // Since this will likely be repeated a lot in the HTML, we use a more concise message
@@ -748,7 +838,8 @@ function pushAdditionalFormFields(
838
839 function pushFormActionAttribute(
840 target: Array<Chunk | PrecomputedChunk>,
751 - responseState: ResponseState,
841 + resumableState: ResumableState,
842 + renderState: RenderState,
843 formAction: any,
844 formEncType: any,
845 formMethod: any,
@@ -788,7 +879,7 @@ function pushFormActionAttribute(
879 if (typeof customAction === 'function') {
880 // This action has a custom progressive enhancement form that can submit the form
881 // back to the server if it's invoked before hydration. Such as a Server Action.
791 - const prefix = makeFormFieldPrefix(responseState);
882 + const prefix = makeFormFieldPrefix(resumableState);
883 const customFields = formAction.$$FORM_ACTION(prefix);
884 name = customFields.name;
885 formAction = customFields.action || '';
@@ -814,7 +905,7 @@ function pushFormActionAttribute(
905 formEncType = null;
906 formMethod = null;
907 formTarget = null;
817 - injectFormReplayingRuntime(responseState);
908 + injectFormReplayingRuntime(resumableState, renderState);
909 }
910 }
911 if (name != null) {
@@ -1163,7 +1254,7 @@ function pushInnerHTML(
1254 }
1255 }
1256
1166 -// TODO: Move these to ResponseState so that we warn for every request.
1257 +// TODO: Move these to RenderState so that we warn for every request.
1258 // It would help debugging in stateful servers (e.g. service worker).
1259 let didWarnDefaultInputValue = false;
1260 let didWarnDefaultChecked = false;
@@ -1391,17 +1482,20 @@ function pushStartOption(
1482 const formReplayingRuntimeScript =
1483 stringToPrecomputedChunk(formReplayingRuntime);
1484
1394 -function injectFormReplayingRuntime(responseState: ResponseState): void {
1485 +function injectFormReplayingRuntime(
1486 + resumableState: ResumableState,
1487 + renderState: RenderState,
1488 +): void {
1489 // If we haven't sent it yet, inject the runtime that tracks submitted JS actions
1490 // for later replaying by Fiber. If we use an external runtime, we don't need
1491 // to emit anything. It's always used.
1492 if (
1399 - (responseState.instructions & SentFormReplayingRuntime) === NothingSent &&
1400 - (!enableFizzExternalRuntime || !responseState.externalRuntimeScript)
1493 + (resumableState.instructions & SentFormReplayingRuntime) === NothingSent &&
1494 + (!enableFizzExternalRuntime || !resumableState.externalRuntimeScript)
1495 ) {
1402 - responseState.instructions |= SentFormReplayingRuntime;
1403 - responseState.bootstrapChunks.unshift(
1404 - responseState.startInlineScript,
1496 + resumableState.instructions |= SentFormReplayingRuntime;
1497 + resumableState.bootstrapChunks.unshift(
1498 + renderState.startInlineScript,
1499 formReplayingRuntimeScript,
1500 endInlineScript,
1501 );
@@ -1411,7 +1505,8 @@ function injectFormReplayingRuntime(responseState: ResponseState): void {
1505 function pushStartForm(
1506 target: Array<Chunk | PrecomputedChunk>,
1507 props: Object,
1414 - responseState: ResponseState,
1508 + resumableState: ResumableState,
1509 + renderState: RenderState,
1510 ): ReactNodeList {
1511 target.push(startChunkForTag('form'));
1512
@@ -1482,7 +1577,7 @@ function pushStartForm(
1577 if (typeof customAction === 'function') {
1578 // This action has a custom progressive enhancement form that can submit the form
1579 // back to the server if it's invoked before hydration. Such as a Server Action.
1485 - const prefix = makeFormFieldPrefix(responseState);
1580 + const prefix = makeFormFieldPrefix(resumableState);
1581 const customFields = formAction.$$FORM_ACTION(prefix);
1582 formAction = customFields.action || '';
1583 formEncType = customFields.encType;
@@ -1507,7 +1602,7 @@ function pushStartForm(
1602 formEncType = null;
1603 formMethod = null;
1604 formTarget = null;
1510 - injectFormReplayingRuntime(responseState);
1605 + injectFormReplayingRuntime(resumableState, renderState);
1606 }
1607 }
1608 if (formAction != null) {
@@ -1545,7 +1640,8 @@ function pushStartForm(
1640 function pushInput(
1641 target: Array<Chunk | PrecomputedChunk>,
1642 props: Object,
1548 - responseState: ResponseState,
1643 + resumableState: ResumableState,
1644 + renderState: RenderState,
1645 ): ReactNodeList {
1646 if (__DEV__) {
1647 checkControlledValueProps('input', props);
@@ -1626,7 +1722,8 @@ function pushInput(
1722
1723 const formData = pushFormActionAttribute(
1724 target,
1629 - responseState,
1725 + resumableState,
1726 + renderState,
1727 formAction,
1728 formEncType,
1729 formMethod,
@@ -1685,7 +1782,8 @@ function pushInput(
1782 function pushStartButton(
1783 target: Array<Chunk | PrecomputedChunk>,
1784 props: Object,
1688 - responseState: ResponseState,
1785 + resumableState: ResumableState,
1786 + renderState: RenderState,
1787 ): ReactNodeList {
1788 target.push(startChunkForTag('button'));
1789
@@ -1748,7 +1846,8 @@ function pushStartButton(
1846
1847 const formData = pushFormActionAttribute(
1848 target,
1751 - responseState,
1849 + resumableState,
1850 + renderState,
1851 formAction,
1852 formEncType,
1853 formMethod,
@@ -1894,7 +1993,7 @@ function pushStartTextArea(
1993 function pushMeta(
1994 target: Array<Chunk | PrecomputedChunk>,
1995 props: Object,
1897 - responseState: ResponseState,
1996 + renderState: RenderState,
1997 textEmbedded: boolean,
1998 insertionMode: InsertionMode,
1999 noscriptTagInScope: boolean,
@@ -1914,12 +2013,12 @@ function pushMeta(
2013 }
2014
2015 if (typeof props.charSet === 'string') {
1917 - return pushSelfClosing(responseState.charsetChunks, props, 'meta');
2016 + return pushSelfClosing(renderState.charsetChunks, props, 'meta');
2017 } else if (props.name === 'viewport') {
2018 // "viewport" isn't related to preconnect but it has the right priority
1920 - return pushSelfClosing(responseState.preconnectChunks, props, 'meta');
2019 + return pushSelfClosing(renderState.preconnectChunks, props, 'meta');
2020 } else {
1922 - return pushSelfClosing(responseState.hoistableChunks, props, 'meta');
2021 + return pushSelfClosing(renderState.hoistableChunks, props, 'meta');
2022 }
2023 }
2024 } else {
@@ -1930,8 +2029,8 @@ function pushMeta(
2029 function pushLink(
2030 target: Array<Chunk | PrecomputedChunk>,
2031 props: Object,
1933 - responseState: ResponseState,
1934 - resources: Resources,
2032 + resumableState: ResumableState,
2033 + renderState: RenderState,
2034 textEmbedded: boolean,
2035 insertionMode: InsertionMode,
2036 noscriptTagInScope: boolean,
@@ -1998,10 +2097,10 @@ function pushLink(
2097 return pushLinkImpl(target, props);
2098 } else {
2099 // This stylesheet refers to a Resource and we create a new one if necessary
2001 - let resource = resources.stylesMap.get(key);
2100 + let resource = resumableState.stylesMap.get(key);
2101 if (!resource) {
2102 const resourceProps = stylesheetPropsFromRawProps(props);
2004 - const preloadResource = resources.preloadsMap.get(key);
2103 + const preloadResource = resumableState.preloadsMap.get(key);
2104 let state = NoState;
2105 if (preloadResource) {
2106 // If we already had a preload we don't want that resource to flush directly.
@@ -2021,11 +2120,11 @@ function pushLink(
2120 state,
2121 props: resourceProps,
2122 };
2024 - resources.stylesMap.set(key, resource);
2025 - let precedenceSet = resources.precedences.get(precedence);
2123 + resumableState.stylesMap.set(key, resource);
2124 + let precedenceSet = resumableState.precedences.get(precedence);
2125 if (!precedenceSet) {
2126 precedenceSet = new Set();
2028 - resources.precedences.set(precedence, precedenceSet);
2127 + resumableState.precedences.set(precedence, precedenceSet);
2128 const emptyStyleResource = {
2129 type: 'style',
2130 chunks: ([]: Array<Chunk | PrecomputedChunk>),
@@ -2037,19 +2136,19 @@ function pushLink(
2136 };
2137 precedenceSet.add(emptyStyleResource);
2138 if (__DEV__) {
2040 - if (resources.stylePrecedences.has(precedence)) {
2139 + if (resumableState.stylePrecedences.has(precedence)) {
2140 console.error(
2141 'React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.',
2142 precedence,
2143 );
2144 }
2145 }
2047 - resources.stylePrecedences.set(precedence, emptyStyleResource);
2146 + resumableState.stylePrecedences.set(precedence, emptyStyleResource);
2147 }
2148 precedenceSet.add(resource);
2149 }
2051 - if (resources.boundaryResources) {
2052 - resources.boundaryResources.add(resource);
2150 + if (renderState.boundaryResources) {
2151 + renderState.boundaryResources.add(resource);
2152 }
2153 if (textEmbedded) {
2154 // This link follows text but we aren't writing a tag. while not as efficient as possible we need
@@ -2074,11 +2173,11 @@ function pushLink(
2173 switch (props.rel) {
2174 case 'preconnect':
2175 case 'dns-prefetch':
2077 - return pushLinkImpl(responseState.preconnectChunks, props);
2176 + return pushLinkImpl(renderState.preconnectChunks, props);
2177 case 'preload':
2079 - return pushLinkImpl(responseState.preloadChunks, props);
2178 + return pushLinkImpl(renderState.preloadChunks, props);
2179 default:
2081 - return pushLinkImpl(responseState.hoistableChunks, props);
2180 + return pushLinkImpl(renderState.hoistableChunks, props);
2181 }
2182 }
2183 } else {
@@ -2119,7 +2218,8 @@ function pushLinkImpl(
2218 function pushStyle(
2219 target: Array<Chunk | PrecomputedChunk>,
2220 props: Object,
2122 - resources: Resources,
2221 + resumableState: ResumableState,
2222 + renderState: RenderState,
2223 textEmbedded: boolean,
2224 insertionMode: InsertionMode,
2225 noscriptTagInScope: boolean,
@@ -2179,9 +2279,9 @@ function pushStyle(
2279 }
2280
2281 const key = getResourceKey('style', href);
2182 - let resource = resources.stylesMap.get(key);
2282 + let resource = resumableState.stylesMap.get(key);
2283 if (!resource) {
2184 - resource = resources.stylePrecedences.get(precedence);
2284 + resource = resumableState.stylePrecedences.get(precedence);
2285 if (!resource) {
2286 resource = {
2287 type: 'style',
@@ -2192,24 +2292,24 @@ function pushStyle(
2292 hrefs: [href],
2293 },
2294 };
2195 - resources.stylePrecedences.set(precedence, resource);
2295 + resumableState.stylePrecedences.set(precedence, resource);
2296 const precedenceSet: Set<StyleResource> = new Set();
2297 precedenceSet.add(resource);
2298 if (__DEV__) {
2199 - if (resources.precedences.has(precedence)) {
2299 + if (resumableState.precedences.has(precedence)) {
2300 console.error(
2301 'React constructed a new style precedence set when one already exists for this precedence: "%s". This is a bug in React.',
2302 precedence,
2303 );
2304 }
2305 }
2206 - resources.precedences.set(precedence, precedenceSet);
2306 + resumableState.precedences.set(precedence, precedenceSet);
2307 } else {
2308 resource.props.hrefs.push(href);
2309 }
2210 - resources.stylesMap.set(key, resource);
2211 - if (resources.boundaryResources) {
2212 - resources.boundaryResources.add(resource);
2310 + resumableState.stylesMap.set(key, resource);
2311 + if (renderState.boundaryResources) {
2312 + renderState.boundaryResources.add(resource);
2313 }
2314 pushStyleContents(resource.chunks, props);
2315 }
@@ -2333,7 +2433,7 @@ function getImagePreloadKey(
2433 function pushImg(
2434 target: Array<Chunk | PrecomputedChunk>,
2435 props: Object,
2336 - resources: Resources,
2436 + resumableState: ResumableState,
2437 ): null {
2438 const {src, srcSet} = props;
2439 if (
@@ -2359,10 +2459,10 @@ function pushImg(
2459 )
2460 ) {
2461 // We have a suspensey image and ought to preload it to optimize the loading of display blocking
2362 - // resources.
2462 + // resumableState.
2463 const {sizes} = props;
2464 const key = getImagePreloadKey(src, srcSet, sizes);
2365 - let resource = resources.preloadsMap.get(key);
2465 + let resource = resumableState.preloadsMap.get(key);
2466 if (!resource) {
2467 resource = {
2468 type: 'preload',
@@ -2385,16 +2485,16 @@ function pushImg(
2485 referrerPolicy: props.referrerPolicy,
2486 },
2487 };
2388 - resources.preloadsMap.set(key, resource);
2488 + resumableState.preloadsMap.set(key, resource);
2489 pushLinkImpl(resource.chunks, resource.props);
2490 }
2491 if (
2492 props.fetchPriority === 'high' ||
2393 - resources.highImagePreloads.size < 10
2493 + resumableState.highImagePreloads.size < 10
2494 ) {
2395 - resources.highImagePreloads.add(resource);
2495 + resumableState.highImagePreloads.add(resource);
2496 } else {
2397 - resources.bulkPreloads.add(resource);
2497 + resumableState.bulkPreloads.add(resource);
2498 }
2499 }
2500 return pushSelfClosing(target, props, 'img');
@@ -2463,7 +2563,7 @@ function pushStartMenuItem(
2563 function pushTitle(
2564 target: Array<Chunk | PrecomputedChunk>,
2565 props: Object,
2466 - responseState: ResponseState,
2566 + renderState: RenderState,
2567 insertionMode: InsertionMode,
2568 noscriptTagInScope: boolean,
2569 ): ReactNodeList {
@@ -2521,7 +2621,7 @@ function pushTitle(
2621 !noscriptTagInScope &&
2622 props.itemProp == null
2623 ) {
2524 - pushTitleImpl(responseState.hoistableChunks, props);
2624 + pushTitleImpl(renderState.hoistableChunks, props);
2625 return null;
2626 } else {
2627 return pushTitleImpl(target, props);
@@ -2653,14 +2753,14 @@ function pushStartTitle(
2753 function pushStartHead(
2754 target: Array<Chunk | PrecomputedChunk>,
2755 props: Object,
2656 - responseState: ResponseState,
2756 + renderState: RenderState,
2757 insertionMode: InsertionMode,
2758 ): ReactNodeList {
2759 if (enableFloat) {
2660 - if (insertionMode < HTML_MODE && responseState.headChunks === null) {
2760 + if (insertionMode < HTML_MODE && renderState.headChunks === null) {
2761 // This <head> is the Document.head and should be part of the preamble
2662 - responseState.headChunks = [];
2663 - return pushStartGenericElement(responseState.headChunks, props, 'head');
2762 + renderState.headChunks = [];
2763 + return pushStartGenericElement(renderState.headChunks, props, 'head');
2764 } else {
2765 // This <head> is deep and is likely just an error. we emit it inline though.
2766 // Validation should warn that this tag is the the wrong spot.
@@ -2674,14 +2774,14 @@ function pushStartHead(
2774 function pushStartHtml(
2775 target: Array<Chunk | PrecomputedChunk>,
2776 props: Object,
2677 - responseState: ResponseState,
2777 + renderState: RenderState,
2778 insertionMode: InsertionMode,
2779 ): ReactNodeList {
2780 if (enableFloat) {
2681 - if (insertionMode === ROOT_HTML_MODE && responseState.htmlChunks === null) {
2781 + if (insertionMode === ROOT_HTML_MODE && renderState.htmlChunks === null) {
2782 // This <html> is the Document.documentElement and should be part of the preamble
2683 - responseState.htmlChunks = [DOCTYPE];
2684 - return pushStartGenericElement(responseState.htmlChunks, props, 'html');
2783 + renderState.htmlChunks = [DOCTYPE];
2784 + return pushStartGenericElement(renderState.htmlChunks, props, 'html');
2785 } else {
2786 // This <html> is deep and is likely just an error. we emit it inline though.
2787 // Validation should warn that this tag is the the wrong spot.
@@ -2701,7 +2801,7 @@ function pushStartHtml(
2801 function pushScript(
2802 target: Array<Chunk | PrecomputedChunk>,
2803 props: Object,
2704 - resources: Resources,
2804 + resumableState: ResumableState,
2805 textEmbedded: boolean,
2806 insertionMode: InsertionMode,
2807 noscriptTagInScope: boolean,
@@ -2729,7 +2829,7 @@ function pushScript(
2829 const src = props.src;
2830 const key = getResourceKey('script', src);
2831 // We can make this <script> into a ScriptResource
2732 - let resource = resources.scriptsMap.get(key);
2832 + let resource = resumableState.scriptsMap.get(key);
2833 if (!resource) {
2834 resource = {
2835 type: 'script',
@@ -2737,12 +2837,12 @@ function pushScript(
2837 state: NoState,
2838 props: null,
2839 };
2740 - resources.scriptsMap.set(key, resource);
2840 + resumableState.scriptsMap.set(key, resource);
2841 // Add to the script flushing queue
2742 - resources.scripts.add(resource);
2842 + resumableState.scripts.add(resource);
2843
2844 let scriptProps = props;
2745 - const preloadResource = resources.preloadsMap.get(key);
2845 + const preloadResource = resumableState.preloadsMap.get(key);
2846 if (preloadResource) {
2847 // If we already had a preload we don't want that resource to flush directly.
2848 // We let the newly created resource govern flushing.
@@ -3036,8 +3136,8 @@ export function pushStartInstance(
3136 target: Array<Chunk | PrecomputedChunk>,
3137 type: string,
3138 props: Object,
3039 - resources: Resources,
3040 - responseState: ResponseState,
3139 + resumableState: ResumableState,
3140 + renderState: RenderState,
3141 formatContext: FormatContext,
3142 textEmbedded: boolean,
3143 ): ReactNodeList {
@@ -3093,11 +3193,11 @@ export function pushStartInstance(
3193 case 'textarea':
3194 return pushStartTextArea(target, props);
3195 case 'input':
3096 - return pushInput(target, props, responseState);
3196 + return pushInput(target, props, resumableState, renderState);
3197 case 'button':
3098 - return pushStartButton(target, props, responseState);
3198 + return pushStartButton(target, props, resumableState, renderState);
3199 case 'form':
3100 - return pushStartForm(target, props, responseState);
3200 + return pushStartForm(target, props, resumableState, renderState);
3201 case 'menuitem':
3202 return pushStartMenuItem(target, props);
3203 case 'title':
@@ -3105,7 +3205,7 @@ export function pushStartInstance(
3205 ? pushTitle(
3206 target,
3207 props,
3108 - responseState,
3208 + renderState,
3209 formatContext.insertionMode,
3210 formatContext.noscriptTagInScope,
3211 )
@@ -3114,8 +3214,8 @@ export function pushStartInstance(
3214 return pushLink(
3215 target,
3216 props,
3117 - responseState,
3118 - resources,
3217 + resumableState,
3218 + renderState,
3219 textEmbedded,
3220 formatContext.insertionMode,
3221 formatContext.noscriptTagInScope,
@@ -3125,7 +3225,7 @@ export function pushStartInstance(
3225 ? pushScript(
3226 target,
3227 props,
3128 - resources,
3228 + resumableState,
3229 textEmbedded,
3230 formatContext.insertionMode,
3231 formatContext.noscriptTagInScope,
@@ -3135,7 +3235,8 @@ export function pushStartInstance(
3235 return pushStyle(
3236 target,
3237 props,
3138 - resources,
3238 + resumableState,
3239 + renderState,
3240 textEmbedded,
3241 formatContext.insertionMode,
3242 formatContext.noscriptTagInScope,
@@ -3144,7 +3245,7 @@ export function pushStartInstance(
3245 return pushMeta(
3246 target,
3247 props,
3147 - responseState,
3248 + renderState,
3249 textEmbedded,
3250 formatContext.insertionMode,
3251 formatContext.noscriptTagInScope,
@@ -3156,7 +3257,7 @@ export function pushStartInstance(
3257 }
3258 case 'img': {
3259 return enableFloat
3159 - ? pushImg(target, props, resources)
3260 + ? pushImg(target, props, resumableState)
3261 : pushSelfClosing(target, props, type);
3262 }
3263 // Omitted close tags
@@ -3190,14 +3291,14 @@ export function pushStartInstance(
3291 return pushStartHead(
3292 target,
3293 props,
3193 - responseState,
3294 + renderState,
3295 formatContext.insertionMode,
3296 );
3297 case 'html': {
3298 return pushStartHtml(
3299 target,
3300 props,
3200 - responseState,
3301 + renderState,
3302 formatContext.insertionMode,
3303 );
3304 }
@@ -3219,7 +3320,7 @@ export function pushEndInstance(
3320 target: Array<Chunk | PrecomputedChunk>,
3321 type: string,
3322 props: Object,
3222 - responseState: ResponseState,
3323 + resumableState: ResumableState,
3324 formatContext: FormatContext,
3325 ): void {
3326 switch (type) {
@@ -3263,13 +3364,14 @@ export function pushEndInstance(
3364 // we won't emit any more tags
3365 case 'body': {
3366 if (enableFloat && formatContext.insertionMode <= HTML_HTML_MODE) {
3266 - responseState.hasBody = true;
3367 + resumableState.hasBody = true;
3368 return;
3369 }
3370 break;
3371 }
3372 case 'html':
3373 if (enableFloat && formatContext.insertionMode === ROOT_HTML_MODE) {
3374 + resumableState.hasHtml = true;
3375 return;
3376 }
3377 break;
@@ -3279,9 +3381,9 @@ export function pushEndInstance(
3381
3382 function writeBootstrap(
3383 destination: Destination,
3282 - responseState: ResponseState,
3384 + resumableState: ResumableState,
3385 ): boolean {
3284 - const bootstrapChunks = responseState.bootstrapChunks;
3386 + const bootstrapChunks = resumableState.bootstrapChunks;
3387 let i = 0;
3388 for (; i < bootstrapChunks.length - 1; i++) {
3389 writeChunk(destination, bootstrapChunks[i]);
@@ -3296,9 +3398,9 @@ function writeBootstrap(
3398
3399 export function writeCompletedRoot(
3400 destination: Destination,
3299 - responseState: ResponseState,
3401 + resumableState: ResumableState,
3402 ): boolean {
3301 - return writeBootstrap(destination, responseState);
3403 + return writeBootstrap(destination, resumableState);
3404 }
3405
3406 // Structural Nodes
@@ -3310,11 +3412,11 @@ const placeholder1 = stringToPrecomputedChunk('<template id="');
3412 const placeholder2 = stringToPrecomputedChunk('"></template>');
3413 export function writePlaceholder(
3414 destination: Destination,
3313 - responseState: ResponseState,
3415 + renderState: RenderState,
3416 id: number,
3417 ): boolean {
3418 writeChunk(destination, placeholder1);
3317 - writeChunk(destination, responseState.placeholderPrefix);
3419 + writeChunk(destination, renderState.placeholderPrefix);
3420 const formattedID = stringToChunk(id.toString(16));
3421 writeChunk(destination, formattedID);
3422 return writeChunkAndReturn(destination, placeholder2);
@@ -3357,13 +3459,13 @@ export function pushEndCompletedSuspenseBoundary(
3459
3460 export function writeStartCompletedSuspenseBoundary(
3461 destination: Destination,
3360 - responseState: ResponseState,
3462 + renderState: RenderState,
3463 ): boolean {
3464 return writeChunkAndReturn(destination, startCompletedSuspenseBoundary);
3465 }
3466 export function writeStartPendingSuspenseBoundary(
3467 destination: Destination,
3366 - responseState: ResponseState,
3468 + renderState: RenderState,
3469 id: SuspenseBoundaryID,
3470 ): boolean {
3471 writeChunk(destination, startPendingSuspenseBoundary1);
@@ -3379,7 +3481,7 @@ export function writeStartPendingSuspenseBoundary(
3481 }
3482 export function writeStartClientRenderedSuspenseBoundary(
3483 destination: Destination,
3382 - responseState: ResponseState,
3484 + renderState: RenderState,
3485 errorDigest: ?string,
3486 errorMesssage: ?string,
3487 errorComponentStack: ?string,
@@ -3430,19 +3532,19 @@ export function writeStartClientRenderedSuspenseBoundary(
3532 }
3533 export function writeEndCompletedSuspenseBoundary(
3534 destination: Destination,
3433 - responseState: ResponseState,
3535 + renderState: RenderState,
3536 ): boolean {
3537 return writeChunkAndReturn(destination, endSuspenseBoundary);
3538 }
3539 export function writeEndPendingSuspenseBoundary(
3540 destination: Destination,
3439 - responseState: ResponseState,
3541 + renderState: RenderState,
3542 ): boolean {
3543 return writeChunkAndReturn(destination, endSuspenseBoundary);
3544 }
3545 export function writeEndClientRenderedSuspenseBoundary(
3546 destination: Destination,
3445 - responseState: ResponseState,
3547 + renderState: RenderState,
3548 ): boolean {
3549 return writeChunkAndReturn(destination, endSuspenseBoundary);
3550 }
@@ -3485,7 +3587,7 @@ const endSegmentColGroup = stringToPrecomputedChunk('</colgroup></table>');
3587
3588 export function writeStartSegment(
3589 destination: Destination,
3488 - responseState: ResponseState,
3590 + renderState: RenderState,
3591 formatContext: FormatContext,
3592 id: number,
3593 ): boolean {
@@ -3494,25 +3596,25 @@ export function writeStartSegment(
3596 case HTML_HTML_MODE:
3597 case HTML_MODE: {
3598 writeChunk(destination, startSegmentHTML);
3497 - writeChunk(destination, responseState.segmentPrefix);
3599 + writeChunk(destination, renderState.segmentPrefix);
3600 writeChunk(destination, stringToChunk(id.toString(16)));
3601 return writeChunkAndReturn(destination, startSegmentHTML2);
3602 }
3603 case SVG_MODE: {
3604 writeChunk(destination, startSegmentSVG);
3503 - writeChunk(destination, responseState.segmentPrefix);
3605 + writeChunk(destination, renderState.segmentPrefix);
3606 writeChunk(destination, stringToChunk(id.toString(16)));
3607 return writeChunkAndReturn(destination, startSegmentSVG2);
3608 }
3609 case MATHML_MODE: {
3610 writeChunk(destination, startSegmentMathML);
3509 - writeChunk(destination, responseState.segmentPrefix);
3611 + writeChunk(destination, renderState.segmentPrefix);
3612 writeChunk(destination, stringToChunk(id.toString(16)));
3613 return writeChunkAndReturn(destination, startSegmentMathML2);
3614 }
3615 case HTML_TABLE_MODE: {
3616 writeChunk(destination, startSegmentTable);
3515 - writeChunk(destination, responseState.segmentPrefix);
3617 + writeChunk(destination, renderState.segmentPrefix);
3618 writeChunk(destination, stringToChunk(id.toString(16)));
3619 return writeChunkAndReturn(destination, startSegmentTable2);
3620 }
@@ -3522,19 +3624,19 @@ export function writeStartSegment(
3624 // and it's kind of an edge case to suspend in a table. Totally supported though.
3625 case HTML_TABLE_BODY_MODE: {
3626 writeChunk(destination, startSegmentTableBody);
3525 - writeChunk(destination, responseState.segmentPrefix);
3627 + writeChunk(destination, renderState.segmentPrefix);
3628 writeChunk(destination, stringToChunk(id.toString(16)));
3629 return writeChunkAndReturn(destination, startSegmentTableBody2);
3630 }
3631 case HTML_TABLE_ROW_MODE: {
3632 writeChunk(destination, startSegmentTableRow);
3531 - writeChunk(destination, responseState.segmentPrefix);
3633 + writeChunk(destination, renderState.segmentPrefix);
3634 writeChunk(destination, stringToChunk(id.toString(16)));
3635 return writeChunkAndReturn(destination, startSegmentTableRow2);
3636 }
3637 case HTML_COLGROUP_MODE: {
3638 writeChunk(destination, startSegmentColGroup);
3537 - writeChunk(destination, responseState.segmentPrefix);
3639 + writeChunk(destination, renderState.segmentPrefix);
3640 writeChunk(destination, stringToChunk(id.toString(16)));
3641 return writeChunkAndReturn(destination, startSegmentColGroup2);
3642 }
@@ -3592,20 +3694,21 @@ const completeSegmentDataEnd = dataElementQuotedEnd;
3694
3695 export function writeCompletedSegmentInstruction(
3696 destination: Destination,
3595 - responseState: ResponseState,
3697 + resumableState: ResumableState,
3698 + renderState: RenderState,
3699 contentSegmentID: number,
3700 ): boolean {
3701 const scriptFormat =
3702 !enableFizzExternalRuntime ||
3600 - responseState.streamingFormat === ScriptStreamingFormat;
3703 + resumableState.streamingFormat === ScriptStreamingFormat;
3704 if (scriptFormat) {
3602 - writeChunk(destination, responseState.startInlineScript);
3705 + writeChunk(destination, renderState.startInlineScript);
3706 if (
3604 - (responseState.instructions & SentCompleteSegmentFunction) ===
3707 + (resumableState.instructions & SentCompleteSegmentFunction) ===
3708 NothingSent
3709 ) {
3710 // The first time we write this, we'll need to include the full implementation.
3608 - responseState.instructions |= SentCompleteSegmentFunction;
3711 + resumableState.instructions |= SentCompleteSegmentFunction;
3712 writeChunk(destination, completeSegmentScript1Full);
3713 } else {
3714 // Future calls can just reuse the same function.
@@ -3616,7 +3719,7 @@ export function writeCompletedSegmentInstruction(
3719 }
3720
3721 // Write function arguments, which are string literals
3619 - writeChunk(destination, responseState.segmentPrefix);
3722 + writeChunk(destination, renderState.segmentPrefix);
3723 const formattedID = stringToChunk(contentSegmentID.toString(16));
3724 writeChunk(destination, formattedID);
3725 if (scriptFormat) {
@@ -3624,7 +3727,7 @@ export function writeCompletedSegmentInstruction(
3727 } else {
3728 writeChunk(destination, completeSegmentData2);
3729 }
3627 - writeChunk(destination, responseState.placeholderPrefix);
3730 + writeChunk(destination, renderState.placeholderPrefix);
3731 writeChunk(destination, formattedID);
3732
3733 if (scriptFormat) {
@@ -3665,51 +3768,52 @@ const completeBoundaryDataEnd = dataElementQuotedEnd;
3768
3769 export function writeCompletedBoundaryInstruction(
3770 destination: Destination,
3668 - responseState: ResponseState,
3771 + resumableState: ResumableState,
3772 + renderState: RenderState,
3773 boundaryID: SuspenseBoundaryID,
3774 contentSegmentID: number,
3775 boundaryResources: BoundaryResources,
3776 ): boolean {
3777 let requiresStyleInsertion;
3778 if (enableFloat) {
3675 - requiresStyleInsertion = responseState.stylesToHoist;
3779 + requiresStyleInsertion = renderState.stylesToHoist;
3780 // If necessary stylesheets will be flushed with this instruction.
3781 // Any style tags not yet hoisted in the Document will also be hoisted.
3782 // We reset this state since after this instruction executes all styles
3783 // up to this point will have been hoisted
3680 - responseState.stylesToHoist = false;
3784 + renderState.stylesToHoist = false;
3785 }
3786 const scriptFormat =
3787 !enableFizzExternalRuntime ||
3684 - responseState.streamingFormat === ScriptStreamingFormat;
3788 + resumableState.streamingFormat === ScriptStreamingFormat;
3789 if (scriptFormat) {
3686 - writeChunk(destination, responseState.startInlineScript);
3790 + writeChunk(destination, renderState.startInlineScript);
3791 if (enableFloat && requiresStyleInsertion) {
3792 if (
3689 - (responseState.instructions & SentCompleteBoundaryFunction) ===
3793 + (resumableState.instructions & SentCompleteBoundaryFunction) ===
3794 NothingSent
3795 ) {
3692 - responseState.instructions |=
3796 + resumableState.instructions |=
3797 SentStyleInsertionFunction | SentCompleteBoundaryFunction;
3798 writeChunk(
3799 destination,
3800 clonePrecomputedChunk(completeBoundaryWithStylesScript1FullBoth),
3801 );
3802 } else if (
3699 - (responseState.instructions & SentStyleInsertionFunction) ===
3803 + (resumableState.instructions & SentStyleInsertionFunction) ===
3804 NothingSent
3805 ) {
3702 - responseState.instructions |= SentStyleInsertionFunction;
3806 + resumableState.instructions |= SentStyleInsertionFunction;
3807 writeChunk(destination, completeBoundaryWithStylesScript1FullPartial);
3808 } else {
3809 writeChunk(destination, completeBoundaryWithStylesScript1Partial);
3810 }
3811 } else {
3812 if (
3709 - (responseState.instructions & SentCompleteBoundaryFunction) ===
3813 + (resumableState.instructions & SentCompleteBoundaryFunction) ===
3814 NothingSent
3815 ) {
3712 - responseState.instructions |= SentCompleteBoundaryFunction;
3816 + resumableState.instructions |= SentCompleteBoundaryFunction;
3817 writeChunk(destination, completeBoundaryScript1Full);
3818 } else {
3819 writeChunk(destination, completeBoundaryScript1Partial);
@@ -3737,7 +3841,7 @@ export function writeCompletedBoundaryInstruction(
3841 } else {
3842 writeChunk(destination, completeBoundaryData2);
3843 }
3740 - writeChunk(destination, responseState.segmentPrefix);
3844 + writeChunk(destination, renderState.segmentPrefix);
3845 writeChunk(destination, formattedContentID);
3846 if (enableFloat && requiresStyleInsertion) {
3847 // Script and data writers must format this differently:
@@ -3764,7 +3868,7 @@ export function writeCompletedBoundaryInstruction(
3868 } else {
3869 writeMore = writeChunkAndReturn(destination, completeBoundaryDataEnd);
3870 }
3767 - return writeBootstrap(destination, responseState) && writeMore;
3871 + return writeBootstrap(destination, resumableState) && writeMore;
3872 }
3873
3874 const clientRenderScript1Full = stringToPrecomputedChunk(
@@ -3785,7 +3889,8 @@ const clientRenderDataEnd = dataElementQuotedEnd;
3889
3890 export function writeClientRenderBoundaryInstruction(
3891 destination: Destination,
3788 - responseState: ResponseState,
3892 + resumableState: ResumableState,
3893 + renderState: RenderState,
3894 boundaryID: SuspenseBoundaryID,
3895 errorDigest: ?string,
3896 errorMessage?: string,
@@ -3793,15 +3898,15 @@ export function writeClientRenderBoundaryInstruction(
3898 ): boolean {
3899 const scriptFormat =
3900 !enableFizzExternalRuntime ||
3796 - responseState.streamingFormat === ScriptStreamingFormat;
3901 + resumableState.streamingFormat === ScriptStreamingFormat;
3902 if (scriptFormat) {
3798 - writeChunk(destination, responseState.startInlineScript);
3903 + writeChunk(destination, renderState.startInlineScript);
3904 if (
3800 - (responseState.instructions & SentClientRenderFunction) ===
3905 + (resumableState.instructions & SentClientRenderFunction) ===
3906 NothingSent
3907 ) {
3908 // The first time we write this, we'll need to include the full implementation.
3804 - responseState.instructions |= SentClientRenderFunction;
3909 + resumableState.instructions |= SentClientRenderFunction;
3910 writeChunk(destination, clientRenderScript1Full);
3911 } else {
3912 // Future calls can just reuse the same function.
@@ -4002,7 +4107,7 @@ function flushStyleTagsLateForBoundary(
4107 export function writeResourcesForBoundary(
4108 destination: Destination,
4109 boundaryResources: BoundaryResources,
4005 - responseState: ResponseState,
4110 + renderState: RenderState,
4111 ): boolean {
4112 // Reset these on each invocation, they are only safe to read in this function
4113 currentlyRenderingBoundaryHasStylesToHoist = false;
@@ -4011,7 +4116,7 @@ export function writeResourcesForBoundary(
4116 // Flush each Boundary resource
4117 boundaryResources.forEach(flushStyleTagsLateForBoundary, destination);
4118 if (currentlyRenderingBoundaryHasStylesToHoist) {
4014 - responseState.stylesToHoist = true;
4119 + renderState.stylesToHoist = true;
4120 }
4121 return destinationHasCapacity;
4122 }
@@ -4162,27 +4267,27 @@ function preloadLateStyles(
4267 // of the flushing code in Fizz.
4268 export function writePreamble(
4269 destination: Destination,
4165 - resources: Resources,
4166 - responseState: ResponseState,
4270 + resumableState: ResumableState,
4271 + renderState: RenderState,
4272 willFlushAllSegments: boolean,
4273 ): void {
4274 // This function must be called exactly once on every request
4275 if (
4276 enableFizzExternalRuntime &&
4277 !willFlushAllSegments &&
4173 - responseState.externalRuntimeScript
4278 + resumableState.externalRuntimeScript
4279 ) {
4280 // If the root segment is incomplete due to suspended tasks
4281 // (e.g. willFlushAllSegments = false) and we are using data
4282 // streaming format, ensure the external runtime is sent.
4283 // (User code could choose to send this even earlier by calling
4284 // preinit(...), if they know they will suspend).
4180 - const {src, chunks} = responseState.externalRuntimeScript;
4181 - internalPreinitScript(resources, src, chunks);
4285 + const {src, chunks} = resumableState.externalRuntimeScript;
4286 + internalPreinitScript(resumableState, src, chunks);
4287 }
4288
4184 - const htmlChunks = responseState.htmlChunks;
4185 - const headChunks = responseState.headChunks;
4289 + const htmlChunks = renderState.htmlChunks;
4290 + const headChunks = renderState.headChunks;
4291
4292 let i = 0;
4293
@@ -4209,48 +4314,51 @@ export function writePreamble(
4314 }
4315
4316 // Emit high priority Hoistables
4212 - const charsetChunks = responseState.charsetChunks;
4317 + const charsetChunks = renderState.charsetChunks;
4318 for (i = 0; i < charsetChunks.length; i++) {
4319 writeChunk(destination, charsetChunks[i]);
4320 }
4321 charsetChunks.length = 0;
4322
4323 // emit preconnect resources
4219 - resources.preconnects.forEach(flushResourceInPreamble, destination);
4220 - resources.preconnects.clear();
4324 + resumableState.preconnects.forEach(flushResourceInPreamble, destination);
4325 + resumableState.preconnects.clear();
4326
4222 - const preconnectChunks = responseState.preconnectChunks;
4327 + const preconnectChunks = renderState.preconnectChunks;
4328 for (i = 0; i < preconnectChunks.length; i++) {
4329 writeChunk(destination, preconnectChunks[i]);
4330 }
4331 preconnectChunks.length = 0;
4332
4228 - resources.fontPreloads.forEach(flushResourceInPreamble, destination);
4229 - resources.fontPreloads.clear();
4333 + resumableState.fontPreloads.forEach(flushResourceInPreamble, destination);
4334 + resumableState.fontPreloads.clear();
4335
4231 - resources.highImagePreloads.forEach(flushResourceInPreamble, destination);
4232 - resources.highImagePreloads.clear();
4336 + resumableState.highImagePreloads.forEach(
4337 + flushResourceInPreamble,
4338 + destination,
4339 + );
4340 + resumableState.highImagePreloads.clear();
4341
4342 // Flush unblocked stylesheets by precedence
4235 - resources.precedences.forEach(flushAllStylesInPreamble, destination);
4343 + resumableState.precedences.forEach(flushAllStylesInPreamble, destination);
4344
4237 - resources.bootstrapScripts.forEach(flushResourceInPreamble, destination);
4345 + resumableState.bootstrapScripts.forEach(flushResourceInPreamble, destination);
4346
4239 - resources.scripts.forEach(flushResourceInPreamble, destination);
4240 - resources.scripts.clear();
4347 + resumableState.scripts.forEach(flushResourceInPreamble, destination);
4348 + resumableState.scripts.clear();
4349
4242 - resources.bulkPreloads.forEach(flushResourceInPreamble, destination);
4243 - resources.bulkPreloads.clear();
4350 + resumableState.bulkPreloads.forEach(flushResourceInPreamble, destination);
4351 + resumableState.bulkPreloads.clear();
4352
4353 // Write embedding preloadChunks
4246 - const preloadChunks = responseState.preloadChunks;
4354 + const preloadChunks = renderState.preloadChunks;
4355 for (i = 0; i < preloadChunks.length; i++) {
4356 writeChunk(destination, preloadChunks[i]);
4357 }
4358 preloadChunks.length = 0;
4359
4360 // Write embedding hoistableChunks
4253 - const hoistableChunks = responseState.hoistableChunks;
4361 + const hoistableChunks = renderState.hoistableChunks;
4362 for (i = 0; i < hoistableChunks.length; i++) {
4363 writeChunk(destination, hoistableChunks[i]);
4364 }
@@ -4275,8 +4383,8 @@ export function writePreamble(
4383 // of the flushing code in Fizz.
4384 export function writeHoistables(
4385 destination: Destination,
4278 - resources: Resources,
4279 - responseState: ResponseState,
4386 + resumableState: ResumableState,
4387 + renderState: RenderState,
4388 ): void {
4389 let i = 0;
4390
@@ -4285,43 +4393,46 @@ export function writeHoistables(
4393 // We omit charsetChunks because we have already sent the shell and if it wasn't
4394 // already sent it is too late now.
4395
4288 - resources.preconnects.forEach(flushResourceLate, destination);
4289 - resources.preconnects.clear();
4396 + resumableState.preconnects.forEach(flushResourceLate, destination);
4397 + resumableState.preconnects.clear();
4398
4291 - const preconnectChunks = responseState.preconnectChunks;
4399 + const preconnectChunks = renderState.preconnectChunks;
4400 for (i = 0; i < preconnectChunks.length; i++) {
4401 writeChunk(destination, preconnectChunks[i]);
4402 }
4403 preconnectChunks.length = 0;
4404
4297 - resources.fontPreloads.forEach(flushResourceLate, destination);
4298 - resources.fontPreloads.clear();
4405 + resumableState.fontPreloads.forEach(flushResourceLate, destination);
4406 + resumableState.fontPreloads.clear();
4407
4300 - resources.highImagePreloads.forEach(flushResourceInPreamble, destination);
4301 - resources.highImagePreloads.clear();
4408 + resumableState.highImagePreloads.forEach(
4409 + flushResourceInPreamble,
4410 + destination,
4411 + );
4412 + resumableState.highImagePreloads.clear();
4413
4414 // Preload any stylesheets. these will emit in a render instruction that follows this
4415 // but we want to kick off preloading as soon as possible
4305 - resources.precedences.forEach(preloadLateStyles, destination);
4416 + resumableState.precedences.forEach(preloadLateStyles, destination);
4417
4418 // bootstrap scripts should flush above script priority but these can only flush in the preamble
4419 // so we elide the code here for performance
4420
4310 - resources.scripts.forEach(flushResourceLate, destination);
4311 - resources.scripts.clear();
4421 + resumableState.scripts.forEach(flushResourceLate, destination);
4422 + resumableState.scripts.clear();
4423
4313 - resources.bulkPreloads.forEach(flushResourceLate, destination);
4314 - resources.bulkPreloads.clear();
4424 + resumableState.bulkPreloads.forEach(flushResourceLate, destination);
4425 + resumableState.bulkPreloads.clear();
4426
4427 // Write embedding preloadChunks
4317 - const preloadChunks = responseState.preloadChunks;
4428 + const preloadChunks = renderState.preloadChunks;
4429 for (i = 0; i < preloadChunks.length; i++) {
4430 writeChunk(destination, preloadChunks[i]);
4431 }
4432 preloadChunks.length = 0;
4433
4434 // Write embedding hoistableChunks
4324 - const hoistableChunks = responseState.hoistableChunks;
4435 + const hoistableChunks = renderState.hoistableChunks;
4436 for (i = 0; i < hoistableChunks.length; i++) {
4437 writeChunk(destination, hoistableChunks[i]);
4438 }
@@ -4330,14 +4441,14 @@ export function writeHoistables(
4441
4442 export function writePostamble(
4443 destination: Destination,
4333 - responseState: ResponseState,
4444 + resumableState: ResumableState,
4445 ): void {
4335 - if (responseState.hasBody) {
4446 + if (resumableState.hasBody) {
4447 writeChunk(destination, endTag1);
4448 writeChunk(destination, stringToChunk('body'));
4449 writeChunk(destination, endTag2);
4450 }
4340 - if (responseState.htmlChunks) {
4451 + if (resumableState.hasHtml) {
4452 writeChunk(destination, endTag1);
4453 writeChunk(destination, stringToChunk('html'));
4454 writeChunk(destination, endTag2);
@@ -4823,54 +4934,6 @@ type Resource =
4934 | PreloadResource
4935 | PreconnectResource;
4936
4826 -export type Resources = {
4827 - // Request local cache
4828 - preloadsMap: Map<string, PreloadResource>,
4829 - preconnectsMap: Map<string, PreconnectResource>,
4830 - stylesMap: Map<string, StyleResource>,
4831 - scriptsMap: Map<string, ScriptResource>,
4832 -
4833 - // Flushing queues for Resource dependencies
4834 - preconnects: Set<PreconnectResource>,
4835 - fontPreloads: Set<PreloadResource>,
4836 - highImagePreloads: Set<PreloadResource>,
4837 - // usedImagePreloads: Set<PreloadResource>,
4838 - precedences: Map<string, Set<StyleResource>>,
4839 - stylePrecedences: Map<string, StyleTagResource>,
4840 - bootstrapScripts: Set<PreloadResource>,
4841 - scripts: Set<ScriptResource>,
4842 - bulkPreloads: Set<PreloadResource>,
4843 -
4844 - // Module-global-like reference for current boundary resources
4845 - boundaryResources: ?BoundaryResources,
4846 - ...
4847 -};
4848 -
4849 -// @TODO add bootstrap script to implicit preloads
4850 -export function createResources(): Resources {
4851 - return {
4852 - // persistent
4853 - preloadsMap: new Map(),
4854 - preconnectsMap: new Map(),
4855 - stylesMap: new Map(),
4856 - scriptsMap: new Map(),
4857 -
4858 - // cleared on flush
4859 - preconnects: new Set(),
4860 - fontPreloads: new Set(),
4861 - highImagePreloads: new Set(),
4862 - // usedImagePreloads: new Set(),
4863 - precedences: new Map(),
4864 - stylePrecedences: new Map(),
4865 - bootstrapScripts: new Set(),
4866 - scripts: new Set(),
4867 - bulkPreloads: new Set(),
4868 -
4869 - // like a module global for currently rendering boundary
4870 - boundaryResources: null,
4871 - };
4872 -}
4873 -
4937 export type BoundaryResources = Set<StyleResource>;
4938
4939 export function createBoundaryResources(): BoundaryResources {
@@ -4878,10 +4941,10 @@ export function createBoundaryResources(): BoundaryResources {
4941 }
4942
4943 export function setCurrentlyRenderingBoundaryResourcesTarget(
4881 - resources: Resources,
4944 + renderState: RenderState,
4945 boundaryResources: null | BoundaryResources,
4946 ) {
4884 - resources.boundaryResources = boundaryResources;
4947 + renderState.boundaryResources = boundaryResources;
4948 }
4949
4950 function getResourceKey(as: string, href: string): string {
@@ -4901,7 +4964,7 @@ function prefetchDNS(href: string, options?: ?PrefetchDNSOptions) {
4964 // fetching) and we don't want to warn in those cases.
4965 return;
4966 }
4904 - const resources = getResources(request);
4967 + const resumableState = getResumableState(request);
4968 if (__DEV__) {
4969 if (typeof href !== 'string' || !href) {
4970 console.error(
@@ -4928,7 +4991,7 @@ function prefetchDNS(href: string, options?: ?PrefetchDNSOptions) {
4991
4992 if (typeof href === 'string' && href) {
4993 const key = getResourceKey('prefetchDNS', href);
4931 - let resource = resources.preconnectsMap.get(key);
4994 + let resource = resumableState.preconnectsMap.get(key);
4995 if (!resource) {
4996 resource = {
4997 type: 'preconnect',
@@ -4936,13 +4999,13 @@ function prefetchDNS(href: string, options?: ?PrefetchDNSOptions) {
4999 state: NoState,
5000 props: null,
5001 };
4939 - resources.preconnectsMap.set(key, resource);
5002 + resumableState.preconnectsMap.set(key, resource);
5003 pushLinkImpl(
5004 resource.chunks,
5005 ({href, rel: 'dns-prefetch'}: PreconnectProps),
5006 );
5007 }
4945 - resources.preconnects.add(resource);
5008 + resumableState.preconnects.add(resource);
5009 flushResources(request);
5010 }
5011 }
@@ -4960,7 +5023,7 @@ function preconnect(href: string, options?: ?PreconnectOptions) {
5023 // fetching) and we don't want to warn in those cases.
5024 return;
5025 }
4963 - const resources = getResources(request);
5026 + const resumableState = getResumableState(request);
5027 if (__DEV__) {
5028 if (typeof href !== 'string' || !href) {
5029 console.error(
@@ -4991,7 +5054,7 @@ function preconnect(href: string, options?: ?PreconnectOptions) {
5054 const key = `[preconnect][${
5055 crossOrigin === null ? 'null' : crossOrigin
5056 }]${href}`;
4994 - let resource = resources.preconnectsMap.get(key);
5057 + let resource = resumableState.preconnectsMap.get(key);
5058 if (!resource) {
5059 resource = {
5060 type: 'preconnect',
@@ -4999,13 +5062,13 @@ function preconnect(href: string, options?: ?PreconnectOptions) {
5062 state: NoState,
5063 props: null,
5064 };
5002 - resources.preconnectsMap.set(key, resource);
5065 + resumableState.preconnectsMap.set(key, resource);
5066 pushLinkImpl(
5067 resource.chunks,
5068 ({rel: 'preconnect', href, crossOrigin}: PreconnectProps),
5069 );
5070 }
5008 - resources.preconnects.add(resource);
5071 + resumableState.preconnects.add(resource);
5072 flushResources(request);
5073 }
5074 }
@@ -5023,7 +5086,7 @@ function preload(href: string, options: PreloadOptions) {
5086 // fetching) and we don't want to warn in those cases.
5087 return;
5088 }
5026 - const resources = getResources(request);
5089 + const resumableState = getResumableState(request);
5090 if (__DEV__) {
5091 let encountered = '';
5092 if (typeof href !== 'string' || !href) {
@@ -5066,7 +5129,7 @@ function preload(href: string, options: PreloadOptions) {
5129 } else {
5130 key = getResourceKey(as, href);
5131 }
5069 - let resource = resources.preloadsMap.get(key);
5132 + let resource = resumableState.preloadsMap.get(key);
5133 if (!resource) {
5134 resource = {
5135 type: 'preload',
@@ -5074,15 +5137,15 @@ function preload(href: string, options: PreloadOptions) {
5137 state: NoState,
5138 props: preloadPropsFromPreloadOptions(href, as, options),
5139 };
5077 - resources.preloadsMap.set(key, resource);
5140 + resumableState.preloadsMap.set(key, resource);
5141 pushLinkImpl(resource.chunks, resource.props);
5142 }
5143 if (as === 'font') {
5081 - resources.fontPreloads.add(resource);
5144 + resumableState.fontPreloads.add(resource);
5145 } else if (as === 'image' && options.fetchPriority === 'high') {
5083 - resources.highImagePreloads.add(resource);
5146 + resumableState.highImagePreloads.add(resource);
5147 } else {
5085 - resources.bulkPreloads.add(resource);
5148 + resumableState.bulkPreloads.add(resource);
5149 }
5150 flushResources(request);
5151 }
@@ -5101,7 +5164,7 @@ function preloadModule(href: string, options?: ?PreloadModuleOptions): void {
5164 // fetching) and we don't want to warn in those cases.
5165 return;
5166 }
5104 - const resources = getResources(request);
5167 + const resumableState = getResumableState(request);
5168 if (__DEV__) {
5169 let encountered = '';
5170 if (typeof href !== 'string' || !href) {
@@ -5129,7 +5192,7 @@ function preloadModule(href: string, options?: ?PreloadModuleOptions): void {
5192 const as =
5193 options && typeof options.as === 'string' ? options.as : 'script';
5194 const key = getResourceKey(as, href);
5132 - let resource = resources.preloadsMap.get(key);
5195 + let resource = resumableState.preloadsMap.get(key);
5196 if (!resource) {
5197 resource = {
5198 type: 'preload',
@@ -5137,10 +5200,10 @@ function preloadModule(href: string, options?: ?PreloadModuleOptions): void {
5200 state: NoState,
5201 props: preloadModulePropsFromPreloadModuleOptions(href, as, options),
5202 };
5140 - resources.preloadsMap.set(key, resource);
5203 + resumableState.preloadsMap.set(key, resource);
5204 pushLinkImpl(resource.chunks, resource.props);
5205 }
5143 - resources.bulkPreloads.add(resource);
5206 + resumableState.bulkPreloads.add(resource);
5207 flushResources(request);
5208 }
5209 }
@@ -5158,7 +5221,7 @@ function preinit(href: string, options: PreinitOptions): void {
5221 // fetching) and we don't want to warn in those cases.
5222 return;
5223 }
5161 - const resources = getResources(request);
5224 + const resumableState = getResumableState(request);
5225 if (__DEV__) {
5226 if (typeof href !== 'string' || !href) {
5227 console.error(
@@ -5187,11 +5250,11 @@ function preinit(href: string, options: PreinitOptions): void {
5250 switch (as) {
5251 case 'style': {
5252 const key = getResourceKey(as, href);
5190 - let resource = resources.stylesMap.get(key);
5253 + let resource = resumableState.stylesMap.get(key);
5254 const precedence = options.precedence || 'default';
5255 if (!resource) {
5256 let state = NoState;
5194 - const preloadResource = resources.preloadsMap.get(key);
5257 + const preloadResource = resumableState.preloadsMap.get(key);
5258 if (preloadResource && preloadResource.state & Flushed) {
5259 state = PreloadFlushed;
5260 }
@@ -5201,11 +5264,11 @@ function preinit(href: string, options: PreinitOptions): void {
5264 state,
5265 props: stylesheetPropsFromPreinitOptions(href, precedence, options),
5266 };
5204 - resources.stylesMap.set(key, resource);
5205 - let precedenceSet = resources.precedences.get(precedence);
5267 + resumableState.stylesMap.set(key, resource);
5268 + let precedenceSet = resumableState.precedences.get(precedence);
5269 if (!precedenceSet) {
5270 precedenceSet = new Set();
5208 - resources.precedences.set(precedence, precedenceSet);
5271 + resumableState.precedences.set(precedence, precedenceSet);
5272 const emptyStyleResource = {
5273 type: 'style',
5274 chunks: ([]: Array<Chunk | PrecomputedChunk>),
@@ -5217,14 +5280,14 @@ function preinit(href: string, options: PreinitOptions): void {
5280 };
5281 precedenceSet.add(emptyStyleResource);
5282 if (__DEV__) {
5220 - if (resources.stylePrecedences.has(precedence)) {
5283 + if (resumableState.stylePrecedences.has(precedence)) {
5284 console.error(
5285 'React constructed an empty style resource when a style resource already exists for this precedence: "%s". This is a bug in React.',
5286 precedence,
5287 );
5288 }
5289 }
5227 - resources.stylePrecedences.set(precedence, emptyStyleResource);
5290 + resumableState.stylePrecedences.set(precedence, emptyStyleResource);
5291 }
5292 precedenceSet.add(resource);
5293 flushResources(request);
@@ -5234,7 +5297,7 @@ function preinit(href: string, options: PreinitOptions): void {
5297 case 'script': {
5298 const src = href;
5299 const key = getResourceKey(as, src);
5237 - let resource = resources.scriptsMap.get(key);
5300 + let resource = resumableState.scriptsMap.get(key);
5301 if (!resource) {
5302 resource = {
5303 type: 'script',
@@ -5242,9 +5305,9 @@ function preinit(href: string, options: PreinitOptions): void {
5305 state: NoState,
5306 props: null,
5307 };
5245 - resources.scriptsMap.set(key, resource);
5308 + resumableState.scriptsMap.set(key, resource);
5309 const resourceProps = scriptPropsFromPreinitOptions(src, options);
5247 - resources.scripts.add(resource);
5310 + resumableState.scripts.add(resource);
5311 pushScriptImpl(resource.chunks, resourceProps);
5312 flushResources(request);
5313 }
@@ -5267,7 +5330,7 @@ function preinitModule(href: string, options?: ?PreinitModuleOptions): void {
5330 // fetching) and we don't want to warn in those cases.
5331 return;
5332 }
5270 - const resources = getResources(request);
5333 + const resumableState = getResumableState(request);
5334 if (__DEV__) {
5335 let encountered = '';
5336 if (typeof href !== 'string' || !href) {
@@ -5319,7 +5382,7 @@ function preinitModule(href: string, options?: ?PreinitModuleOptions): void {
5382 case 'script': {
5383 const src = href;
5384 const key = getResourceKey(as, src);
5322 - let resource = resources.scriptsMap.get(key);
5385 + let resource = resumableState.scriptsMap.get(key);
5386 if (!resource) {
5387 resource = {
5388 type: 'script',
@@ -5327,12 +5390,12 @@ function preinitModule(href: string, options?: ?PreinitModuleOptions): void {
5390 state: NoState,
5391 props: null,
5392 };
5330 - resources.scriptsMap.set(key, resource);
5393 + resumableState.scriptsMap.set(key, resource);
5394 const resourceProps = modulePropsFromPreinitModuleOptions(
5395 src,
5396 options,
5397 );
5335 - resources.scripts.add(resource);
5398 + resumableState.scripts.add(resource);
5399 pushScriptImpl(resource.chunks, resourceProps);
5400 flushResources(request);
5401 }
@@ -5347,7 +5410,7 @@ function preinitModule(href: string, options?: ?PreinitModuleOptions): void {
5410 // scripts at any other point in time we will need to check whether the preload
5411 // already exists and not assume it
5412 function preloadBootstrapScript(
5350 - resources: Resources,
5413 + resumableState: ResumableState,
5414 src: string,
5415 nonce: ?string,
5416 integrity: ?string,
@@ -5355,7 +5418,7 @@ function preloadBootstrapScript(
5418 ): void {
5419 const key = getResourceKey('script', src);
5420 if (__DEV__) {
5358 - if (resources.preloadsMap.has(key)) {
5421 + if (resumableState.preloadsMap.has(key)) {
5422 // This is coded as a React error because it should be impossible for a userspace preload to preempt this call
5423 // If a userspace preload can preempt it then this assumption is broken and we need to reconsider this strategy
5424 // rather than instruct the user to not preload their bootstrap scripts themselves
@@ -5380,8 +5443,8 @@ function preloadBootstrapScript(
5443 state: NoState,
5444 props,
5445 };
5383 - resources.preloadsMap.set(key, resource);
5384 - resources.bootstrapScripts.add(resource);
5446 + resumableState.preloadsMap.set(key, resource);
5447 + resumableState.bootstrapScripts.add(resource);
5448 pushLinkImpl(resource.chunks, props);
5449 }
5450
@@ -5390,7 +5453,7 @@ function preloadBootstrapScript(
5453 // scripts at any other point in time we will need to check whether the preload
5454 // already exists and not assume it
5455 function preloadBootstrapModule(
5393 - resources: Resources,
5456 + resumableState: ResumableState,
5457 src: string,
5458 nonce: ?string,
5459 integrity: ?string,
@@ -5398,7 +5461,7 @@ function preloadBootstrapModule(
5461 ): void {
5462 const key = getResourceKey('script', src);
5463 if (__DEV__) {
5401 - if (resources.preloadsMap.has(key)) {
5464 + if (resumableState.preloadsMap.has(key)) {
5465 // This is coded as a React error because it should be impossible for a userspace preload to preempt this call
5466 // If a userspace preload can preempt it then this assumption is broken and we need to reconsider this strategy
5467 // rather than instruct the user to not preload their bootstrap scripts themselves
@@ -5422,19 +5485,19 @@ function preloadBootstrapModule(
5485 state: NoState,
5486 props,
5487 };
5425 - resources.preloadsMap.set(key, resource);
5426 - resources.bootstrapScripts.add(resource);
5488 + resumableState.preloadsMap.set(key, resource);
5489 + resumableState.bootstrapScripts.add(resource);
5490 pushLinkImpl(resource.chunks, props);
5491 return;
5492 }
5493
5494 function internalPreinitScript(
5432 - resources: Resources,
5495 + resumableState: ResumableState,
5496 src: string,
5497 chunks: Array<Chunk | PrecomputedChunk>,
5498 ): void {
5499 const key = getResourceKey('script', src);
5437 - let resource = resources.scriptsMap.get(key);
5500 + let resource = resumableState.scriptsMap.get(key);
5501 if (!resource) {
5502 resource = {
5503 type: 'script',
@@ -5442,8 +5505,8 @@ function internalPreinitScript(
5505 state: NoState,
5506 props: null,
5507 };
5445 - resources.scriptsMap.set(key, resource);
5446 - resources.scripts.add(resource);
5508 + resumableState.scriptsMap.set(key, resource);
5509 + resumableState.scripts.add(resource);
5510 }
5511 return;
5512 }
@@ -5575,10 +5638,10 @@ function hoistStyleResource(this: BoundaryResources, resource: StyleResource) {
5638 }
5639
5640 export function hoistResources(
5578 - resources: Resources,
5641 + renderState: RenderState,
5642 source: BoundaryResources,
5643 ): void {
5581 - const currentBoundaryResources = resources.boundaryResources;
5644 + const currentBoundaryResources = renderState.boundaryResources;
5645 if (currentBoundaryResources) {
5646 source.forEach(hoistStyleResource, currentBoundaryResources);
5647 }
packages/react-dom-bindings/src/server/ReactFizzConfigDOMLegacy.js
+41 -68
@@ -7,16 +7,10 @@
7 * @flow
8 */
9
10 -import type {
11 - Resources,
12 - BootstrapScriptDescriptor,
13 - ExternalRuntimeScript,
14 - StreamingFormat,
15 - InstructionState,
16 -} from './ReactFizzConfigDOM';
10 +import type {ResumableState, BoundaryResources} from './ReactFizzConfigDOM';
11
12 import {
19 - createResponseState as createResponseStateImpl,
13 + createRenderState as createRenderStateImpl,
14 pushTextInstance as pushTextInstanceImpl,
15 pushSegmentFinale as pushSegmentFinaleImpl,
16 writeStartCompletedSuspenseBoundary as writeStartCompletedSuspenseBoundaryImpl,
@@ -37,65 +31,44 @@ import {NotPending} from '../shared/ReactDOMFormActions';
31
32 export const isPrimaryRenderer = false;
33
40 -export type ResponseState = {
34 +export type RenderState = {
35 // Keep this in sync with ReactFizzConfigDOM
42 - bootstrapChunks: Array<Chunk | PrecomputedChunk>,
36 placeholderPrefix: PrecomputedChunk,
37 segmentPrefix: PrecomputedChunk,
38 boundaryPrefix: string,
46 - idPrefix: string,
47 - nextSuspenseID: number,
48 - streamingFormat: StreamingFormat,
39 startInlineScript: PrecomputedChunk,
50 - instructions: InstructionState,
51 - externalRuntimeScript: null | ExternalRuntimeScript,
40 htmlChunks: null | Array<Chunk | PrecomputedChunk>,
41 headChunks: null | Array<Chunk | PrecomputedChunk>,
54 - hasBody: boolean,
42 charsetChunks: Array<Chunk | PrecomputedChunk>,
43 preconnectChunks: Array<Chunk | PrecomputedChunk>,
44 preloadChunks: Array<Chunk | PrecomputedChunk>,
45 hoistableChunks: Array<Chunk | PrecomputedChunk>,
46 + boundaryResources: ?BoundaryResources,
47 stylesToHoist: boolean,
48 // This is an extra field for the legacy renderer
49 generateStaticMarkup: boolean,
50 };
51
64 -export function createResponseState(
65 - resources: Resources,
52 +export function createRenderState(
53 + resumableState: ResumableState,
54 + nonce: string | void,
55 generateStaticMarkup: boolean,
67 - identifierPrefix: string | void,
68 - externalRuntimeConfig: string | BootstrapScriptDescriptor | void,
69 -): ResponseState {
70 - const responseState = createResponseStateImpl(
71 - resources,
72 - identifierPrefix,
73 - undefined,
74 - undefined,
75 - undefined,
76 - undefined,
77 - externalRuntimeConfig,
78 - );
56 +): RenderState {
57 + const renderState = createRenderStateImpl(resumableState, nonce);
58 return {
59 // Keep this in sync with ReactFizzConfigDOM
81 - bootstrapChunks: responseState.bootstrapChunks,
82 - placeholderPrefix: responseState.placeholderPrefix,
83 - segmentPrefix: responseState.segmentPrefix,
84 - boundaryPrefix: responseState.boundaryPrefix,
85 - idPrefix: responseState.idPrefix,
86 - nextSuspenseID: responseState.nextSuspenseID,
87 - streamingFormat: responseState.streamingFormat,
88 - startInlineScript: responseState.startInlineScript,
89 - instructions: responseState.instructions,
90 - externalRuntimeScript: responseState.externalRuntimeScript,
91 - htmlChunks: responseState.htmlChunks,
92 - headChunks: responseState.headChunks,
93 - hasBody: responseState.hasBody,
94 - charsetChunks: responseState.charsetChunks,
95 - preconnectChunks: responseState.preconnectChunks,
96 - preloadChunks: responseState.preloadChunks,
97 - hoistableChunks: responseState.hoistableChunks,
98 - stylesToHoist: responseState.stylesToHoist,
60 + placeholderPrefix: renderState.placeholderPrefix,
61 + segmentPrefix: renderState.segmentPrefix,
62 + boundaryPrefix: renderState.boundaryPrefix,
63 + startInlineScript: renderState.startInlineScript,
64 + htmlChunks: renderState.htmlChunks,
65 + headChunks: renderState.headChunks,
66 + charsetChunks: renderState.charsetChunks,
67 + preconnectChunks: renderState.preconnectChunks,
68 + preloadChunks: renderState.preloadChunks,
69 + hoistableChunks: renderState.hoistableChunks,
70 + boundaryResources: renderState.boundaryResources,
71 + stylesToHoist: renderState.stylesToHoist,
72
73 // This is an extra field for the legacy renderer
74 generateStaticMarkup,
@@ -111,7 +84,7 @@ import {
84 export const doctypeChunk: PrecomputedChunk = stringToPrecomputedChunk('');
85
86 export type {
114 - Resources,
87 + ResumableState,
88 BoundaryResources,
89 FormatContext,
90 SuspenseBoundaryID,
@@ -137,7 +110,7 @@ export {
110 writePlaceholder,
111 writeCompletedRoot,
112 createRootFormatContext,
140 - createResources,
113 + createResumableState,
114 createBoundaryResources,
115 writePreamble,
116 writeHoistables,
@@ -152,29 +125,29 @@ import escapeTextForBrowser from './escapeTextForBrowser';
125 export function pushTextInstance(
126 target: Array<Chunk | PrecomputedChunk>,
127 text: string,
155 - responseState: ResponseState,
128 + renderState: RenderState,
129 textEmbedded: boolean,
130 ): boolean {
158 - if (responseState.generateStaticMarkup) {
131 + if (renderState.generateStaticMarkup) {
132 target.push(stringToChunk(escapeTextForBrowser(text)));
133 return false;
134 } else {
162 - return pushTextInstanceImpl(target, text, responseState, textEmbedded);
135 + return pushTextInstanceImpl(target, text, renderState, textEmbedded);
136 }
137 }
138
139 export function pushSegmentFinale(
140 target: Array<Chunk | PrecomputedChunk>,
168 - responseState: ResponseState,
141 + renderState: RenderState,
142 lastPushedText: boolean,
143 textEmbedded: boolean,
144 ): void {
172 - if (responseState.generateStaticMarkup) {
145 + if (renderState.generateStaticMarkup) {
146 return;
147 } else {
148 return pushSegmentFinaleImpl(
149 target,
177 - responseState,
150 + renderState,
151 lastPushedText,
152 textEmbedded,
153 );
@@ -183,31 +156,31 @@ export function pushSegmentFinale(
156
157 export function writeStartCompletedSuspenseBoundary(
158 destination: Destination,
186 - responseState: ResponseState,
159 + renderState: RenderState,
160 ): boolean {
188 - if (responseState.generateStaticMarkup) {
161 + if (renderState.generateStaticMarkup) {
162 // A completed boundary is done and doesn't need a representation in the HTML
163 // if we're not going to be hydrating it.
164 return true;
165 }
193 - return writeStartCompletedSuspenseBoundaryImpl(destination, responseState);
166 + return writeStartCompletedSuspenseBoundaryImpl(destination, renderState);
167 }
168 export function writeStartClientRenderedSuspenseBoundary(
169 destination: Destination,
197 - responseState: ResponseState,
170 + renderState: RenderState,
171 // flushing these error arguments are not currently supported in this legacy streaming format.
172 errorDigest: ?string,
173 errorMessage: ?string,
174 errorComponentStack: ?string,
175 ): boolean {
203 - if (responseState.generateStaticMarkup) {
176 + if (renderState.generateStaticMarkup) {
177 // A client rendered boundary is done and doesn't need a representation in the HTML
178 // since we'll never hydrate it. This is arguably an error in static generation.
179 return true;
180 }
181 return writeStartClientRenderedSuspenseBoundaryImpl(
182 destination,
210 - responseState,
183 + renderState,
184 errorDigest,
185 errorMessage,
186 errorComponentStack,
@@ -215,21 +188,21 @@ export function writeStartClientRenderedSuspenseBoundary(
188 }
189 export function writeEndCompletedSuspenseBoundary(
190 destination: Destination,
218 - responseState: ResponseState,
191 + renderState: RenderState,
192 ): boolean {
220 - if (responseState.generateStaticMarkup) {
193 + if (renderState.generateStaticMarkup) {
194 return true;
195 }
223 - return writeEndCompletedSuspenseBoundaryImpl(destination, responseState);
196 + return writeEndCompletedSuspenseBoundaryImpl(destination, renderState);
197 }
198 export function writeEndClientRenderedSuspenseBoundary(
199 destination: Destination,
227 - responseState: ResponseState,
200 + renderState: RenderState,
201 ): boolean {
229 - if (responseState.generateStaticMarkup) {
202 + if (renderState.generateStaticMarkup) {
203 return true;
204 }
232 - return writeEndClientRenderedSuspenseBoundaryImpl(destination, responseState);
205 + return writeEndClientRenderedSuspenseBoundaryImpl(destination, renderState);
206 }
207
208 export type TransitionStatus = FormStatus;
packages/react-dom/npm/server.browser.js
+3
@@ -15,3 +15,6 @@ exports.renderToStaticMarkup = l.renderToStaticMarkup;
15 exports.renderToNodeStream = l.renderToNodeStream;
16 exports.renderToStaticNodeStream = l.renderToStaticNodeStream;
17 exports.renderToReadableStream = s.renderToReadableStream;
18 +if (s.resume) {
19 + exports.resume = s.resume;
20 +}
packages/react-dom/npm/server.bun.js
+3
@@ -12,6 +12,9 @@ if (process.env.NODE_ENV === 'production') {
12
13 exports.version = b.version;
14 exports.renderToReadableStream = b.renderToReadableStream;
15 +if (b.resume) {
16 + exports.resume = b.resume;
17 +}
18 exports.renderToNodeStream = b.renderToNodeStream;
19 exports.renderToStaticNodeStream = b.renderToStaticNodeStream;
20 exports.renderToString = l.renderToString;
packages/react-dom/npm/server.edge.js
+3
@@ -16,3 +16,6 @@ exports.renderToNodeStream = b.renderToNodeStream;
16 exports.renderToStaticNodeStream = b.renderToStaticNodeStream;
17 exports.renderToString = l.renderToString;
18 exports.renderToStaticMarkup = l.renderToStaticMarkup;
19 +if (b.resume) {
20 + exports.resume = b.resume;
21 +}
packages/react-dom/npm/server.node.js
+3
@@ -15,3 +15,6 @@ exports.renderToStaticMarkup = l.renderToStaticMarkup;
15 exports.renderToNodeStream = l.renderToNodeStream;
16 exports.renderToStaticNodeStream = l.renderToStaticNodeStream;
17 exports.renderToPipeableStream = s.renderToPipeableStream;
18 +if (s.resume) {
19 + exports.resume = s.resume;
20 +}
packages/react-dom/server.browser.js
+8 -1
@@ -37,7 +37,14 @@ export function renderToStaticNodeStream() {
37 }
38
39 export function renderToReadableStream() {
40 - return require('./src/server/ReactDOMFizzServerBrowser').renderToReadableStream.apply(
40 + return require('./src/server/react-dom-server.browser').renderToReadableStream.apply(
41 + this,
42 + arguments,
43 + );
44 +}
45 +
46 +export function resume() {
47 + return require('./src/server/react-dom-server.browser').resume.apply(
48 this,
49 arguments,
50 );
packages/react-dom/server.bun.js
+10 -3
@@ -12,21 +12,21 @@ import ReactVersion from 'shared/ReactVersion';
12 export {ReactVersion as version};
13
14 export function renderToReadableStream() {
15 - return require('./src/server/ReactDOMFizzServerBun').renderToReadableStream.apply(
15 + return require('./src/server/react-dom-server.bun').renderToReadableStream.apply(
16 this,
17 arguments,
18 );
19 }
20
21 export function renderToNodeStream() {
22 - return require('./src/server/ReactDOMFizzServerBun').renderToNodeStream.apply(
22 + return require('./src/server/react-dom-server.bun').renderToNodeStream.apply(
23 this,
24 arguments,
25 );
26 }
27
28 export function renderToStaticNodeStream() {
29 - return require('./src/server/ReactDOMFizzServerBun').renderToStaticNodeStream.apply(
29 + return require('./src/server/react-dom-server.bun').renderToStaticNodeStream.apply(
30 this,
31 arguments,
32 );
@@ -45,3 +45,10 @@ export function renderToStaticMarkup() {
45 arguments,
46 );
47 }
48 +
49 +export function resume() {
50 + return require('./src/server/react-dom-server.bun').resume.apply(
51 + this,
52 + arguments,
53 + );
54 +}
packages/react-dom/server.edge.js
+10 -3
@@ -12,21 +12,21 @@ import ReactVersion from 'shared/ReactVersion';
12 export {ReactVersion as version};
13
14 export function renderToReadableStream() {
15 - return require('./src/server/ReactDOMFizzServerEdge').renderToReadableStream.apply(
15 + return require('./src/server/react-dom-server.edge').renderToReadableStream.apply(
16 this,
17 arguments,
18 );
19 }
20
21 export function renderToNodeStream() {
22 - return require('./src/server/ReactDOMFizzServerEdge').renderToNodeStream.apply(
22 + return require('./src/server/react-dom-server.edge').renderToNodeStream.apply(
23 this,
24 arguments,
25 );
26 }
27
28 export function renderToStaticNodeStream() {
29 - return require('./src/server/ReactDOMFizzServerEdge').renderToStaticNodeStream.apply(
29 + return require('./src/server/react-dom-server.edge').renderToStaticNodeStream.apply(
30 this,
31 arguments,
32 );
@@ -45,3 +45,10 @@ export function renderToStaticMarkup() {
45 arguments,
46 );
47 }
48 +
49 +export function resume() {
50 + return require('./src/server/react-dom-server.edge').resume.apply(
51 + this,
52 + arguments,
53 + );
54 +}
packages/react-dom/server.node.js
+8 -1
@@ -37,7 +37,14 @@ export function renderToStaticNodeStream() {
37 }
38
39 export function renderToPipeableStream() {
40 - return require('./src/server/ReactDOMFizzServerNode').renderToPipeableStream.apply(
40 + return require('./src/server/react-dom-server.node').renderToPipeableStream.apply(
41 + this,
42 + arguments,
43 + );
44 +}
45 +
46 +export function resume() {
47 + return require('./src/server/react-dom-server.node').resume.apply(
48 this,
49 arguments,
50 );
packages/react-dom/src/__tests__/ReactDOMFizzStatic-test.js
+3 -1
@@ -218,12 +218,14 @@ describe('ReactDOMFizzStatic', () => {
218 );
219 }
220
221 - const promise = ReactDOMFizzStatic.prerenderToNodeStreams(<App />);
221 + const promise = ReactDOMFizzStatic.prerenderToNodeStream(<App />);
222
223 resolveText('Hello');
224
225 const result = await promise;
226
227 + expect(result.postponed).toBe(null);
228 +
229 await act(async () => {
230 result.prelude.pipe(writable);
231 });
packages/react-dom/src/__tests__/ReactDOMFizzStaticNode-test.js
+14 -14
@@ -47,8 +47,8 @@ describe('ReactDOMFizzStaticNode', () => {
47 }
48
49 // @gate experimental
50 - it('should call prerenderToNodeStreams', async () => {
51 - const result = await ReactDOMFizzStatic.prerenderToNodeStreams(
50 + it('should call prerenderToNodeStream', async () => {
51 + const result = await ReactDOMFizzStatic.prerenderToNodeStream(
52 <div>hello world</div>,
53 );
54 const prelude = await readContent(result.prelude);
@@ -57,7 +57,7 @@ describe('ReactDOMFizzStaticNode', () => {
57
58 // @gate experimental
59 it('should emit DOCTYPE at the root of the document', async () => {
60 - const result = await ReactDOMFizzStatic.prerenderToNodeStreams(
60 + const result = await ReactDOMFizzStatic.prerenderToNodeStream(
61 <html>
62 <body>hello world</body>
63 </html>,
@@ -76,7 +76,7 @@ describe('ReactDOMFizzStaticNode', () => {
76
77 // @gate experimental
78 it('should emit bootstrap script src at the end', async () => {
79 - const result = await ReactDOMFizzStatic.prerenderToNodeStreams(
79 + const result = await ReactDOMFizzStatic.prerenderToNodeStream(
80 <div>hello world</div>,
81 {
82 bootstrapScriptContent: 'INIT();',
@@ -101,7 +101,7 @@ describe('ReactDOMFizzStaticNode', () => {
101 }
102 return 'Done';
103 }
104 - const resultPromise = ReactDOMFizzStatic.prerenderToNodeStreams(
104 + const resultPromise = ReactDOMFizzStatic.prerenderToNodeStream(
105 <div>
106 <Suspense fallback="Loading">
107 <Wait />
@@ -127,7 +127,7 @@ describe('ReactDOMFizzStaticNode', () => {
127 const reportedErrors = [];
128 let caughtError = null;
129 try {
130 - await ReactDOMFizzStatic.prerenderToNodeStreams(
130 + await ReactDOMFizzStatic.prerenderToNodeStream(
131 <div>
132 <Throw />
133 </div>,
@@ -149,7 +149,7 @@ describe('ReactDOMFizzStaticNode', () => {
149 const reportedErrors = [];
150 let caughtError = null;
151 try {
152 - await ReactDOMFizzStatic.prerenderToNodeStreams(
152 + await ReactDOMFizzStatic.prerenderToNodeStream(
153 <div>
154 <Suspense fallback={<Throw />}>
155 <InfiniteSuspend />
@@ -171,7 +171,7 @@ describe('ReactDOMFizzStaticNode', () => {
171 // @gate experimental
172 it('should not error the stream when an error is thrown inside suspense boundary', async () => {
173 const reportedErrors = [];
174 - const result = await ReactDOMFizzStatic.prerenderToNodeStreams(
174 + const result = await ReactDOMFizzStatic.prerenderToNodeStream(
175 <div>
176 <Suspense fallback={<div>Loading</div>}>
177 <Throw />
@@ -193,7 +193,7 @@ describe('ReactDOMFizzStaticNode', () => {
193 it('should be able to complete by aborting even if the promise never resolves', async () => {
194 const errors = [];
195 const controller = new AbortController();
196 - const resultPromise = ReactDOMFizzStatic.prerenderToNodeStreams(
196 + const resultPromise = ReactDOMFizzStatic.prerenderToNodeStream(
197 <div>
198 <Suspense fallback={<div>Loading</div>}>
199 <InfiniteSuspend />
@@ -223,7 +223,7 @@ describe('ReactDOMFizzStaticNode', () => {
223 it('should reject if aborting before the shell is complete', async () => {
224 const errors = [];
225 const controller = new AbortController();
226 - const promise = ReactDOMFizzStatic.prerenderToNodeStreams(
226 + const promise = ReactDOMFizzStatic.prerenderToNodeStream(
227 <div>
228 <InfiniteSuspend />
229 </div>,
@@ -262,7 +262,7 @@ describe('ReactDOMFizzStaticNode', () => {
262 </Suspense>
263 );
264 }
265 - const streamPromise = ReactDOMFizzStatic.prerenderToNodeStreams(
265 + const streamPromise = ReactDOMFizzStatic.prerenderToNodeStream(
266 <div>
267 <App />
268 </div>,
@@ -291,7 +291,7 @@ describe('ReactDOMFizzStaticNode', () => {
291 const theReason = new Error('aborted for reasons');
292 controller.abort(theReason);
293
294 - const promise = ReactDOMFizzStatic.prerenderToNodeStreams(
294 + const promise = ReactDOMFizzStatic.prerenderToNodeStream(
295 <div>
296 <Suspense fallback={<div>Loading</div>}>
297 <InfiniteSuspend />
@@ -342,7 +342,7 @@ describe('ReactDOMFizzStaticNode', () => {
342
343 const errors = [];
344 const controller = new AbortController();
345 - const resultPromise = ReactDOMFizzStatic.prerenderToNodeStreams(<App />, {
345 + const resultPromise = ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
346 signal: controller.signal,
347 onError(x) {
348 errors.push(x);
@@ -384,7 +384,7 @@ describe('ReactDOMFizzStaticNode', () => {
384
385 const errors = [];
386 const controller = new AbortController();
387 - const resultPromise = ReactDOMFizzStatic.prerenderToNodeStreams(<App />, {
387 + const resultPromise = ReactDOMFizzStatic.prerenderToNodeStream(<App />, {
388 signal: controller.signal,
389 onError(x) {
390 errors.push(x.message);
packages/react-dom/src/server/ReactDOMFizzServerBrowser.js
+92 -14
@@ -7,6 +7,7 @@
7 * @flow
8 */
9
10 +import type {PostponedState} from 'react-server/src/ReactFizzServer';
11 import type {ReactNodeList} from 'shared/ReactTypes';
12 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
13
@@ -20,8 +21,8 @@ import {
21 } from 'react-server/src/ReactFizzServer';
22
23 import {
23 - createResources,
24 - createResponseState,
24 + createResumableState,
25 + createRenderState,
26 createRootFormatContext,
27 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
28
@@ -39,6 +40,14 @@ type Options = {
40 unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
41 };
42
43 +type ResumeOptions = {
44 + nonce?: string,
45 + signal?: AbortSignal,
46 + onError?: (error: mixed) => ?string,
47 + onPostpone?: (reason: string) => void,
48 + unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
49 +};
50 +
51 // TODO: Move to sub-classing ReadableStream.
52 type ReactDOMServerReadableStream = ReadableStream & {
53 allReady: Promise<void>,
@@ -81,19 +90,18 @@ function renderToReadableStream(
90 allReady.catch(() => {});
91 reject(error);
92 }
84 - const resources = createResources();
93 + const resumableState = createResumableState(
94 + options ? options.identifierPrefix : undefined,
95 + options ? options.nonce : undefined,
96 + options ? options.bootstrapScriptContent : undefined,
97 + options ? options.bootstrapScripts : undefined,
98 + options ? options.bootstrapModules : undefined,
99 + options ? options.unstable_externalRuntimeSrc : undefined,
100 + );
101 const request = createRequest(
102 children,
87 - resources,
88 - createResponseState(
89 - resources,
90 - options ? options.identifierPrefix : undefined,
91 - options ? options.nonce : undefined,
92 - options ? options.bootstrapScriptContent : undefined,
93 - options ? options.bootstrapScripts : undefined,
94 - options ? options.bootstrapModules : undefined,
95 - options ? options.unstable_externalRuntimeSrc : undefined,
96 - ),
103 + resumableState,
104 + createRenderState(resumableState, options ? options.nonce : undefined),
105 createRootFormatContext(options ? options.namespaceURI : undefined),
106 options ? options.progressiveChunkSize : undefined,
107 options ? options.onError : undefined,
@@ -119,4 +127,74 @@ function renderToReadableStream(
127 });
128 }
129
122 -export {renderToReadableStream, ReactVersion as version};
130 +function resume(
131 + children: ReactNodeList,
132 + postponedState: PostponedState,
133 + options?: ResumeOptions,
134 +): Promise<ReactDOMServerReadableStream> {
135 + return new Promise((resolve, reject) => {
136 + let onFatalError;
137 + let onAllReady;
138 + const allReady = new Promise<void>((res, rej) => {
139 + onAllReady = res;
140 + onFatalError = rej;
141 + });
142 +
143 + function onShellReady() {
144 + const stream: ReactDOMServerReadableStream = (new ReadableStream(
145 + {
146 + type: 'bytes',
147 + pull: (controller): ?Promise<void> => {
148 + startFlowing(request, controller);
149 + },
150 + cancel: (reason): ?Promise<void> => {
151 + abort(request);
152 + },
153 + },
154 + // $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
155 + {highWaterMark: 0},
156 + ): any);
157 + // TODO: Move to sub-classing ReadableStream.
158 + stream.allReady = allReady;
159 + resolve(stream);
160 + }
161 + function onShellError(error: mixed) {
162 + // If the shell errors the caller of `renderToReadableStream` won't have access to `allReady`.
163 + // However, `allReady` will be rejected by `onFatalError` as well.
164 + // So we need to catch the duplicate, uncatchable fatal error in `allReady` to prevent a `UnhandledPromiseRejection`.
165 + allReady.catch(() => {});
166 + reject(error);
167 + }
168 + const request = createRequest(
169 + children,
170 + postponedState.resumableState,
171 + createRenderState(
172 + postponedState.resumableState,
173 + options ? options.nonce : undefined,
174 + ),
175 + postponedState.rootFormatContext,
176 + postponedState.progressiveChunkSize,
177 + options ? options.onError : undefined,
178 + onAllReady,
179 + onShellReady,
180 + onShellError,
181 + onFatalError,
182 + options ? options.onPostpone : undefined,
183 + );
184 + if (options && options.signal) {
185 + const signal = options.signal;
186 + if (signal.aborted) {
187 + abort(request, (signal: any).reason);
188 + } else {
189 + const listener = () => {
190 + abort(request, (signal: any).reason);
191 + signal.removeEventListener('abort', listener);
192 + };
193 + signal.addEventListener('abort', listener);
194 + }
195 + }
196 + startWork(request);
197 + });
198 +}
199 +
200 +export {renderToReadableStream, resume, ReactVersion as version};
packages/react-dom/src/server/ReactDOMFizzServerBun.js
+12 -13
@@ -20,8 +20,8 @@ import {
20 } from 'react-server/src/ReactFizzServer';
21
22 import {
23 - createResources,
24 - createResponseState,
23 + createResumableState,
24 + createRenderState,
25 createRootFormatContext,
26 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
27
@@ -82,19 +82,18 @@ function renderToReadableStream(
82 allReady.catch(() => {});
83 reject(error);
84 }
85 - const resources = createResources();
85 + const resumableState = createResumableState(
86 + options ? options.identifierPrefix : undefined,
87 + options ? options.nonce : undefined,
88 + options ? options.bootstrapScriptContent : undefined,
89 + options ? options.bootstrapScripts : undefined,
90 + options ? options.bootstrapModules : undefined,
91 + options ? options.unstable_externalRuntimeSrc : undefined,
92 + );
93 const request = createRequest(
94 children,
88 - resources,
89 - createResponseState(
90 - resources,
91 - options ? options.identifierPrefix : undefined,
92 - options ? options.nonce : undefined,
93 - options ? options.bootstrapScriptContent : undefined,
94 - options ? options.bootstrapScripts : undefined,
95 - options ? options.bootstrapModules : undefined,
96 - options ? options.unstable_externalRuntimeSrc : undefined,
97 - ),
95 + resumableState,
96 + createRenderState(resumableState, options ? options.nonce : undefined),
97 createRootFormatContext(options ? options.namespaceURI : undefined),
98 options ? options.progressiveChunkSize : undefined,
99 options ? options.onError : undefined,
packages/react-dom/src/server/ReactDOMFizzServerEdge.js
+92 -14
@@ -7,6 +7,7 @@
7 * @flow
8 */
9
10 +import type {PostponedState} from 'react-server/src/ReactFizzServer';
11 import type {ReactNodeList} from 'shared/ReactTypes';
12 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
13
@@ -20,8 +21,8 @@ import {
21 } from 'react-server/src/ReactFizzServer';
22
23 import {
23 - createResources,
24 - createResponseState,
24 + createResumableState,
25 + createRenderState,
26 createRootFormatContext,
27 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
28
@@ -39,6 +40,14 @@ type Options = {
40 unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
41 };
42
43 +type ResumeOptions = {
44 + nonce?: string,
45 + signal?: AbortSignal,
46 + onError?: (error: mixed) => ?string,
47 + onPostpone?: (reason: string) => void,
48 + unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
49 +};
50 +
51 // TODO: Move to sub-classing ReadableStream.
52 type ReactDOMServerReadableStream = ReadableStream & {
53 allReady: Promise<void>,
@@ -81,19 +90,18 @@ function renderToReadableStream(
90 allReady.catch(() => {});
91 reject(error);
92 }
84 - const resources = createResources();
93 + const resumableState = createResumableState(
94 + options ? options.identifierPrefix : undefined,
95 + options ? options.nonce : undefined,
96 + options ? options.bootstrapScriptContent : undefined,
97 + options ? options.bootstrapScripts : undefined,
98 + options ? options.bootstrapModules : undefined,
99 + options ? options.unstable_externalRuntimeSrc : undefined,
100 + );
101 const request = createRequest(
102 children,
87 - resources,
88 - createResponseState(
89 - resources,
90 - options ? options.identifierPrefix : undefined,
91 - options ? options.nonce : undefined,
92 - options ? options.bootstrapScriptContent : undefined,
93 - options ? options.bootstrapScripts : undefined,
94 - options ? options.bootstrapModules : undefined,
95 - options ? options.unstable_externalRuntimeSrc : undefined,
96 - ),
103 + resumableState,
104 + createRenderState(resumableState, options ? options.nonce : undefined),
105 createRootFormatContext(options ? options.namespaceURI : undefined),
106 options ? options.progressiveChunkSize : undefined,
107 options ? options.onError : undefined,
@@ -119,4 +127,74 @@ function renderToReadableStream(
127 });
128 }
129
122 -export {renderToReadableStream, ReactVersion as version};
130 +function resume(
131 + children: ReactNodeList,
132 + postponedState: PostponedState,
133 + options?: ResumeOptions,
134 +): Promise<ReactDOMServerReadableStream> {
135 + return new Promise((resolve, reject) => {
136 + let onFatalError;
137 + let onAllReady;
138 + const allReady = new Promise<void>((res, rej) => {
139 + onAllReady = res;
140 + onFatalError = rej;
141 + });
142 +
143 + function onShellReady() {
144 + const stream: ReactDOMServerReadableStream = (new ReadableStream(
145 + {
146 + type: 'bytes',
147 + pull: (controller): ?Promise<void> => {
148 + startFlowing(request, controller);
149 + },
150 + cancel: (reason): ?Promise<void> => {
151 + abort(request);
152 + },
153 + },
154 + // $FlowFixMe[prop-missing] size() methods are not allowed on byte streams.
155 + {highWaterMark: 0},
156 + ): any);
157 + // TODO: Move to sub-classing ReadableStream.
158 + stream.allReady = allReady;
159 + resolve(stream);
160 + }
161 + function onShellError(error: mixed) {
162 + // If the shell errors the caller of `renderToReadableStream` won't have access to `allReady`.
163 + // However, `allReady` will be rejected by `onFatalError` as well.
164 + // So we need to catch the duplicate, uncatchable fatal error in `allReady` to prevent a `UnhandledPromiseRejection`.
165 + allReady.catch(() => {});
166 + reject(error);
167 + }
168 + const request = createRequest(
169 + children,
170 + postponedState.resumableState,
171 + createRenderState(
172 + postponedState.resumableState,
173 + options ? options.nonce : undefined,
174 + ),
175 + postponedState.rootFormatContext,
176 + postponedState.progressiveChunkSize,
177 + options ? options.onError : undefined,
178 + onAllReady,
179 + onShellReady,
180 + onShellError,
181 + onFatalError,
182 + options ? options.onPostpone : undefined,
183 + );
184 + if (options && options.signal) {
185 + const signal = options.signal;
186 + if (signal.aborted) {
187 + abort(request, (signal: any).reason);
188 + } else {
189 + const listener = () => {
190 + abort(request, (signal: any).reason);
191 + signal.removeEventListener('abort', listener);
192 + };
193 + signal.addEventListener('abort', listener);
194 + }
195 + }
196 + startWork(request);
197 + });
198 +}
199 +
200 +export {renderToReadableStream, resume, ReactVersion as version};
packages/react-dom/src/server/ReactDOMFizzServerNode.js
+87 -15
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import type {Request} from 'react-server/src/ReactFizzServer';
10 +import type {Request, PostponedState} from 'react-server/src/ReactFizzServer';
11 import type {ReactNodeList} from 'shared/ReactTypes';
12 import type {Writable} from 'stream';
13 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
@@ -23,8 +23,8 @@ import {
23 } from 'react-server/src/ReactFizzServer';
24
25 import {
26 - createResources,
27 - createResponseState,
26 + createResumableState,
27 + createRenderState,
28 createRootFormatContext,
29 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
30
@@ -53,6 +53,15 @@ type Options = {
53 unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
54 };
55
56 +type ResumeOptions = {
57 + nonce?: string,
58 + onShellReady?: () => void,
59 + onShellError?: (error: mixed) => void,
60 + onAllReady?: () => void,
61 + onError?: (error: mixed) => ?string,
62 + onPostpone?: (reason: string) => void,
63 +};
64 +
65 type PipeableStream = {
66 // Cancel any pending I/O and put anything remaining into
67 // client rendered mode.
@@ -61,19 +70,18 @@ type PipeableStream = {
70 };
71
72 function createRequestImpl(children: ReactNodeList, options: void | Options) {
64 - const resources = createResources();
73 + const resumableState = createResumableState(
74 + options ? options.identifierPrefix : undefined,
75 + options ? options.nonce : undefined,
76 + options ? options.bootstrapScriptContent : undefined,
77 + options ? options.bootstrapScripts : undefined,
78 + options ? options.bootstrapModules : undefined,
79 + options ? options.unstable_externalRuntimeSrc : undefined,
80 + );
81 return createRequest(
82 children,
67 - resources,
68 - createResponseState(
69 - resources,
70 - options ? options.identifierPrefix : undefined,
71 - options ? options.nonce : undefined,
72 - options ? options.bootstrapScriptContent : undefined,
73 - options ? options.bootstrapScripts : undefined,
74 - options ? options.bootstrapModules : undefined,
75 - options ? options.unstable_externalRuntimeSrc : undefined,
76 - ),
83 + resumableState,
84 + createRenderState(resumableState, options ? options.nonce : undefined),
85 createRootFormatContext(options ? options.namespaceURI : undefined),
86 options ? options.progressiveChunkSize : undefined,
87 options ? options.onError : undefined,
@@ -121,4 +129,68 @@ function renderToPipeableStream(
129 };
130 }
131
124 -export {renderToPipeableStream, ReactVersion as version};
132 +function resumeRequestImpl(
133 + children: ReactNodeList,
134 + postponedState: PostponedState,
135 + options: void | ResumeOptions,
136 +) {
137 + return createRequest(
138 + children,
139 + postponedState.resumableState,
140 + createRenderState(
141 + postponedState.resumableState,
142 + options ? options.nonce : undefined,
143 + ),
144 + postponedState.rootFormatContext,
145 + postponedState.progressiveChunkSize,
146 + options ? options.onError : undefined,
147 + options ? options.onAllReady : undefined,
148 + options ? options.onShellReady : undefined,
149 + options ? options.onShellError : undefined,
150 + undefined,
151 + options ? options.onPostpone : undefined,
152 + );
153 +}
154 +
155 +function resumeToPipeableStream(
156 + children: ReactNodeList,
157 + postponedState: PostponedState,
158 + options?: ResumeOptions,
159 +): PipeableStream {
160 + const request = resumeRequestImpl(children, postponedState, options);
161 + let hasStartedFlowing = false;
162 + startWork(request);
163 + return {
164 + pipe<T: Writable>(destination: T): T {
165 + if (hasStartedFlowing) {
166 + throw new Error(
167 + 'React currently only supports piping to one writable stream.',
168 + );
169 + }
170 + hasStartedFlowing = true;
171 + startFlowing(request, destination);
172 + destination.on('drain', createDrainHandler(destination, request));
173 + destination.on(
174 + 'error',
175 + createAbortHandler(
176 + request,
177 + 'The destination stream errored while writing data.',
178 + ),
179 + );
180 + destination.on(
181 + 'close',
182 + createAbortHandler(request, 'The destination stream closed early.'),
183 + );
184 + return destination;
185 + },
186 + abort(reason: mixed) {
187 + abort(request, reason);
188 + },
189 + };
190 +}
191 +
192 +export {
193 + renderToPipeableStream,
194 + resumeToPipeableStream,
195 + ReactVersion as version,
196 +};
packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js
+15 -12
@@ -9,6 +9,7 @@
9
10 import type {ReactNodeList} from 'shared/ReactTypes';
11 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
12 +import type {PostponedState} from 'react-server/src/ReactFizzServer';
13
14 import ReactVersion from 'shared/ReactVersion';
15
@@ -17,11 +18,12 @@ import {
18 startWork,
19 startFlowing,
20 abort,
21 + getPostponedState,
22 } from 'react-server/src/ReactFizzServer';
23
24 import {
23 - createResources,
24 - createResponseState,
25 + createResumableState,
26 + createRenderState,
27 createRootFormatContext,
28 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
29
@@ -39,6 +41,7 @@ type Options = {
41 };
42
43 type StaticResult = {
44 + postponed: null | PostponedState,
45 prelude: ReadableStream,
46 };
47
@@ -62,23 +65,23 @@ function prerender(
65 );
66
67 const result = {
68 + postponed: getPostponedState(request),
69 prelude: stream,
70 };
71 resolve(result);
72 }
69 - const resources = createResources();
73 + const resources = createResumableState(
74 + options ? options.identifierPrefix : undefined,
75 + undefined, // nonce is not compatible with prerendered bootstrap scripts
76 + options ? options.bootstrapScriptContent : undefined,
77 + options ? options.bootstrapScripts : undefined,
78 + options ? options.bootstrapModules : undefined,
79 + options ? options.unstable_externalRuntimeSrc : undefined,
80 + );
81 const request = createRequest(
82 children,
83 resources,
73 - createResponseState(
74 - resources,
75 - options ? options.identifierPrefix : undefined,
76 - undefined,
77 - options ? options.bootstrapScriptContent : undefined,
78 - options ? options.bootstrapScripts : undefined,
79 - options ? options.bootstrapModules : undefined,
80 - options ? options.unstable_externalRuntimeSrc : undefined,
81 - ),
84 + createRenderState(resources, undefined),
85 createRootFormatContext(options ? options.namespaceURI : undefined),
86 options ? options.progressiveChunkSize : undefined,
87 options ? options.onError : undefined,
packages/react-dom/src/server/ReactDOMFizzStaticEdge.js
+15 -12
@@ -9,6 +9,7 @@
9
10 import type {ReactNodeList} from 'shared/ReactTypes';
11 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
12 +import type {PostponedState} from 'react-server/src/ReactFizzServer';
13
14 import ReactVersion from 'shared/ReactVersion';
15
@@ -17,11 +18,12 @@ import {
18 startWork,
19 startFlowing,
20 abort,
21 + getPostponedState,
22 } from 'react-server/src/ReactFizzServer';
23
24 import {
23 - createResources,
24 - createResponseState,
25 + createResumableState,
26 + createRenderState,
27 createRootFormatContext,
28 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
29
@@ -39,6 +41,7 @@ type Options = {
41 };
42
43 type StaticResult = {
44 + postponed: null | PostponedState,
45 prelude: ReadableStream,
46 };
47
@@ -62,23 +65,23 @@ function prerender(
65 );
66
67 const result = {
68 + postponed: getPostponedState(request),
69 prelude: stream,
70 };
71 resolve(result);
72 }
69 - const resources = createResources();
73 + const resources = createResumableState(
74 + options ? options.identifierPrefix : undefined,
75 + undefined, // nonce is not compatible with prerendered bootstrap scripts
76 + options ? options.bootstrapScriptContent : undefined,
77 + options ? options.bootstrapScripts : undefined,
78 + options ? options.bootstrapModules : undefined,
79 + options ? options.unstable_externalRuntimeSrc : undefined,
80 + );
81 const request = createRequest(
82 children,
83 resources,
73 - createResponseState(
74 - resources,
75 - options ? options.identifierPrefix : undefined,
76 - undefined,
77 - options ? options.bootstrapScriptContent : undefined,
78 - options ? options.bootstrapScripts : undefined,
79 - options ? options.bootstrapModules : undefined,
80 - options ? options.unstable_externalRuntimeSrc : undefined,
81 - ),
84 + createRenderState(resources, undefined),
85 createRootFormatContext(options ? options.namespaceURI : undefined),
86 options ? options.progressiveChunkSize : undefined,
87 options ? options.onError : undefined,
packages/react-dom/src/server/ReactDOMFizzStaticNode.js
+18 -15
@@ -9,6 +9,7 @@
9
10 import type {ReactNodeList} from 'shared/ReactTypes';
11 import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
12 +import type {PostponedState} from 'react-server/src/ReactFizzServer';
13
14 import {Writable, Readable} from 'stream';
15
@@ -19,11 +20,12 @@ import {
20 startWork,
21 startFlowing,
22 abort,
23 + getPostponedState,
24 } from 'react-server/src/ReactFizzServer';
25
26 import {
25 - createResources,
26 - createResponseState,
27 + createResumableState,
28 + createRenderState,
29 createRootFormatContext,
30 } from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
31
@@ -41,6 +43,7 @@ type Options = {
43 };
44
45 type StaticResult = {
46 + postponed: null | PostponedState,
47 prelude: Readable,
48 };
49
@@ -60,7 +63,7 @@ function createFakeWritable(readable: any): Writable {
63 }: any);
64 }
65
63 -function prerenderToNodeStreams(
66 +function prerenderToNodeStream(
67 children: ReactNodeList,
68 options?: Options,
69 ): Promise<StaticResult> {
@@ -76,23 +79,23 @@ function prerenderToNodeStreams(
79 const writable = createFakeWritable(readable);
80
81 const result = {
82 + postponed: getPostponedState(request),
83 prelude: readable,
84 };
85 resolve(result);
86 }
83 - const resources = createResources();
87 + const resumableState = createResumableState(
88 + options ? options.identifierPrefix : undefined,
89 + undefined, // nonce is not compatible with prerendered bootstrap scripts
90 + options ? options.bootstrapScriptContent : undefined,
91 + options ? options.bootstrapScripts : undefined,
92 + options ? options.bootstrapModules : undefined,
93 + options ? options.unstable_externalRuntimeSrc : undefined,
94 + );
95 const request = createRequest(
96 children,
86 - resources,
87 - createResponseState(
88 - resources,
89 - options ? options.identifierPrefix : undefined,
90 - undefined,
91 - options ? options.bootstrapScriptContent : undefined,
92 - options ? options.bootstrapScripts : undefined,
93 - options ? options.bootstrapModules : undefined,
94 - options ? options.unstable_externalRuntimeSrc : undefined,
95 - ),
97 + resumableState,
98 + createRenderState(resumableState, undefined),
99 createRootFormatContext(options ? options.namespaceURI : undefined),
100 options ? options.progressiveChunkSize : undefined,
101 options ? options.onError : undefined,
@@ -118,4 +121,4 @@ function prerenderToNodeStreams(
121 });
122 }
123
121 -export {prerenderToNodeStreams, ReactVersion as version};
124 +export {prerenderToNodeStream, ReactVersion as version};
packages/react-dom/src/server/ReactDOMLegacyServerImpl.js
+12 -12
@@ -10,7 +10,6 @@
10 import ReactVersion from 'shared/ReactVersion';
11
12 import type {ReactNodeList} from 'shared/ReactTypes';
13 -import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
13
14 import {
15 createRequest,
@@ -20,8 +19,8 @@ import {
19 } from 'react-server/src/ReactFizzServer';
20
21 import {
23 - createResources,
24 - createResponseState,
22 + createResumableState,
23 + createRenderState,
24 createRootFormatContext,
25 } from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
26
@@ -38,7 +37,6 @@ function renderToStringImpl(
37 options: void | ServerOptions,
38 generateStaticMarkup: boolean,
39 abortReason: string,
41 - unstable_externalRuntimeSrc?: string | BootstrapScriptDescriptor,
40 ): string {
41 let didFatal = false;
42 let fatalError = null;
@@ -62,16 +60,18 @@ function renderToStringImpl(
60 function onShellReady() {
61 readyToStream = true;
62 }
65 - const resources = createResources();
63 + const resumableState = createResumableState(
64 + options ? options.identifierPrefix : undefined,
65 + undefined,
66 + undefined,
67 + undefined,
68 + undefined,
69 + undefined,
70 + );
71 const request = createRequest(
72 children,
68 - resources,
69 - createResponseState(
70 - resources,
71 - generateStaticMarkup,
72 - options ? options.identifierPrefix : undefined,
73 - unstable_externalRuntimeSrc,
74 - ),
73 + resumableState,
74 + createRenderState(resumableState, undefined, generateStaticMarkup),
75 createRootFormatContext(),
76 Infinity,
77 onError,
packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js
+12 -9
@@ -19,8 +19,8 @@ import {
19 } from 'react-server/src/ReactFizzServer';
20
21 import {
22 - createResources,
23 - createResponseState,
22 + createResumableState,
23 + createRenderState,
24 createRootFormatContext,
25 } from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
26
@@ -71,15 +71,18 @@ function renderToNodeStreamImpl(
71 startFlowing(request, destination);
72 }
73 const destination = new ReactMarkupReadableStream();
74 - const resources = createResources();
74 + const resumableState = createResumableState(
75 + options ? options.identifierPrefix : undefined,
76 + undefined,
77 + undefined,
78 + undefined,
79 + undefined,
80 + undefined,
81 + );
82 const request = createRequest(
83 children,
77 - resources,
78 - createResponseState(
79 - resources,
80 - false,
81 - options ? options.identifierPrefix : undefined,
82 - ),
84 + resumableState,
85 + createRenderState(resumableState, undefined, false),
86 createRootFormatContext(),
87 Infinity,
88 onError,
packages/react-dom/src/server/react-dom-server.browser.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export * from './ReactDOMFizzServerBrowser.js';
packages/react-dom/src/server/react-dom-server.browser.stable.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export {renderToReadableStream, version} from './ReactDOMFizzServerBrowser.js';
packages/react-dom/src/server/react-dom-server.bun.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export * from './ReactDOMFizzServerBun.js';
packages/react-dom/src/server/react-dom-server.bun.stable.js new
+15
@@ -0,0 +1,15 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export {
11 + renderToReadableStream,
12 + renderToNodeStream,
13 + renderToStaticNodeStream,
14 + version,
15 +} from './ReactDOMFizzServerBun.js';
packages/react-dom/src/server/react-dom-server.edge.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export * from './ReactDOMFizzServerEdge.js';
packages/react-dom/src/server/react-dom-server.edge.stable.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export {renderToReadableStream, version} from './ReactDOMFizzServerEdge.js';
packages/react-dom/src/server/react-dom-server.node.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export * from './ReactDOMFizzServerNode.js';
packages/react-dom/src/server/react-dom-server.node.stable.js new
+10
@@ -0,0 +1,10 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 + */
9 +
10 +export {renderToPipeableStream, version} from './ReactDOMFizzServerNode.js';
packages/react-dom/static.node.js
+1 -1
@@ -8,6 +8,6 @@
8 */
9
10 export {
11 - prerenderToNodeStreams,
11 + prerenderToNodeStream,
12 version,
13 } from './src/server/ReactDOMFizzStaticNode';
packages/react-noop-renderer/src/ReactNoopServer.js
+12 -16
@@ -51,7 +51,7 @@ type Destination = {
51 stack: Array<Segment | Instance | SuspenseInstance>,
52 };
53
54 -type Resources = null;
54 +type RenderState = null;
55 type BoundaryResources = null;
56
57 const POP = Buffer.from('/', 'utf8');
@@ -104,7 +104,7 @@ const ReactNoopServer = ReactFizzServer({
104 pushTextInstance(
105 target: Array<Uint8Array>,
106 text: string,
107 - responseState: ResponseState,
107 + renderState: RenderState,
108 textEmbedded: boolean,
109 ): boolean {
110 const textInstance: TextInstance = {
@@ -140,21 +140,21 @@ const ReactNoopServer = ReactFizzServer({
140 // This is a noop in ReactNoop
141 pushSegmentFinale(
142 target: Array<Uint8Array>,
143 - responseState: ResponseState,
143 + renderState: RenderState,
144 lastPushedText: boolean,
145 textEmbedded: boolean,
146 ): void {},
147
148 writeCompletedRoot(
149 destination: Destination,
150 - responseState: ResponseState,
150 + renderState: RenderState,
151 ): boolean {
152 return true;
153 },
154
155 writePlaceholder(
156 destination: Destination,
157 - responseState: ResponseState,
157 + renderState: RenderState,
158 id: number,
159 ): boolean {
160 const parent = destination.stack[destination.stack.length - 1];
@@ -166,7 +166,7 @@ const ReactNoopServer = ReactFizzServer({
166
167 writeStartCompletedSuspenseBoundary(
168 destination: Destination,
169 - responseState: ResponseState,
169 + renderState: RenderState,
170 suspenseInstance: SuspenseInstance,
171 ): boolean {
172 suspenseInstance.state = 'complete';
@@ -176,7 +176,7 @@ const ReactNoopServer = ReactFizzServer({
176 },
177 writeStartPendingSuspenseBoundary(
178 destination: Destination,
179 - responseState: ResponseState,
179 + renderState: RenderState,
180 suspenseInstance: SuspenseInstance,
181 ): boolean {
182 suspenseInstance.state = 'pending';
@@ -186,7 +186,7 @@ const ReactNoopServer = ReactFizzServer({
186 },
187 writeStartClientRenderedSuspenseBoundary(
188 destination: Destination,
189 - responseState: ResponseState,
189 + renderState: RenderState,
190 suspenseInstance: SuspenseInstance,
191 ): boolean {
192 suspenseInstance.state = 'client-render';
@@ -206,7 +206,7 @@ const ReactNoopServer = ReactFizzServer({
206
207 writeStartSegment(
208 destination: Destination,
209 - responseState: ResponseState,
209 + renderState: RenderState,
210 formatContext: null,
211 id: number,
212 ): boolean {
@@ -225,7 +225,7 @@ const ReactNoopServer = ReactFizzServer({
225
226 writeCompletedSegmentInstruction(
227 destination: Destination,
228 - responseState: ResponseState,
228 + renderState: RenderState,
229 contentSegmentID: number,
230 ): boolean {
231 const segment = destination.segments.get(contentSegmentID);
@@ -245,7 +245,7 @@ const ReactNoopServer = ReactFizzServer({
245
246 writeCompletedBoundaryInstruction(
247 destination: Destination,
248 - responseState: ResponseState,
248 + renderState: RenderState,
249 boundary: SuspenseInstance,
250 contentSegmentID: number,
251 ): boolean {
@@ -259,7 +259,7 @@ const ReactNoopServer = ReactFizzServer({
259
260 writeClientRenderBoundaryInstruction(
261 destination: Destination,
262 - responseState: ResponseState,
262 + renderState: RenderState,
263 boundary: SuspenseInstance,
264 ): boolean {
265 boundary.status = 'client-render';
@@ -269,10 +269,6 @@ const ReactNoopServer = ReactFizzServer({
269 writeHoistables() {},
270 writePostamble() {},
271
272 - createResources(): Resources {
273 - return null;
274 - },
275 -
272 createBoundaryResources(): BoundaryResources {
273 return null;
274 },
packages/react-server-dom-fb/src/ReactDOMServerFB.js
+12 -13
@@ -23,8 +23,8 @@ import {
23 } from 'react-server/src/ReactFizzServer';
24
25 import {
26 - createResources,
27 - createResponseState,
26 + createResumableState,
27 + createRenderState,
28 createRootFormatContext,
29 } from 'react-server/src/ReactFizzConfig';
30
@@ -50,19 +50,18 @@ function renderToStream(children: ReactNodeList, options: Options): Stream {
50 fatal: false,
51 error: null,
52 };
53 - const resources = createResources();
53 + const resumableState = createResumableState(
54 + options ? options.identifierPrefix : undefined,
55 + undefined,
56 + options ? options.bootstrapScriptContent : undefined,
57 + options ? options.bootstrapScripts : undefined,
58 + options ? options.bootstrapModules : undefined,
59 + options ? options.unstable_externalRuntimeSrc : undefined,
60 + );
61 const request = createRequest(
62 children,
56 - resources,
57 - createResponseState(
58 - resources,
59 - options ? options.identifierPrefix : undefined,
60 - undefined,
61 - options ? options.bootstrapScriptContent : undefined,
62 - options ? options.bootstrapScripts : undefined,
63 - options ? options.bootstrapModules : undefined,
64 - options ? options.unstable_externalRuntimeSrc : undefined,
65 - ),
63 + resumableState,
64 + createRenderState(resumableState, undefined),
65 createRootFormatContext(undefined),
66 options ? options.progressiveChunkSize : undefined,
67 options.onError,
packages/react-server/src/ReactFizzHooks.js
+8 -8
@@ -16,7 +16,7 @@ import type {
16 Usable,
17 } from 'shared/ReactTypes';
18
19 -import type {ResponseState} from './ReactFizzConfig';
19 +import type {ResumableState} from './ReactFizzConfig';
20 import type {Task} from './ReactFizzServer';
21 import type {ThenableState} from './ReactFizzThenable';
22 import type {TransitionStatus} from './ReactFizzConfig';
@@ -554,15 +554,15 @@ function useId(): string {
554 const task: Task = (currentlyRenderingTask: any);
555 const treeId = getTreeId(task.treeContext);
556
557 - const responseState = currentResponseState;
558 - if (responseState === null) {
557 + const resumableState = currentResumableState;
558 + if (resumableState === null) {
559 throw new Error(
560 'Invalid hook call. Hooks can only be called inside of the body of a function component.',
561 );
562 }
563
564 const localId = localIdCounter++;
565 - return makeId(responseState, treeId, localId);
565 + return makeId(resumableState, treeId, localId);
566 }
567
568 function use<T>(usable: Usable<T>): T {
@@ -652,9 +652,9 @@ if (enableAsyncActions) {
652 HooksDispatcher.useOptimistic = useOptimistic;
653 }
654
655 -export let currentResponseState: null | ResponseState = (null: any);
656 -export function setCurrentResponseState(
657 - responseState: null | ResponseState,
655 +export let currentResumableState: null | ResumableState = (null: any);
656 +export function setCurrentResumableState(
657 + resumableState: null | ResumableState,
658 ): void {
659 - currentResponseState = responseState;
659 + currentResumableState = resumableState;
660 }
packages/react-server/src/ReactFizzServer.js
+68 -53
@@ -23,9 +23,9 @@ import type {
23 import type {LazyComponent as LazyComponentType} from 'react/src/ReactLazy';
24 import type {
25 SuspenseBoundaryID,
26 - ResponseState,
26 + RenderState,
27 + ResumableState,
28 FormatContext,
28 - Resources,
29 BoundaryResources,
30 } from './ReactFizzConfig';
31 import type {ContextSnapshot} from './ReactFizzNewContext';
@@ -100,8 +100,8 @@ import {
100 checkDidRenderIdHook,
101 resetHooksState,
102 HooksDispatcher,
103 - currentResponseState,
104 - setCurrentResponseState,
103 + currentResumableState,
104 + setCurrentResumableState,
105 getThenableStateAfterSuspending,
106 unwrapThenable,
107 } from './ReactFizzHooks';
@@ -222,14 +222,14 @@ const CLOSED = 2;
222 export opaque type Request = {
223 destination: null | Destination,
224 flushScheduled: boolean,
225 - +responseState: ResponseState,
225 + +resumableState: ResumableState,
226 + +renderState: RenderState,
227 +progressiveChunkSize: number,
228 status: 0 | 1 | 2,
229 fatalError: mixed,
230 nextSegmentId: number,
231 allPendingTasks: number, // when it reaches zero, we can close the connection.
232 pendingRootTasks: number, // when this reaches zero, we've finished at least the root boundary.
232 - resources: Resources,
233 completedRootSegment: null | Segment, // Completed but not yet flushed root segments.
234 abortableTasks: Set<Task>,
235 pingedTasks: Array<Task>, // High priority tasks that should be worked on first.
@@ -283,8 +283,8 @@ function noop(): void {}
283
284 export function createRequest(
285 children: ReactNodeList,
286 - resources: Resources,
287 - responseState: ResponseState,
286 + resumableState: ResumableState,
287 + renderState: RenderState,
288 rootFormatContext: FormatContext,
289 progressiveChunkSize: void | number,
290 onError: void | ((error: mixed) => ?string),
@@ -300,7 +300,8 @@ export function createRequest(
300 const request: Request = {
301 destination: null,
302 flushScheduled: false,
303 - responseState,
303 + resumableState,
304 + renderState,
305 progressiveChunkSize:
306 progressiveChunkSize === undefined
307 ? DEFAULT_PROGRESSIVE_CHUNK_SIZE
@@ -310,7 +311,6 @@ export function createRequest(
311 nextSegmentId: 0,
312 allPendingTasks: 0,
313 pendingRootTasks: 0,
313 - resources,
314 completedRootSegment: null,
315 abortableTasks: abortSet,
316 pingedTasks: pingedTasks,
@@ -622,7 +622,7 @@ function renderSuspenseBoundary(
622 task.blockedSegment = contentRootSegment;
623 if (enableFloat) {
624 setCurrentlyRenderingBoundaryResourcesTarget(
625 - request.resources,
625 + request.renderState,
626 newBoundary.resources,
627 );
628 }
@@ -631,7 +631,7 @@ function renderSuspenseBoundary(
631 renderNode(request, task, content, 0);
632 pushSegmentFinale(
633 contentRootSegment.chunks,
634 - request.responseState,
634 + request.renderState,
635 contentRootSegment.lastPushedText,
636 contentRootSegment.textEmbedded,
637 );
@@ -672,7 +672,7 @@ function renderSuspenseBoundary(
672 } finally {
673 if (enableFloat) {
674 setCurrentlyRenderingBoundaryResourcesTarget(
675 - request.resources,
675 + request.renderState,
676 parentBoundary ? parentBoundary.resources : null,
677 );
678 }
@@ -734,8 +734,8 @@ function renderHostElement(
734 segment.chunks,
735 type,
736 props,
737 - request.resources,
738 - request.responseState,
737 + request.resumableState,
738 + request.renderState,
739 segment.formatContext,
740 segment.lastPushedText,
741 );
@@ -754,7 +754,7 @@ function renderHostElement(
754 segment.chunks,
755 type,
756 props,
757 - request.responseState,
757 + request.resumableState,
758 prevContext,
759 );
760 segment.lastPushedText = false;
@@ -1568,7 +1568,7 @@ function renderNodeDestructiveImpl(
1568 segment.lastPushedText = pushTextInstance(
1569 task.blockedSegment.chunks,
1570 node,
1571 - request.responseState,
1571 + request.renderState,
1572 segment.lastPushedText,
1573 );
1574 return;
@@ -1579,7 +1579,7 @@ function renderNodeDestructiveImpl(
1579 segment.lastPushedText = pushTextInstance(
1580 task.blockedSegment.chunks,
1581 '' + node,
1582 - request.responseState,
1582 + request.renderState,
1583 segment.lastPushedText,
1584 );
1585 return;
@@ -1975,7 +1975,7 @@ function retryTask(request: Request, task: Task): void {
1975 if (enableFloat) {
1976 const blockedBoundary = task.blockedBoundary;
1977 setCurrentlyRenderingBoundaryResourcesTarget(
1978 - request.resources,
1978 + request.renderState,
1979 blockedBoundary ? blockedBoundary.resources : null,
1980 );
1981 }
@@ -2009,7 +2009,7 @@ function retryTask(request: Request, task: Task): void {
2009 renderNodeDestructive(request, task, prevThenableState, task.node, 0);
2010 pushSegmentFinale(
2011 segment.chunks,
2012 - request.responseState,
2012 + request.renderState,
2013 segment.lastPushedText,
2014 segment.textEmbedded,
2015 );
@@ -2047,7 +2047,7 @@ function retryTask(request: Request, task: Task): void {
2047 }
2048 } finally {
2049 if (enableFloat) {
2050 - setCurrentlyRenderingBoundaryResourcesTarget(request.resources, null);
2050 + setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null);
2051 }
2052 if (__DEV__) {
2053 currentTaskInDEV = prevTaskInDEV;
@@ -2076,8 +2076,8 @@ export function performWork(request: Request): void {
2076 prevGetCurrentStackImpl = ReactDebugCurrentFrame.getCurrentStack;
2077 ReactDebugCurrentFrame.getCurrentStack = getCurrentStackInDEV;
2078 }
2079 - const prevResponseState = currentResponseState;
2080 - setCurrentResponseState(request.responseState);
2079 + const prevResumableState = currentResumableState;
2080 + setCurrentResumableState(request.resumableState);
2081 try {
2082 const pingedTasks = request.pingedTasks;
2083 let i;
@@ -2093,7 +2093,7 @@ export function performWork(request: Request): void {
2093 logRecoverableError(request, error);
2094 fatalError(request, error);
2095 } finally {
2096 - setCurrentResponseState(prevResponseState);
2096 + setCurrentResumableState(prevResumableState);
2097 ReactCurrentDispatcher.current = prevDispatcher;
2098 if (enableCache) {
2099 ReactCurrentCache.current = prevCacheDispatcher;
@@ -2130,7 +2130,7 @@ function flushSubtree(
2130 // When this segment finally completes it won't be embedded in text since it will flush separately
2131 segment.lastPushedText = false;
2132 segment.textEmbedded = false;
2133 - return writePlaceholder(destination, request.responseState, segmentID);
2133 + return writePlaceholder(destination, request.renderState, segmentID);
2134 }
2135 case COMPLETED: {
2136 segment.status = FLUSHED;
@@ -2183,7 +2183,7 @@ function flushSegment(
2183
2184 writeStartClientRenderedSuspenseBoundary(
2185 destination,
2186 - request.responseState,
2186 + request.renderState,
2187 boundary.errorDigest,
2188 boundary.errorMessage,
2189 boundary.errorComponentStack,
@@ -2193,7 +2193,7 @@ function flushSegment(
2193
2194 return writeEndClientRenderedSuspenseBoundary(
2195 destination,
2196 - request.responseState,
2196 + request.renderState,
2197 );
2198 } else if (boundary.pendingTasks > 0) {
2199 // This boundary is still loading. Emit a pending suspense boundary wrapper.
@@ -2206,14 +2206,17 @@ function flushSegment(
2206 }
2207
2208 /// This is the first time we should have referenced this ID.
2209 - const id = (boundary.id = assignSuspenseBoundaryID(request.responseState));
2209 + const id = (boundary.id = assignSuspenseBoundaryID(
2210 + request.renderState,
2211 + request.resumableState,
2212 + ));
2213
2211 - writeStartPendingSuspenseBoundary(destination, request.responseState, id);
2214 + writeStartPendingSuspenseBoundary(destination, request.renderState, id);
2215
2216 // Flush the fallback.
2217 flushSubtree(request, destination, segment);
2218
2216 - return writeEndPendingSuspenseBoundary(destination, request.responseState);
2219 + return writeEndPendingSuspenseBoundary(destination, request.renderState);
2220 } else if (boundary.byteSize > request.progressiveChunkSize) {
2221 // This boundary is large and will be emitted separately so that we can progressively show
2222 // other content. We add it to the queue during the flush because we have to ensure that
@@ -2228,20 +2231,20 @@ function flushSegment(
2231 // Emit a pending rendered suspense boundary wrapper.
2232 writeStartPendingSuspenseBoundary(
2233 destination,
2231 - request.responseState,
2234 + request.renderState,
2235 boundary.id,
2236 );
2237
2238 // Flush the fallback.
2239 flushSubtree(request, destination, segment);
2240
2238 - return writeEndPendingSuspenseBoundary(destination, request.responseState);
2241 + return writeEndPendingSuspenseBoundary(destination, request.renderState);
2242 } else {
2243 if (enableFloat) {
2241 - hoistResources(request.resources, boundary.resources);
2244 + hoistResources(request.renderState, boundary.resources);
2245 }
2246 // We can inline this boundary's content as a complete boundary.
2244 - writeStartCompletedSuspenseBoundary(destination, request.responseState);
2247 + writeStartCompletedSuspenseBoundary(destination, request.renderState);
2248
2249 const completedSegments = boundary.completedSegments;
2250
@@ -2254,10 +2257,7 @@ function flushSegment(
2257 const contentSegment = completedSegments[0];
2258 flushSegment(request, destination, contentSegment);
2259
2257 - return writeEndCompletedSuspenseBoundary(
2258 - destination,
2259 - request.responseState,
2260 - );
2260 + return writeEndCompletedSuspenseBoundary(destination, request.renderState);
2261 }
2262 }
2263
@@ -2268,7 +2268,8 @@ function flushClientRenderedBoundary(
2268 ): boolean {
2269 return writeClientRenderBoundaryInstruction(
2270 destination,
2271 - request.responseState,
2271 + request.resumableState,
2272 + request.renderState,
2273 boundary.id,
2274 boundary.errorDigest,
2275 boundary.errorMessage,
@@ -2283,7 +2284,7 @@ function flushSegmentContainer(
2284 ): boolean {
2285 writeStartSegment(
2286 destination,
2286 - request.responseState,
2287 + request.renderState,
2288 segment.formatContext,
2289 segment.id,
2290 );
@@ -2298,7 +2299,7 @@ function flushCompletedBoundary(
2299 ): boolean {
2300 if (enableFloat) {
2301 setCurrentlyRenderingBoundaryResourcesTarget(
2301 - request.resources,
2302 + request.renderState,
2303 boundary.resources,
2304 );
2305 }
@@ -2314,13 +2315,14 @@ function flushCompletedBoundary(
2315 writeResourcesForBoundary(
2316 destination,
2317 boundary.resources,
2317 - request.responseState,
2318 + request.renderState,
2319 );
2320 }
2321
2322 return writeCompletedBoundaryInstruction(
2323 destination,
2323 - request.responseState,
2324 + request.resumableState,
2325 + request.renderState,
2326 boundary.id,
2327 boundary.rootSegmentID,
2328 boundary.resources,
@@ -2334,7 +2336,7 @@ function flushPartialBoundary(
2336 ): boolean {
2337 if (enableFloat) {
2338 setCurrentlyRenderingBoundaryResourcesTarget(
2337 - request.resources,
2339 + request.renderState,
2340 boundary.resources,
2341 );
2342 }
@@ -2362,7 +2364,7 @@ function flushPartialBoundary(
2364 return writeResourcesForBoundary(
2365 destination,
2366 boundary.resources,
2365 - request.responseState,
2367 + request.renderState,
2368 );
2369 } else {
2370 return true;
@@ -2397,7 +2399,8 @@ function flushPartiallyCompletedSegment(
2399 flushSegmentContainer(request, destination, segment);
2400 return writeCompletedSegmentInstruction(
2401 destination,
2400 - request.responseState,
2402 + request.resumableState,
2403 + request.renderState,
2404 segmentID,
2405 );
2406 }
@@ -2421,15 +2424,15 @@ function flushCompletedQueues(
2424 if (enableFloat) {
2425 writePreamble(
2426 destination,
2424 - request.resources,
2425 - request.responseState,
2427 + request.resumableState,
2428 + request.renderState,
2429 request.allPendingTasks === 0,
2430 );
2431 }
2432
2433 flushSegment(request, destination, completedRootSegment);
2434 request.completedRootSegment = null;
2432 - writeCompletedRoot(destination, request.responseState);
2435 + writeCompletedRoot(destination, request.resumableState);
2436 } else {
2437 // We haven't flushed the root yet so we don't need to check any other branches further down
2438 return;
@@ -2440,7 +2443,7 @@ function flushCompletedQueues(
2443 }
2444
2445 if (enableFloat) {
2443 - writeHoistables(destination, request.resources, request.responseState);
2446 + writeHoistables(destination, request.resumableState, request.renderState);
2447 }
2448
2449 // We emit client rendering instructions for already emitted boundaries first.
@@ -2519,7 +2522,7 @@ function flushCompletedQueues(
2522 ) {
2523 request.flushScheduled = false;
2524 if (enableFloat) {
2522 - writePostamble(destination, request.responseState);
2525 + writePostamble(destination, request.resumableState);
2526 }
2527 completeWriting(destination);
2528 flushBuffered(destination);
@@ -2610,6 +2613,18 @@ export function flushResources(request: Request): void {
2613 enqueueFlush(request);
2614 }
2615
2613 -export function getResources(request: Request): Resources {
2614 - return request.resources;
2616 +export function getResumableState(request: Request): ResumableState {
2617 + return request.resumableState;
2618 +}
2619 +
2620 +export type PostponedState = {
2621 + nextSegmentId: number,
2622 + rootFormatContext: FormatContext,
2623 + progressiveChunkSize: number,
2624 + resumableState: ResumableState,
2625 +};
2626 +
2627 +// Returns the state of a postponed request or null if nothing was postponed.
2628 +export function getPostponedState(request: Request): null | PostponedState {
2629 + return null;
2630 }
packages/react-server/src/forks/ReactFizzConfig.custom.js
+2 -2
@@ -28,8 +28,8 @@ import type {TransitionStatus} from 'react-reconciler/src/ReactFiberConfig';
28
29 declare var $$$config: any;
30 export opaque type Destination = mixed; // eslint-disable-line no-undef
31 -export opaque type ResponseState = mixed;
32 -export opaque type Resources = mixed;
31 +export opaque type RenderState = mixed;
32 +export opaque type ResumableState = mixed;
33 export opaque type BoundaryResources = mixed;
34 export opaque type FormatContext = mixed;
35 export opaque type SuspenseBoundaryID = mixed;
scripts/rollup/bundles.js
+4 -4
@@ -233,7 +233,7 @@ const bundles = [
233 {
234 bundleTypes: [NODE_DEV, NODE_PROD, UMD_DEV, UMD_PROD],
235 moduleType: RENDERER,
236 - entry: 'react-dom/src/server/ReactDOMFizzServerBrowser.js',
236 + entry: 'react-dom/src/server/react-dom-server.browser.js',
237 name: 'react-dom-server.browser',
238 global: 'ReactDOMServer',
239 minifyWithProdErrorCodes: true,
@@ -243,7 +243,7 @@ const bundles = [
243 {
244 bundleTypes: [NODE_DEV, NODE_PROD],
245 moduleType: RENDERER,
246 - entry: 'react-dom/src/server/ReactDOMFizzServerNode.js',
246 + entry: 'react-dom/src/server/react-dom-server.node.js',
247 name: 'react-dom-server.node',
248 global: 'ReactDOMServer',
249 minifyWithProdErrorCodes: false,
@@ -264,7 +264,7 @@ const bundles = [
264 {
265 bundleTypes: [NODE_DEV, NODE_PROD],
266 moduleType: RENDERER,
267 - entry: 'react-dom/src/server/ReactDOMFizzServerEdge.js',
267 + entry: 'react-dom/src/server/react-dom-server.edge.js',
268 name: 'react-dom-server.edge', // 'node_modules/react/*.js',
269
270 global: 'ReactDOMServer',
@@ -277,7 +277,7 @@ const bundles = [
277 {
278 bundleTypes: [BUN_DEV, BUN_PROD],
279 moduleType: RENDERER,
280 - entry: 'react-dom/src/server/ReactDOMFizzServerBun.js',
280 + entry: 'react-dom/src/server/react-dom-server.bun.js',
281 name: 'react-dom-server.bun', // 'node_modules/react/*.js',
282
283 global: 'ReactDOMServer',
scripts/shared/inlinedHostConfigs.js
+10 -4
@@ -12,7 +12,7 @@ module.exports = [
12 entryPoints: [
13 'react-dom',
14 'react-dom/unstable_testing',
15 - 'react-dom/src/server/ReactDOMFizzServerNode.js',
15 + 'react-dom/src/server/react-dom-server.node.js',
16 'react-dom/static.node',
17 'react-dom/server-rendering-stub',
18 'react-dom/unstable_server-external-runtime',
@@ -27,6 +27,7 @@ module.exports = [
27 'react-dom/server.node',
28 'react-dom/static',
29 'react-dom/static.node',
30 + 'react-dom/src/server/react-dom-server.node',
31 'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node
32 'react-dom/src/server/ReactDOMFizzStaticNode.js',
33 'react-server-dom-webpack',
@@ -46,10 +47,11 @@ module.exports = [
47 },
48 {
49 shortName: 'dom-bun',
49 - entryPoints: ['react-dom', 'react-dom/src/server/ReactDOMFizzServerBun.js'],
50 + entryPoints: ['react-dom', 'react-dom/src/server/react-dom-server.bun.js'],
51 paths: [
52 'react-dom',
53 'react-dom/server.bun',
54 + 'react-dom/src/server/react-dom-server.bun',
55 'react-dom/src/server/ReactDOMFizzServerBun.js',
56 'react-dom-bindings',
57 'shared/ReactDOMSharedInternals',
@@ -62,7 +64,7 @@ module.exports = [
64 entryPoints: [
65 'react-dom',
66 'react-dom/unstable_testing',
65 - 'react-dom/src/server/ReactDOMFizzServerBrowser.js',
67 + 'react-dom/src/server/react-dom-server.browser.js',
68 'react-dom/static.browser',
69 'react-dom/server-rendering-stub',
70 'react-dom/unstable_server-external-runtime',
@@ -76,6 +78,7 @@ module.exports = [
78 'react-dom/server.browser',
79 'react-dom/static.browser',
80 'react-dom/unstable_testing',
81 + 'react-dom/src/server/react-dom-server.browser',
82 'react-dom/src/server/ReactDOMFizzServerBrowser.js', // react-dom/server.browser
83 'react-dom/src/server/ReactDOMFizzStaticBrowser.js',
84 'react-server-dom-webpack',
@@ -118,7 +121,7 @@ module.exports = [
121 {
122 shortName: 'dom-edge-webpack',
123 entryPoints: [
121 - 'react-dom/src/server/ReactDOMFizzServerEdge.js',
124 + 'react-dom/src/server/react-dom-server.edge.js',
125 'react-dom/static.edge',
126 'react-server-dom-webpack/server.edge',
127 'react-server-dom-webpack/client.edge',
@@ -130,6 +133,7 @@ module.exports = [
133 'react-dom/server.edge',
134 'react-dom/static.edge',
135 'react-dom/unstable_testing',
136 + 'react-dom/src/server/react-dom-server.edge',
137 'react-dom/src/server/ReactDOMFizzServerEdge.js', // react-dom/server.edge
138 'react-dom/src/server/ReactDOMFizzStaticEdge.js',
139 'react-server-dom-webpack',
@@ -160,6 +164,7 @@ module.exports = [
164 'react-dom/server.node',
165 'react-dom/static',
166 'react-dom/static.node',
167 + 'react-dom/src/server/react-dom-server.node',
168 'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node
169 'react-dom/src/server/ReactDOMFizzStaticNode.js',
170 'react-server-dom-webpack',
@@ -193,6 +198,7 @@ module.exports = [
198 'react-dom/server.node',
199 'react-dom/static',
200 'react-dom/static.node',
201 + 'react-dom/src/server/react-dom-server.node',
202 'react-dom/src/server/ReactDOMFizzServerNode.js', // react-dom/server.node
203 'react-dom/src/server/ReactDOMFizzStaticNode.js',
204 'react-server-dom-esm',