From a364e6004f56f84f6f2c5b149635db105620af2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Markb=C3=A5ge?= Date: Thu, 7 Sep 2023 00:18:30 -0400 Subject: [PATCH] [Fizz] Split createRequest into createRequest, createPrerenderRequest and resumeRequest (#27342) Just moving some internal code around again. I originally encoded what type of work using startRender vs startPrerender. I had intended to do more forking of the work loop but we've decided not to go with that strategy. It also turns out that forking when we start working is actually too late because of a subtle thing where you can call abort before work begins. Therefore it's important that starting the work comes later. --- .../src/server/ReactDOMFizzServerBrowser.js | 13 +-- .../src/server/ReactDOMFizzServerBun.js | 4 +- .../src/server/ReactDOMFizzServerEdge.js | 13 +-- .../src/server/ReactDOMFizzServerNode.js | 13 +-- .../src/server/ReactDOMFizzStaticBrowser.js | 8 +- .../src/server/ReactDOMFizzStaticEdge.js | 8 +- .../src/server/ReactDOMFizzStaticNode.js | 8 +- .../src/server/ReactDOMLegacyServerImpl.js | 4 +- .../server/ReactDOMLegacyServerNodeStream.js | 4 +- .../src/ReactNoopFlightServer.js | 2 +- .../src/ReactNoopServer.js | 2 +- .../src/ReactFlightDOMServerNode.js | 4 +- .../src/ReactDOMServerFB.js | 4 +- .../src/ReactFlightDOMServerBrowser.js | 4 +- .../src/ReactFlightDOMServerEdge.js | 4 +- .../src/ReactFlightDOMServerNode.js | 4 +- packages/react-server/src/ReactFizzServer.js | 108 ++++++++++++++++-- .../react-server/src/ReactFlightServer.js | 2 +- 18 files changed, 150 insertions(+), 59 deletions(-) diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js index 71631f9573086..44df0fb2deecc 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerBrowser.js @@ -16,7 +16,8 @@ import ReactVersion from 'shared/ReactVersion'; import { createRequest, - startRender, + resumeRequest, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -129,7 +130,7 @@ function renderToReadableStream( signal.addEventListener('abort', listener); } } - startRender(request); + startWork(request); }); } @@ -171,16 +172,14 @@ function resume( allReady.catch(() => {}); reject(error); } - const request = createRequest( + const request = resumeRequest( children, - postponedState.resumableState, + postponedState, createRenderState( postponedState.resumableState, options ? options.nonce : undefined, undefined, // importMap ), - postponedState.rootFormatContext, - postponedState.progressiveChunkSize, options ? options.onError : undefined, onAllReady, onShellReady, @@ -200,7 +199,7 @@ function resume( signal.addEventListener('abort', listener); } } - startRender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMFizzServerBun.js b/packages/react-dom/src/server/ReactDOMFizzServerBun.js index 4464b95551271..997934e1a3d1a 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerBun.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerBun.js @@ -15,7 +15,7 @@ import ReactVersion from 'shared/ReactVersion'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -121,7 +121,7 @@ function renderToReadableStream( signal.addEventListener('abort', listener); } } - startRender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMFizzServerEdge.js b/packages/react-dom/src/server/ReactDOMFizzServerEdge.js index 71631f9573086..44df0fb2deecc 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerEdge.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerEdge.js @@ -16,7 +16,8 @@ import ReactVersion from 'shared/ReactVersion'; import { createRequest, - startRender, + resumeRequest, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -129,7 +130,7 @@ function renderToReadableStream( signal.addEventListener('abort', listener); } } - startRender(request); + startWork(request); }); } @@ -171,16 +172,14 @@ function resume( allReady.catch(() => {}); reject(error); } - const request = createRequest( + const request = resumeRequest( children, - postponedState.resumableState, + postponedState, createRenderState( postponedState.resumableState, options ? options.nonce : undefined, undefined, // importMap ), - postponedState.rootFormatContext, - postponedState.progressiveChunkSize, options ? options.onError : undefined, onAllReady, onShellReady, @@ -200,7 +199,7 @@ function resume( signal.addEventListener('abort', listener); } } - startRender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMFizzServerNode.js b/packages/react-dom/src/server/ReactDOMFizzServerNode.js index c948d4f3996d8..c7de8e073e16e 100644 --- a/packages/react-dom/src/server/ReactDOMFizzServerNode.js +++ b/packages/react-dom/src/server/ReactDOMFizzServerNode.js @@ -18,7 +18,8 @@ import ReactVersion from 'shared/ReactVersion'; import { createRequest, - startRender, + resumeRequest, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -105,7 +106,7 @@ function renderToPipeableStream( ): PipeableStream { const request = createRequestImpl(children, options); let hasStartedFlowing = false; - startRender(request); + startWork(request); return { pipe(destination: T): T { if (hasStartedFlowing) { @@ -140,16 +141,14 @@ function resumeRequestImpl( postponedState: PostponedState, options: void | ResumeOptions, ) { - return createRequest( + return resumeRequest( children, - postponedState.resumableState, + postponedState, createRenderState( postponedState.resumableState, options ? options.nonce : undefined, undefined, // importMap ), - postponedState.rootFormatContext, - postponedState.progressiveChunkSize, options ? options.onError : undefined, options ? options.onAllReady : undefined, options ? options.onShellReady : undefined, @@ -166,7 +165,7 @@ function resumeToPipeableStream( ): PipeableStream { const request = resumeRequestImpl(children, postponedState, options); let hasStartedFlowing = false; - startRender(request); + startWork(request); return { pipe(destination: T): T { if (hasStartedFlowing) { diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js index 64c5cf4ae28fd..fe61a1ba271a0 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticBrowser.js @@ -15,8 +15,8 @@ import type {ImportMap} from '../shared/ReactDOMTypes'; import ReactVersion from 'shared/ReactVersion'; import { - createRequest, - startPrerender, + createPrerenderRequest, + startWork, startFlowing, abort, getPostponedState, @@ -80,7 +80,7 @@ function prerender( options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, ); - const request = createRequest( + const request = createPrerenderRequest( children, resources, createRenderState( @@ -109,7 +109,7 @@ function prerender( signal.addEventListener('abort', listener); } } - startPrerender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js b/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js index 64c5cf4ae28fd..fe61a1ba271a0 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticEdge.js @@ -15,8 +15,8 @@ import type {ImportMap} from '../shared/ReactDOMTypes'; import ReactVersion from 'shared/ReactVersion'; import { - createRequest, - startPrerender, + createPrerenderRequest, + startWork, startFlowing, abort, getPostponedState, @@ -80,7 +80,7 @@ function prerender( options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, ); - const request = createRequest( + const request = createPrerenderRequest( children, resources, createRenderState( @@ -109,7 +109,7 @@ function prerender( signal.addEventListener('abort', listener); } } - startPrerender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js index ee138ef5a3f6b..4c6260ca920f7 100644 --- a/packages/react-dom/src/server/ReactDOMFizzStaticNode.js +++ b/packages/react-dom/src/server/ReactDOMFizzStaticNode.js @@ -17,8 +17,8 @@ import {Writable, Readable} from 'stream'; import ReactVersion from 'shared/ReactVersion'; import { - createRequest, - startPrerender, + createPrerenderRequest, + startWork, startFlowing, abort, getPostponedState, @@ -94,7 +94,7 @@ function prerenderToNodeStream( options ? options.bootstrapModules : undefined, options ? options.unstable_externalRuntimeSrc : undefined, ); - const request = createRequest( + const request = createPrerenderRequest( children, resumableState, createRenderState( @@ -123,7 +123,7 @@ function prerenderToNodeStream( signal.addEventListener('abort', listener); } } - startPrerender(request); + startWork(request); }); } diff --git a/packages/react-dom/src/server/ReactDOMLegacyServerImpl.js b/packages/react-dom/src/server/ReactDOMLegacyServerImpl.js index 24a61cb40f68f..b08b51de19890 100644 --- a/packages/react-dom/src/server/ReactDOMLegacyServerImpl.js +++ b/packages/react-dom/src/server/ReactDOMLegacyServerImpl.js @@ -13,7 +13,7 @@ import type {ReactNodeList} from 'shared/ReactTypes'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -81,7 +81,7 @@ function renderToStringImpl( undefined, undefined, ); - startRender(request); + startWork(request); // If anything suspended and is still pending, we'll abort it before writing. // That way we write only client-rendered boundaries from the start. abort(request, abortReason); diff --git a/packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js b/packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js index d01b063bc421f..a3e39def4ef4c 100644 --- a/packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js +++ b/packages/react-dom/src/server/ReactDOMLegacyServerNodeStream.js @@ -13,7 +13,7 @@ import type {Request} from 'react-server/src/ReactFizzServer'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFizzServer'; @@ -92,7 +92,7 @@ function renderToNodeStreamImpl( undefined, ); destination.request = request; - startRender(request); + startWork(request); return destination; } diff --git a/packages/react-noop-renderer/src/ReactNoopFlightServer.js b/packages/react-noop-renderer/src/ReactNoopFlightServer.js index 560305d539f71..9faab3cdfa110 100644 --- a/packages/react-noop-renderer/src/ReactNoopFlightServer.js +++ b/packages/react-noop-renderer/src/ReactNoopFlightServer.js @@ -84,7 +84,7 @@ function render(model: ReactClientValue, options?: Options): Destination { options ? options.context : undefined, options ? options.identifierPrefix : undefined, ); - ReactNoopFlightServer.startRender(request); + ReactNoopFlightServer.startWork(request); ReactNoopFlightServer.startFlowing(request, destination); return destination; } diff --git a/packages/react-noop-renderer/src/ReactNoopServer.js b/packages/react-noop-renderer/src/ReactNoopServer.js index 44c9559d1ad56..34a2dac5592a2 100644 --- a/packages/react-noop-renderer/src/ReactNoopServer.js +++ b/packages/react-noop-renderer/src/ReactNoopServer.js @@ -304,7 +304,7 @@ function render(children: React$Element, options?: Options): Destination { options ? options.onAllReady : undefined, options ? options.onShellReady : undefined, ); - ReactNoopServer.startRender(request); + ReactNoopServer.startWork(request); ReactNoopServer.startFlowing(request, destination); return destination; } diff --git a/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js index 4d44fa01a3821..def3a58478e57 100644 --- a/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-esm/src/ReactFlightDOMServerNode.js @@ -20,7 +20,7 @@ import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFlightServer'; @@ -73,7 +73,7 @@ function renderToPipeableStream( options ? options.onPostpone : undefined, ); let hasStartedFlowing = false; - startRender(request); + startWork(request); return { pipe(destination: T): T { if (hasStartedFlowing) { diff --git a/packages/react-server-dom-fb/src/ReactDOMServerFB.js b/packages/react-server-dom-fb/src/ReactDOMServerFB.js index 91c99cead07c5..eec7404055e26 100644 --- a/packages/react-server-dom-fb/src/ReactDOMServerFB.js +++ b/packages/react-server-dom-fb/src/ReactDOMServerFB.js @@ -16,7 +16,7 @@ import type {BootstrapScriptDescriptor} from 'react-dom-bindings/src/server/Reac import { createRequest, - startRender, + startWork, performWork, startFlowing, abort, @@ -68,7 +68,7 @@ function renderToStream(children: ReactNodeList, options: Options): Stream { undefined, undefined, ); - startRender(request); + startWork(request); if (destination.fatal) { throw destination.error; } diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js index b445a4be15323..08214a4182ab2 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerBrowser.js @@ -14,7 +14,7 @@ import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFlightServer'; @@ -70,7 +70,7 @@ function renderToReadableStream( { type: 'bytes', start: (controller): ?Promise => { - startRender(request); + startWork(request); }, pull: (controller): ?Promise => { startFlowing(request, controller); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js index b445a4be15323..08214a4182ab2 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerEdge.js @@ -14,7 +14,7 @@ import type {ServerManifest} from 'react-client/src/ReactFlightClientConfig'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFlightServer'; @@ -70,7 +70,7 @@ function renderToReadableStream( { type: 'bytes', start: (controller): ?Promise => { - startRender(request); + startWork(request); }, pull: (controller): ?Promise => { startFlowing(request, controller); diff --git a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js index 17dd769cdecb8..1e39d000ffef4 100644 --- a/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js +++ b/packages/react-server-dom-webpack/src/ReactFlightDOMServerNode.js @@ -20,7 +20,7 @@ import type {ServerContextJSONValue, Thenable} from 'shared/ReactTypes'; import { createRequest, - startRender, + startWork, startFlowing, abort, } from 'react-server/src/ReactFlightServer'; @@ -74,7 +74,7 @@ function renderToPipeableStream( options ? options.onPostpone : undefined, ); let hasStartedFlowing = false; - startRender(request); + startWork(request); return { pipe(destination: T): T { if (hasStartedFlowing) { diff --git a/packages/react-server/src/ReactFizzServer.js b/packages/react-server/src/ReactFizzServer.js index a188e9807373d..4d01fc3504d7d 100644 --- a/packages/react-server/src/ReactFizzServer.js +++ b/packages/react-server/src/ReactFizzServer.js @@ -396,6 +396,106 @@ export function createRequest( return request; } +export function createPrerenderRequest( + children: ReactNodeList, + resumableState: ResumableState, + renderState: RenderState, + rootFormatContext: FormatContext, + progressiveChunkSize: void | number, + onError: void | ((error: mixed) => ?string), + onAllReady: void | (() => void), + onShellReady: void | (() => void), + onShellError: void | ((error: mixed) => void), + onFatalError: void | ((error: mixed) => void), + onPostpone: void | ((reason: string) => void), +): Request { + const request = createRequest( + children, + resumableState, + renderState, + rootFormatContext, + progressiveChunkSize, + onError, + onAllReady, + onShellReady, + onShellError, + onFatalError, + onPostpone, + ); + // Start tracking postponed holes during this render. + request.trackedPostpones = {workingMap: new Map(), root: []}; + return request; +} + +export function resumeRequest( + children: ReactNodeList, + postponedState: PostponedState, + renderState: RenderState, + onError: void | ((error: mixed) => ?string), + onAllReady: void | (() => void), + onShellReady: void | (() => void), + onShellError: void | ((error: mixed) => void), + onFatalError: void | ((error: mixed) => void), + onPostpone: void | ((reason: string) => void), +): Request { + prepareHostDispatcher(); + const pingedTasks: Array = []; + const abortSet: Set = new Set(); + const request: Request = { + destination: null, + flushScheduled: false, + resumableState: postponedState.resumableState, + renderState, + rootFormatContext: postponedState.rootFormatContext, + progressiveChunkSize: postponedState.progressiveChunkSize, + status: OPEN, + fatalError: null, + nextSegmentId: 0, + allPendingTasks: 0, + pendingRootTasks: 0, + completedRootSegment: null, + abortableTasks: abortSet, + pingedTasks: pingedTasks, + clientRenderedBoundaries: ([]: Array), + completedBoundaries: ([]: Array), + partialBoundaries: ([]: Array), + trackedPostpones: null, + onError: onError === undefined ? defaultErrorHandler : onError, + onPostpone: onPostpone === undefined ? noop : onPostpone, + onAllReady: onAllReady === undefined ? noop : onAllReady, + onShellReady: onShellReady === undefined ? noop : onShellReady, + onShellError: onShellError === undefined ? noop : onShellError, + onFatalError: onFatalError === undefined ? noop : onFatalError, + }; + // This segment represents the root fallback. + const rootSegment = createPendingSegment( + request, + 0, + null, + postponedState.rootFormatContext, + // Root segments are never embedded in Text on either edge + false, + false, + ); + // There is no parent so conceptually, we're unblocked to flush this segment. + rootSegment.parentFlushed = true; + const rootTask = createTask( + request, + null, + children, + null, + rootSegment, + abortSet, + null, + postponedState.rootFormatContext, + emptyContextObject, + rootContextSnapshot, + emptyTreeContext, + ); + pingedTasks.push(rootTask); + return request; +} + let currentRequest: null | Request = null; export function resolveRequest(): null | Request { @@ -2751,7 +2851,7 @@ function flushCompletedQueues( } } -export function startRender(request: Request): void { +export function startWork(request: Request): void { request.flushScheduled = request.destination !== null; if (supportsRequestStorage) { scheduleWork(() => requestStorage.run(request, performWork, request)); @@ -2760,12 +2860,6 @@ export function startRender(request: Request): void { } } -export function startPrerender(request: Request): void { - // Start tracking postponed holes during this render. - request.trackedPostpones = {workingMap: new Map(), root: []}; - startRender(request); -} - function enqueueFlush(request: Request): void { if ( request.flushScheduled === false && diff --git a/packages/react-server/src/ReactFlightServer.js b/packages/react-server/src/ReactFlightServer.js index 515b6aa870613..cb62cd0cb1f40 100644 --- a/packages/react-server/src/ReactFlightServer.js +++ b/packages/react-server/src/ReactFlightServer.js @@ -1519,7 +1519,7 @@ function flushCompletedChunks( } } -export function startRender(request: Request): void { +export function startWork(request: Request): void { request.flushScheduled = request.destination !== null; if (supportsRequestStorage) { scheduleWork(() => requestStorage.run(request, performWork, request));