From 953cb02f6de2a9f3eb52456b263e11f839584bc6 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 71631f9573..44df0fb2de 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 4464b95551..997934e1a3 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 71631f9573..44df0fb2de 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 c948d4f399..c7de8e073e 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 64c5cf4ae2..fe61a1ba27 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 64c5cf4ae2..fe61a1ba27 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 ee138ef5a3..4c6260ca92 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 24a61cb40f..b08b51de19 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 d01b063bc4..a3e39def4e 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 560305d539..9faab3cdfa 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 44c9559d1a..34a2dac559 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 4d44fa01a3..def3a58478 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 91c99cead0..eec7404055 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 b445a4be15..08214a4182 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 b445a4be15..08214a4182 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 17dd769cde..1e39d000ff 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 a188e98073..4d01fc3504 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 515b6aa870..cb62cd0cb1 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));