From 7aeecfdc2b75c9b88b0d0aeccd75221ecd16e7b8 Mon Sep 17 00:00:00 2001 From: sebmarkbage Date: Thu, 31 Aug 2023 16:28:12 +0000 Subject: [PATCH] [Fizz] Track postponed holes in the prerender pass (#27317) This is basically the implementation for the prerender pass. Instead of forking basically the whole implementation for prerender, I just add a conditional field on the request. If it's `null` it behaves like before. If it's non-`null` then instead of triggering client rendered boundaries it triggers those into a "postponed" state which is basically just a variant of "pending". It's supposed to be filled in later. It also builds up a serializable tree of which path can be followed to find the holes. This is basically a reverse `KeyPath` tree. It is unfortunate that this approach adds more code to the regular Fizz builds but in practice. It seems like this side is not going to add much code and we might instead just want to merge the builds so that it's smaller when you have `prerender` and `resume` in the same bundle - which I think will be common in practice. This just implements the prerender side, and not the resume side, which is why the tests have a TODO. That's in a follow up PR. DiffTrain build for [b70a0d70224ceb4e277bd8ac535a2caafa5c075a](https://github.com/facebook/react/commit/b70a0d70224ceb4e277bd8ac535a2caafa5c075a) --- compiled/facebook-www/REVISION | 2 +- .../ReactDOMServer-dev.classic.js | 163 +++++++++++------- .../facebook-www/ReactDOMServer-dev.modern.js | 163 +++++++++++------- .../ReactDOMServer-prod.classic.js | 53 +++--- .../ReactDOMServer-prod.modern.js | 53 +++--- .../ReactDOMServerStreaming-dev.modern.js | 161 ++++++++++------- .../ReactDOMServerStreaming-prod.modern.js | 51 +++--- 7 files changed, 383 insertions(+), 263 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 48844cd7d4160..8dbf95b4de81d 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -2fba484cd095ea79b940364cea5107fa4ca9f0c8 +b70a0d70224ceb4e277bd8ac535a2caafa5c075a diff --git a/compiled/facebook-www/ReactDOMServer-dev.classic.js b/compiled/facebook-www/ReactDOMServer-dev.classic.js index 81ffe7823bb44..f17b145ea68d0 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.classic.js +++ b/compiled/facebook-www/ReactDOMServer-dev.classic.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-classic-b66d17d9"; +var ReactVersion = "18.3.0-www-classic-b794beee"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9789,14 +9789,14 @@ function getStackByComponentStackNode(componentStack) { var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; var ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; // Linked list representing the identity of a component given the component/tag name and key. -// The name might be minified but we assume that it's going to be the same generated name. Typically -// because it's just the same compiled output in practice. +var CLIENT_RENDERED = 4; // if it errors or infinitely suspends var PENDING = 0; var COMPLETED = 1; var FLUSHED = 2; var ABORTED = 3; var ERRORED = 4; +var POSTPONED = 5; var OPEN = 0; var CLOSING = 1; var CLOSED = 2; // This is a default heuristic for how to split up the HTML content into progressive @@ -9846,6 +9846,7 @@ function createRequest( flushScheduled: false, resumableState: resumableState, renderState: renderState, + rootFormatContext: rootFormatContext, progressiveChunkSize: progressiveChunkSize === undefined ? DEFAULT_PROGRESSIVE_CHUNK_SIZE @@ -9861,6 +9862,7 @@ function createRequest( clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: onError === undefined ? defaultErrorHandler : onError, onPostpone: onPostpone === undefined ? noop : onPostpone, onAllReady: onAllReady === undefined ? noop : onAllReady, @@ -9913,18 +9915,19 @@ function pingTask(request, task) { } } -function createSuspenseBoundary(request, fallbackAbortableTasks) { +function createSuspenseBoundary(request, fallbackAbortableTasks, keyPath) { return { + status: PENDING, id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID, rootSegmentID: -1, parentFlushed: false, pendingTasks: 0, - forceClientRender: false, completedSegments: [], byteSize: 0, fallbackAbortableTasks: fallbackAbortableTasks, errorDigest: null, - resources: createBoundaryResources() + resources: createBoundaryResources(), + keyPath: keyPath }; } @@ -10117,7 +10120,11 @@ function renderSuspenseBoundary(request, task, props) { var fallback = props.fallback; var content = props.children; var fallbackAbortSet = new Set(); - var newBoundary = createSuspenseBoundary(request, fallbackAbortSet); + var newBoundary = createSuspenseBoundary( + request, + fallbackAbortSet, + task.keyPath + ); var insertionIndex = parentSegment.chunks.length; // The children of the boundary segment is actually the fallback. var boundarySegment = createPendingSegment( @@ -10172,16 +10179,17 @@ function renderSuspenseBoundary(request, task, props) { contentRootSegment.status = COMPLETED; queueCompletedSegment(newBoundary, contentRootSegment); - if (newBoundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) { + newBoundary.status = COMPLETED; // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + popComponentStackInDEV(task); return; } } catch (error) { contentRootSegment.status = ERRORED; - newBoundary.forceClientRender = true; + newBoundary.status = CLIENT_RENDERED; var errorDigest; { @@ -11134,43 +11142,45 @@ function renderNode(request, task, node, childIndex) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] + : thrownValue; - if (typeof x === "object" && x !== null && typeof x.then === "function") { - var wakeable = x; - var thenableState = getThenableStateAfterSuspending(); - spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + var wakeable = x; + var thenableState = getThenableStateAfterSuspending(); + spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; + { + task.componentStack = previousComponentStack; + } + + return; } + } // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - return; - } else { - // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; - } // We assume that we don't need the correct context. - // Let's terminate the rest of the tree and don't render any siblings. + { + task.componentStack = previousComponentStack; + } // We assume that we don't need the correct context. + // Let's terminate the rest of the tree and don't render any siblings. - throw x; - } + throw x; } } @@ -11187,8 +11197,8 @@ function erroredTask(request, boundary, segment, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = errorDigest; { @@ -11243,8 +11253,8 @@ function abortTask(task, request, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = request.onError(error); { @@ -11331,9 +11341,12 @@ function finishedTask(request, boundary, segment) { } else { boundary.pendingTasks--; - if (boundary.forceClientRender); + if (boundary.status === CLIENT_RENDERED); else if (boundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (boundary.status === PENDING) { + boundary.status = COMPLETED; + } // This must have been the last segment we were waiting on. This boundary is now complete. + if (segment.parentFlushed) { // Our parent segment already flushed, so we need to schedule this segment to be emitted. // If it is a segment that was aborted, we'll write other content instead so we don't need @@ -11444,18 +11457,23 @@ function retryTask(request, task) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] - - if (typeof x === "object" && x !== null && typeof x.then === "function") { - // Something suspended again, let's pick it back up later. - var ping = task.ping; - x.then(ping, ping); - task.thenableState = getThenableStateAfterSuspending(); - } else { - task.abortSet.delete(task); - segment.status = ERRORED; - erroredTask(request, task.blockedBoundary, segment, x); + : thrownValue; + + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + // Something suspended again, let's pick it back up later. + var ping = task.ping; + x.then(ping, ping); + task.thenableState = getThenableStateAfterSuspending(); + return; + } } + + task.abortSet.delete(task); + segment.status = ERRORED; + erroredTask(request, task.blockedBoundary, segment, x); + return; } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null); @@ -11545,7 +11563,11 @@ function flushSubtree(request, destination, segment) { case PENDING: { // We're emitting a placeholder for this segment to be filled in later. // Therefore we'll need to assign it an ID - to refer to it by. - var segmentID = (segment.id = request.nextSegmentId++); // When this segment finally completes it won't be embedded in text since it will flush separately + segment.id = request.nextSegmentId++; // Fallthrough + } + + case POSTPONED: { + var segmentID = segment.id; // When this segment finally completes it won't be embedded in text since it will flush separately segment.lastPushedText = false; segment.textEmbedded = false; @@ -11599,7 +11621,7 @@ function flushSegment(request, destination, segment) { boundary.parentFlushed = true; // This segment is a Suspense boundary. We need to decide whether to // emit the content or the fallback now. - if (boundary.forceClientRender) { + if (boundary.status === CLIENT_RENDERED) { // Emit a client rendered suspense boundary wrapper. // We never queue the inner boundary so we'll never emit its content or partial segments. writeStartClientRenderedSuspenseBoundary( @@ -11615,9 +11637,15 @@ function flushSegment(request, destination, segment) { destination, request.renderState ); - } else if (boundary.pendingTasks > 0) { - // This boundary is still loading. Emit a pending suspense boundary wrapper. + } else if (boundary.status !== COMPLETED) { + if (boundary.status === PENDING) { + boundary.id = assignSuspenseBoundaryID( + request.renderState, + request.resumableState + ); + } // This boundary is still loading. Emit a pending suspense boundary wrapper. // Assign an ID to refer to the future content by. + boundary.rootSegmentID = request.nextSegmentId++; if (boundary.completedSegments.length > 0) { @@ -11625,10 +11653,7 @@ function flushSegment(request, destination, segment) { request.partialBoundaries.push(boundary); } /// This is the first time we should have referenced this ID. - var id = (boundary.id = assignSuspenseBoundaryID( - request.renderState, - request.resumableState - )); + var id = boundary.id; writeStartPendingSuspenseBoundary(destination, request.renderState, id); // Flush the fallback. flushSubtree(request, destination, segment); @@ -11924,7 +11949,11 @@ function flushCompletedQueues(request, destination) { request.flushScheduled = false; { - writePostamble(destination, request.resumableState); + // We write the trailing tags but only if don't have any data to resume. + // If we need to resume we'll write the postamble in the resume instead. + { + writePostamble(destination, request.resumableState); + } } { @@ -11940,7 +11969,7 @@ function flushCompletedQueues(request, destination) { } } -function startWork(request) { +function startRender(request) { request.flushScheduled = request.destination !== null; { @@ -12019,7 +12048,7 @@ function flushResources(request) { } function getResumableState(request) { return request.resumableState; -} // Returns the state of a postponed request or null if nothing was postponed. +} function onError() { // Non-fatal errors are ignored. @@ -12076,7 +12105,7 @@ function renderToStringImpl( undefined, undefined ); - startWork(request); // If anything suspended and is still pending, we'll abort it before writing. + startRender(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/compiled/facebook-www/ReactDOMServer-dev.modern.js b/compiled/facebook-www/ReactDOMServer-dev.modern.js index 5f94f24061eab..f0fc3bffae1b4 100644 --- a/compiled/facebook-www/ReactDOMServer-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServer-dev.modern.js @@ -19,7 +19,7 @@ if (__DEV__) { var React = require("react"); var ReactDOM = require("react-dom"); -var ReactVersion = "18.3.0-www-modern-a9c5f511"; +var ReactVersion = "18.3.0-www-modern-8d028b97"; // This refers to a WWW module. var warningWWW = require("warning"); @@ -9548,14 +9548,14 @@ function getStackByComponentStackNode(componentStack) { var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; var ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; // Linked list representing the identity of a component given the component/tag name and key. -// The name might be minified but we assume that it's going to be the same generated name. Typically -// because it's just the same compiled output in practice. +var CLIENT_RENDERED = 4; // if it errors or infinitely suspends var PENDING = 0; var COMPLETED = 1; var FLUSHED = 2; var ABORTED = 3; var ERRORED = 4; +var POSTPONED = 5; var OPEN = 0; var CLOSING = 1; var CLOSED = 2; // This is a default heuristic for how to split up the HTML content into progressive @@ -9605,6 +9605,7 @@ function createRequest( flushScheduled: false, resumableState: resumableState, renderState: renderState, + rootFormatContext: rootFormatContext, progressiveChunkSize: progressiveChunkSize === undefined ? DEFAULT_PROGRESSIVE_CHUNK_SIZE @@ -9620,6 +9621,7 @@ function createRequest( clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: onError === undefined ? defaultErrorHandler : onError, onPostpone: onPostpone === undefined ? noop : onPostpone, onAllReady: onAllReady === undefined ? noop : onAllReady, @@ -9672,18 +9674,19 @@ function pingTask(request, task) { } } -function createSuspenseBoundary(request, fallbackAbortableTasks) { +function createSuspenseBoundary(request, fallbackAbortableTasks, keyPath) { return { + status: PENDING, id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID, rootSegmentID: -1, parentFlushed: false, pendingTasks: 0, - forceClientRender: false, completedSegments: [], byteSize: 0, fallbackAbortableTasks: fallbackAbortableTasks, errorDigest: null, - resources: createBoundaryResources() + resources: createBoundaryResources(), + keyPath: keyPath }; } @@ -9876,7 +9879,11 @@ function renderSuspenseBoundary(request, task, props) { var fallback = props.fallback; var content = props.children; var fallbackAbortSet = new Set(); - var newBoundary = createSuspenseBoundary(request, fallbackAbortSet); + var newBoundary = createSuspenseBoundary( + request, + fallbackAbortSet, + task.keyPath + ); var insertionIndex = parentSegment.chunks.length; // The children of the boundary segment is actually the fallback. var boundarySegment = createPendingSegment( @@ -9931,16 +9938,17 @@ function renderSuspenseBoundary(request, task, props) { contentRootSegment.status = COMPLETED; queueCompletedSegment(newBoundary, contentRootSegment); - if (newBoundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) { + newBoundary.status = COMPLETED; // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + popComponentStackInDEV(task); return; } } catch (error) { contentRootSegment.status = ERRORED; - newBoundary.forceClientRender = true; + newBoundary.status = CLIENT_RENDERED; var errorDigest; { @@ -10882,43 +10890,45 @@ function renderNode(request, task, node, childIndex) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] + : thrownValue; - if (typeof x === "object" && x !== null && typeof x.then === "function") { - var wakeable = x; - var thenableState = getThenableStateAfterSuspending(); - spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + var wakeable = x; + var thenableState = getThenableStateAfterSuspending(); + spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; + { + task.componentStack = previousComponentStack; + } + + return; } + } // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - return; - } else { - // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; - } // We assume that we don't need the correct context. - // Let's terminate the rest of the tree and don't render any siblings. + { + task.componentStack = previousComponentStack; + } // We assume that we don't need the correct context. + // Let's terminate the rest of the tree and don't render any siblings. - throw x; - } + throw x; } } @@ -10935,8 +10945,8 @@ function erroredTask(request, boundary, segment, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = errorDigest; { @@ -10991,8 +11001,8 @@ function abortTask(task, request, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = request.onError(error); { @@ -11079,9 +11089,12 @@ function finishedTask(request, boundary, segment) { } else { boundary.pendingTasks--; - if (boundary.forceClientRender); + if (boundary.status === CLIENT_RENDERED); else if (boundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (boundary.status === PENDING) { + boundary.status = COMPLETED; + } // This must have been the last segment we were waiting on. This boundary is now complete. + if (segment.parentFlushed) { // Our parent segment already flushed, so we need to schedule this segment to be emitted. // If it is a segment that was aborted, we'll write other content instead so we don't need @@ -11192,18 +11205,23 @@ function retryTask(request, task) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] - - if (typeof x === "object" && x !== null && typeof x.then === "function") { - // Something suspended again, let's pick it back up later. - var ping = task.ping; - x.then(ping, ping); - task.thenableState = getThenableStateAfterSuspending(); - } else { - task.abortSet.delete(task); - segment.status = ERRORED; - erroredTask(request, task.blockedBoundary, segment, x); + : thrownValue; + + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + // Something suspended again, let's pick it back up later. + var ping = task.ping; + x.then(ping, ping); + task.thenableState = getThenableStateAfterSuspending(); + return; + } } + + task.abortSet.delete(task); + segment.status = ERRORED; + erroredTask(request, task.blockedBoundary, segment, x); + return; } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null); @@ -11293,7 +11311,11 @@ function flushSubtree(request, destination, segment) { case PENDING: { // We're emitting a placeholder for this segment to be filled in later. // Therefore we'll need to assign it an ID - to refer to it by. - var segmentID = (segment.id = request.nextSegmentId++); // When this segment finally completes it won't be embedded in text since it will flush separately + segment.id = request.nextSegmentId++; // Fallthrough + } + + case POSTPONED: { + var segmentID = segment.id; // When this segment finally completes it won't be embedded in text since it will flush separately segment.lastPushedText = false; segment.textEmbedded = false; @@ -11347,7 +11369,7 @@ function flushSegment(request, destination, segment) { boundary.parentFlushed = true; // This segment is a Suspense boundary. We need to decide whether to // emit the content or the fallback now. - if (boundary.forceClientRender) { + if (boundary.status === CLIENT_RENDERED) { // Emit a client rendered suspense boundary wrapper. // We never queue the inner boundary so we'll never emit its content or partial segments. writeStartClientRenderedSuspenseBoundary( @@ -11363,9 +11385,15 @@ function flushSegment(request, destination, segment) { destination, request.renderState ); - } else if (boundary.pendingTasks > 0) { - // This boundary is still loading. Emit a pending suspense boundary wrapper. + } else if (boundary.status !== COMPLETED) { + if (boundary.status === PENDING) { + boundary.id = assignSuspenseBoundaryID( + request.renderState, + request.resumableState + ); + } // This boundary is still loading. Emit a pending suspense boundary wrapper. // Assign an ID to refer to the future content by. + boundary.rootSegmentID = request.nextSegmentId++; if (boundary.completedSegments.length > 0) { @@ -11373,10 +11401,7 @@ function flushSegment(request, destination, segment) { request.partialBoundaries.push(boundary); } /// This is the first time we should have referenced this ID. - var id = (boundary.id = assignSuspenseBoundaryID( - request.renderState, - request.resumableState - )); + var id = boundary.id; writeStartPendingSuspenseBoundary(destination, request.renderState, id); // Flush the fallback. flushSubtree(request, destination, segment); @@ -11672,7 +11697,11 @@ function flushCompletedQueues(request, destination) { request.flushScheduled = false; { - writePostamble(destination, request.resumableState); + // We write the trailing tags but only if don't have any data to resume. + // If we need to resume we'll write the postamble in the resume instead. + { + writePostamble(destination, request.resumableState); + } } { @@ -11688,7 +11717,7 @@ function flushCompletedQueues(request, destination) { } } -function startWork(request) { +function startRender(request) { request.flushScheduled = request.destination !== null; { @@ -11767,7 +11796,7 @@ function flushResources(request) { } function getResumableState(request) { return request.resumableState; -} // Returns the state of a postponed request or null if nothing was postponed. +} function onError() { // Non-fatal errors are ignored. @@ -11824,7 +11853,7 @@ function renderToStringImpl( undefined, undefined ); - startWork(request); // If anything suspended and is still pending, we'll abort it before writing. + startRender(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/compiled/facebook-www/ReactDOMServer-prod.classic.js b/compiled/facebook-www/ReactDOMServer-prod.classic.js index ca2931ac417d1..9491b329ed5a6 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.classic.js +++ b/compiled/facebook-www/ReactDOMServer-prod.classic.js @@ -2925,6 +2925,7 @@ function createRequest( flushScheduled: !1, resumableState: resumableState, renderState: renderState, + rootFormatContext: rootFormatContext, progressiveChunkSize: void 0 === progressiveChunkSize ? 12800 : progressiveChunkSize, status: 0, @@ -2938,6 +2939,7 @@ function createRequest( clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: void 0 === onError ? defaultErrorHandler : onError, onPostpone: void 0 === onPostpone ? noop : onPostpone, onAllReady: void 0 === onAllReady ? noop : onAllReady, @@ -3271,17 +3273,19 @@ function renderElement(request, task, prevThenableState, type, props, ref) { prevThenableState = props.fallback; props = props.children; ref = new Set(); + initialState = task.keyPath; initialState = { + status: 0, id: null, rootSegmentID: -1, parentFlushed: !1, pendingTasks: 0, - forceClientRender: !1, completedSegments: [], byteSize: 0, fallbackAbortableTasks: ref, errorDigest: null, - resources: new Set() + resources: new Set(), + keyPath: initialState }; contextType = createPendingSegment( request, @@ -3314,12 +3318,14 @@ function renderElement(request, task, prevThenableState, type, props, ref) { oldReplace.chunks.push("\x3c!-- --\x3e")), (oldReplace.status = 1), queueCompletedSegment(initialState, oldReplace), - 0 === initialState.pendingTasks) - ) + 0 === initialState.pendingTasks && 0 === initialState.status) + ) { + initialState.status = 1; break a; + } } catch (error) { (oldReplace.status = 4), - (initialState.forceClientRender = !0), + (initialState.status = 4), (JSCompiler_inline_result = logRecoverableError(request, error)), (initialState.errorDigest = JSCompiler_inline_result); } finally { @@ -3600,8 +3606,8 @@ function abortTask(task, request, error) { 2 !== request.status && (logRecoverableError(request, error), fatalError(request, error))) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = request.onError(error)), boundary.parentFlushed && request.clientRenderedBoundaries.push(boundary)), @@ -3638,9 +3644,10 @@ function finishedTask(request, boundary, segment) { boundary()); } else boundary.pendingTasks--, - boundary.forceClientRender || + 4 !== boundary.status && (0 === boundary.pendingTasks - ? (segment.parentFlushed && + ? (0 === boundary.status && (boundary.status = 1), + segment.parentFlushed && 1 === segment.status && queueCompletedSegment(boundary, segment), boundary.parentFlushed && @@ -3729,8 +3736,8 @@ function performWork(request$jscomp$1) { null === boundary ? fatalError(request$jscomp$0, error$jscomp$0) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = errorDigest), boundary.parentFlushed && request$jscomp$0.clientRenderedBoundaries.push( @@ -3766,7 +3773,9 @@ function flushSubtree(request, destination, segment) { segment.parentFlushed = !0; switch (segment.status) { case 0: - var segmentID = (segment.id = request.nextSegmentId++); + segment.id = request.nextSegmentId++; + case 5: + var segmentID = segment.id; segment.lastPushedText = !1; segment.textEmbedded = !1; request = request.renderState; @@ -3799,7 +3808,7 @@ function flushSegment(request, destination, segment) { var boundary = segment.boundary; if (null === boundary) return flushSubtree(request, destination, segment); boundary.parentFlushed = !0; - if (boundary.forceClientRender) + if (4 === boundary.status) return ( request.renderState.generateStaticMarkup || ((boundary = boundary.errorDigest), @@ -3817,19 +3826,21 @@ function flushSegment(request, destination, segment) { : destination.push("\x3c!--/$--\x3e")), request ); - if (0 < boundary.pendingTasks) { + if (1 !== boundary.status) { + if (0 === boundary.status) { + var JSCompiler_inline_result = request.renderState; + var generatedID = request.resumableState.nextSuspenseID++; + JSCompiler_inline_result = + JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); + boundary.id = JSCompiler_inline_result; + } boundary.rootSegmentID = request.nextSegmentId++; 0 < boundary.completedSegments.length && request.partialBoundaries.push(boundary); - var JSCompiler_inline_result = request.renderState; - var generatedID = request.resumableState.nextSuspenseID++; - JSCompiler_inline_result = - JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); - boundary = boundary.id = JSCompiler_inline_result; writeStartPendingSuspenseBoundary( destination, request.renderState, - boundary + boundary.id ); flushSubtree(request, destination, segment); return destination.push("\x3c!--/$--\x3e"); @@ -4415,4 +4426,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-classic-d7739767"; +exports.version = "18.3.0-www-classic-fde0e1c7"; diff --git a/compiled/facebook-www/ReactDOMServer-prod.modern.js b/compiled/facebook-www/ReactDOMServer-prod.modern.js index c65d4a698ce72..d9b80a3ccc9dc 100644 --- a/compiled/facebook-www/ReactDOMServer-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServer-prod.modern.js @@ -2917,6 +2917,7 @@ function createRequest( flushScheduled: !1, resumableState: resumableState, renderState: renderState, + rootFormatContext: rootFormatContext, progressiveChunkSize: void 0 === progressiveChunkSize ? 12800 : progressiveChunkSize, status: 0, @@ -2930,6 +2931,7 @@ function createRequest( clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: void 0 === onError ? defaultErrorHandler : onError, onPostpone: void 0 === onPostpone ? noop : onPostpone, onAllReady: void 0 === onAllReady ? noop : onAllReady, @@ -3248,17 +3250,19 @@ function renderElement(request, task, prevThenableState, type, props, ref) { ref = props.fallback; props = props.children; contextType = new Set(); + partial = task.keyPath; partial = { + status: 0, id: null, rootSegmentID: -1, parentFlushed: !1, pendingTasks: 0, - forceClientRender: !1, completedSegments: [], byteSize: 0, fallbackAbortableTasks: contextType, errorDigest: null, - resources: new Set() + resources: new Set(), + keyPath: partial }; var boundarySegment = createPendingSegment( request, @@ -3291,12 +3295,14 @@ function renderElement(request, task, prevThenableState, type, props, ref) { contentRootSegment.chunks.push("\x3c!-- --\x3e")), (contentRootSegment.status = 1), queueCompletedSegment(partial, contentRootSegment), - 0 === partial.pendingTasks) - ) + 0 === partial.pendingTasks && 0 === partial.status) + ) { + partial.status = 1; break a; + } } catch (error) { (contentRootSegment.status = 4), - (partial.forceClientRender = !0), + (partial.status = 4), (JSCompiler_inline_result = logRecoverableError(request, error)), (partial.errorDigest = JSCompiler_inline_result); } finally { @@ -3583,8 +3589,8 @@ function abortTask(task, request, error) { 2 !== request.status && (logRecoverableError(request, error), fatalError(request, error))) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = request.onError(error)), boundary.parentFlushed && request.clientRenderedBoundaries.push(boundary)), @@ -3621,9 +3627,10 @@ function finishedTask(request, boundary, segment) { boundary()); } else boundary.pendingTasks--, - boundary.forceClientRender || + 4 !== boundary.status && (0 === boundary.pendingTasks - ? (segment.parentFlushed && + ? (0 === boundary.status && (boundary.status = 1), + segment.parentFlushed && 1 === segment.status && queueCompletedSegment(boundary, segment), boundary.parentFlushed && @@ -3712,8 +3719,8 @@ function performWork(request$jscomp$1) { null === boundary ? fatalError(request$jscomp$0, error$jscomp$0) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = errorDigest), boundary.parentFlushed && request$jscomp$0.clientRenderedBoundaries.push( @@ -3749,7 +3756,9 @@ function flushSubtree(request, destination, segment) { segment.parentFlushed = !0; switch (segment.status) { case 0: - var segmentID = (segment.id = request.nextSegmentId++); + segment.id = request.nextSegmentId++; + case 5: + var segmentID = segment.id; segment.lastPushedText = !1; segment.textEmbedded = !1; request = request.renderState; @@ -3782,7 +3791,7 @@ function flushSegment(request, destination, segment) { var boundary = segment.boundary; if (null === boundary) return flushSubtree(request, destination, segment); boundary.parentFlushed = !0; - if (boundary.forceClientRender) + if (4 === boundary.status) return ( request.renderState.generateStaticMarkup || ((boundary = boundary.errorDigest), @@ -3800,19 +3809,21 @@ function flushSegment(request, destination, segment) { : destination.push("\x3c!--/$--\x3e")), request ); - if (0 < boundary.pendingTasks) { + if (1 !== boundary.status) { + if (0 === boundary.status) { + var JSCompiler_inline_result = request.renderState; + var generatedID = request.resumableState.nextSuspenseID++; + JSCompiler_inline_result = + JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); + boundary.id = JSCompiler_inline_result; + } boundary.rootSegmentID = request.nextSegmentId++; 0 < boundary.completedSegments.length && request.partialBoundaries.push(boundary); - var JSCompiler_inline_result = request.renderState; - var generatedID = request.resumableState.nextSuspenseID++; - JSCompiler_inline_result = - JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); - boundary = boundary.id = JSCompiler_inline_result; writeStartPendingSuspenseBoundary( destination, request.renderState, - boundary + boundary.id ); flushSubtree(request, destination, segment); return destination.push("\x3c!--/$--\x3e"); @@ -4398,4 +4409,4 @@ exports.renderToString = function (children, options) { 'The server used "renderToString" which does not support Suspense. If you intended for this Suspense boundary to render the fallback content on the server consider throwing an Error somewhere within the Suspense boundary. If you intended to have the server wait for the suspended component please switch to "renderToReadableStream" which supports Suspense on the server' ); }; -exports.version = "18.3.0-www-modern-eac21e34"; +exports.version = "18.3.0-www-modern-ec9c6ec0"; diff --git a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js index afcf10b0727fc..93445c7b39cca 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-dev.modern.js @@ -9454,14 +9454,14 @@ function getStackByComponentStackNode(componentStack) { var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; var ReactCurrentCache = ReactSharedInternals.ReactCurrentCache; var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; // Linked list representing the identity of a component given the component/tag name and key. -// The name might be minified but we assume that it's going to be the same generated name. Typically -// because it's just the same compiled output in practice. +var CLIENT_RENDERED = 4; // if it errors or infinitely suspends var PENDING = 0; var COMPLETED = 1; var FLUSHED = 2; var ABORTED = 3; var ERRORED = 4; +var POSTPONED = 5; var OPEN = 0; var CLOSING = 1; var CLOSED = 2; // This is a default heuristic for how to split up the HTML content into progressive @@ -9511,6 +9511,7 @@ function createRequest( flushScheduled: false, resumableState: resumableState, renderState: renderState, + rootFormatContext: rootFormatContext, progressiveChunkSize: progressiveChunkSize === undefined ? DEFAULT_PROGRESSIVE_CHUNK_SIZE @@ -9526,6 +9527,7 @@ function createRequest( clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: onError === undefined ? defaultErrorHandler : onError, onPostpone: onPostpone === undefined ? noop : onPostpone, onAllReady: onAllReady === undefined ? noop : onAllReady, @@ -9575,18 +9577,19 @@ function pingTask(request, task) { } } -function createSuspenseBoundary(request, fallbackAbortableTasks) { +function createSuspenseBoundary(request, fallbackAbortableTasks, keyPath) { return { + status: PENDING, id: UNINITIALIZED_SUSPENSE_BOUNDARY_ID, rootSegmentID: -1, parentFlushed: false, pendingTasks: 0, - forceClientRender: false, completedSegments: [], byteSize: 0, fallbackAbortableTasks: fallbackAbortableTasks, errorDigest: null, - resources: createBoundaryResources() + resources: createBoundaryResources(), + keyPath: keyPath }; } @@ -9779,7 +9782,11 @@ function renderSuspenseBoundary(request, task, props) { var fallback = props.fallback; var content = props.children; var fallbackAbortSet = new Set(); - var newBoundary = createSuspenseBoundary(request, fallbackAbortSet); + var newBoundary = createSuspenseBoundary( + request, + fallbackAbortSet, + task.keyPath + ); var insertionIndex = parentSegment.chunks.length; // The children of the boundary segment is actually the fallback. var boundarySegment = createPendingSegment( @@ -9834,16 +9841,17 @@ function renderSuspenseBoundary(request, task, props) { contentRootSegment.status = COMPLETED; queueCompletedSegment(newBoundary, contentRootSegment); - if (newBoundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (newBoundary.pendingTasks === 0 && newBoundary.status === PENDING) { + newBoundary.status = COMPLETED; // This must have been the last segment we were waiting on. This boundary is now complete. // Therefore we won't need the fallback. We early return so that we don't have to create // the fallback. + popComponentStackInDEV(task); return; } } catch (error) { contentRootSegment.status = ERRORED; - newBoundary.forceClientRender = true; + newBoundary.status = CLIENT_RENDERED; var errorDigest; { @@ -10785,43 +10793,45 @@ function renderNode(request, task, node, childIndex) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] + : thrownValue; - if (typeof x === "object" && x !== null && typeof x.then === "function") { - var wakeable = x; - var thenableState = getThenableStateAfterSuspending(); - spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + var wakeable = x; + var thenableState = getThenableStateAfterSuspending(); + spawnNewSuspendedTask(request, task, thenableState, wakeable); // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; + { + task.componentStack = previousComponentStack; + } + + return; } + } // Restore the context. We assume that this will be restored by the inner + // functions in case nothing throws so we don't use "finally" here. - return; - } else { - // Restore the context. We assume that this will be restored by the inner - // functions in case nothing throws so we don't use "finally" here. - task.blockedSegment.formatContext = previousFormatContext; - task.legacyContext = previousLegacyContext; - task.context = previousContext; - task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. + task.blockedSegment.formatContext = previousFormatContext; + task.legacyContext = previousLegacyContext; + task.context = previousContext; + task.keyPath = previousKeyPath; // Restore all active ReactContexts to what they were before. - switchContext(previousContext); + switchContext(previousContext); - { - task.componentStack = previousComponentStack; - } // We assume that we don't need the correct context. - // Let's terminate the rest of the tree and don't render any siblings. + { + task.componentStack = previousComponentStack; + } // We assume that we don't need the correct context. + // Let's terminate the rest of the tree and don't render any siblings. - throw x; - } + throw x; } } @@ -10838,8 +10848,8 @@ function erroredTask(request, boundary, segment, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = errorDigest; { @@ -10894,8 +10904,8 @@ function abortTask(task, request, error) { } else { boundary.pendingTasks--; - if (!boundary.forceClientRender) { - boundary.forceClientRender = true; + if (boundary.status !== CLIENT_RENDERED) { + boundary.status = CLIENT_RENDERED; boundary.errorDigest = request.onError(error); { @@ -10982,9 +10992,12 @@ function finishedTask(request, boundary, segment) { } else { boundary.pendingTasks--; - if (boundary.forceClientRender); + if (boundary.status === CLIENT_RENDERED); else if (boundary.pendingTasks === 0) { - // This must have been the last segment we were waiting on. This boundary is now complete. + if (boundary.status === PENDING) { + boundary.status = COMPLETED; + } // This must have been the last segment we were waiting on. This boundary is now complete. + if (segment.parentFlushed) { // Our parent segment already flushed, so we need to schedule this segment to be emitted. // If it is a segment that was aborted, we'll write other content instead so we don't need @@ -11095,18 +11108,23 @@ function retryTask(request, task) { // (unstable) API for suspending. This implementation detail can change // later, once we deprecate the old API in favor of `use`. getSuspendedThenable() - : thrownValue; // $FlowFixMe[method-unbinding] - - if (typeof x === "object" && x !== null && typeof x.then === "function") { - // Something suspended again, let's pick it back up later. - var ping = task.ping; - x.then(ping, ping); - task.thenableState = getThenableStateAfterSuspending(); - } else { - task.abortSet.delete(task); - segment.status = ERRORED; - erroredTask(request, task.blockedBoundary, segment, x); + : thrownValue; + + if (typeof x === "object" && x !== null) { + // $FlowFixMe[method-unbinding] + if (typeof x.then === "function") { + // Something suspended again, let's pick it back up later. + var ping = task.ping; + x.then(ping, ping); + task.thenableState = getThenableStateAfterSuspending(); + return; + } } + + task.abortSet.delete(task); + segment.status = ERRORED; + erroredTask(request, task.blockedBoundary, segment, x); + return; } finally { { setCurrentlyRenderingBoundaryResourcesTarget(request.renderState, null); @@ -11196,7 +11214,11 @@ function flushSubtree(request, destination, segment) { case PENDING: { // We're emitting a placeholder for this segment to be filled in later. // Therefore we'll need to assign it an ID - to refer to it by. - var segmentID = (segment.id = request.nextSegmentId++); // When this segment finally completes it won't be embedded in text since it will flush separately + segment.id = request.nextSegmentId++; // Fallthrough + } + + case POSTPONED: { + var segmentID = segment.id; // When this segment finally completes it won't be embedded in text since it will flush separately segment.lastPushedText = false; segment.textEmbedded = false; @@ -11250,7 +11272,7 @@ function flushSegment(request, destination, segment) { boundary.parentFlushed = true; // This segment is a Suspense boundary. We need to decide whether to // emit the content or the fallback now. - if (boundary.forceClientRender) { + if (boundary.status === CLIENT_RENDERED) { // Emit a client rendered suspense boundary wrapper. // We never queue the inner boundary so we'll never emit its content or partial segments. writeStartClientRenderedSuspenseBoundary( @@ -11263,9 +11285,15 @@ function flushSegment(request, destination, segment) { flushSubtree(request, destination, segment); return writeEndClientRenderedSuspenseBoundary(destination); - } else if (boundary.pendingTasks > 0) { - // This boundary is still loading. Emit a pending suspense boundary wrapper. + } else if (boundary.status !== COMPLETED) { + if (boundary.status === PENDING) { + boundary.id = assignSuspenseBoundaryID( + request.renderState, + request.resumableState + ); + } // This boundary is still loading. Emit a pending suspense boundary wrapper. // Assign an ID to refer to the future content by. + boundary.rootSegmentID = request.nextSegmentId++; if (boundary.completedSegments.length > 0) { @@ -11273,10 +11301,7 @@ function flushSegment(request, destination, segment) { request.partialBoundaries.push(boundary); } /// This is the first time we should have referenced this ID. - var id = (boundary.id = assignSuspenseBoundaryID( - request.renderState, - request.resumableState - )); + var id = boundary.id; writeStartPendingSuspenseBoundary(destination, request.renderState, id); // Flush the fallback. flushSubtree(request, destination, segment); @@ -11572,7 +11597,11 @@ function flushCompletedQueues(request, destination) { request.flushScheduled = false; { - writePostamble(destination, request.resumableState); + // We write the trailing tags but only if don't have any data to resume. + // If we need to resume we'll write the postamble in the resume instead. + { + writePostamble(destination, request.resumableState); + } } { @@ -11588,7 +11617,7 @@ function flushCompletedQueues(request, destination) { } } -function startWork(request) { +function startRender(request) { request.flushScheduled = request.destination !== null; } @@ -11657,7 +11686,7 @@ function flushResources(request) { } function getResumableState(request) { return request.resumableState; -} // Returns the state of a postponed request or null if nothing was postponed. +} function renderToStream(children, options) { var destination = { @@ -11684,7 +11713,7 @@ function renderToStream(children, options) { undefined, undefined ); - startWork(request); + startRender(request); if (destination.fatal) { throw destination.error; diff --git a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js index d93d9aa02fc77..b91d5c66e13bb 100644 --- a/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js +++ b/compiled/facebook-www/ReactDOMServerStreaming-prod.modern.js @@ -3048,17 +3048,19 @@ function renderElement(request, task, prevThenableState, type, props, ref) { ref = props.fallback; props = props.children; contextType = new Set(); + partial = task.keyPath; partial = { + status: 0, id: null, rootSegmentID: -1, parentFlushed: !1, pendingTasks: 0, - forceClientRender: !1, completedSegments: [], byteSize: 0, fallbackAbortableTasks: contextType, errorDigest: null, - resources: new Set() + resources: new Set(), + keyPath: partial }; var boundarySegment = createPendingSegment( request, @@ -3090,12 +3092,14 @@ function renderElement(request, task, prevThenableState, type, props, ref) { contentRootSegment.chunks.push("\x3c!-- --\x3e"), (contentRootSegment.status = 1), queueCompletedSegment(partial, contentRootSegment), - 0 === partial.pendingTasks) - ) + 0 === partial.pendingTasks && 0 === partial.status) + ) { + partial.status = 1; break a; + } } catch (error) { (contentRootSegment.status = 4), - (partial.forceClientRender = !0), + (partial.status = 4), (JSCompiler_inline_result = logRecoverableError(request, error)), (partial.errorDigest = JSCompiler_inline_result); } finally { @@ -3387,8 +3391,8 @@ function abortTask(task, request, error) { 2 !== request.status && (logRecoverableError(request, error), fatalError(request, error))) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = request.onError(error)), boundary.parentFlushed && request.clientRenderedBoundaries.push(boundary)), @@ -3427,9 +3431,10 @@ function finishedTask(request, boundary, segment) { boundary()); } else boundary.pendingTasks--, - boundary.forceClientRender || + 4 !== boundary.status && (0 === boundary.pendingTasks - ? (segment.parentFlushed && + ? (0 === boundary.status && (boundary.status = 1), + segment.parentFlushed && 1 === segment.status && queueCompletedSegment(boundary, segment), boundary.parentFlushed && @@ -3449,7 +3454,9 @@ function flushSubtree(request, destination, segment) { segment.parentFlushed = !0; switch (segment.status) { case 0: - var segmentID = (segment.id = request.nextSegmentId++); + segment.id = request.nextSegmentId++; + case 5: + var segmentID = segment.id; segment.lastPushedText = !1; segment.textEmbedded = !1; request = request.renderState; @@ -3484,7 +3491,7 @@ function flushSegment(request, destination, segment) { var boundary = segment.boundary; if (null === boundary) return flushSubtree(request, destination, segment); boundary.parentFlushed = !0; - if (boundary.forceClientRender) + if (4 === boundary.status) (boundary = boundary.errorDigest), writeChunkAndReturn(destination, "\x3c!--$!--\x3e"), writeChunk(destination, ""), flushSubtree(request, destination, segment); - else if (0 < boundary.pendingTasks) { + else if (1 !== boundary.status) { + if (0 === boundary.status) { + var JSCompiler_inline_result = request.renderState; + var generatedID = request.resumableState.nextSuspenseID++; + JSCompiler_inline_result = + JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); + boundary.id = JSCompiler_inline_result; + } boundary.rootSegmentID = request.nextSegmentId++; 0 < boundary.completedSegments.length && request.partialBoundaries.push(boundary); - var JSCompiler_inline_result = request.renderState; - var generatedID = request.resumableState.nextSuspenseID++; - JSCompiler_inline_result = - JSCompiler_inline_result.boundaryPrefix + generatedID.toString(16); - boundary = boundary.id = JSCompiler_inline_result; writeStartPendingSuspenseBoundary( destination, request.renderState, - boundary + boundary.id ); flushSubtree(request, destination, segment); } else if (boundary.byteSize > request.progressiveChunkSize) @@ -4100,8 +4109,8 @@ exports.renderNextChunk = function (stream) { null === boundary ? fatalError(request, error$jscomp$0) : (boundary.pendingTasks--, - boundary.forceClientRender || - ((boundary.forceClientRender = !0), + 4 !== boundary.status && + ((boundary.status = 4), (boundary.errorDigest = errorDigest), boundary.parentFlushed && request.clientRenderedBoundaries.push(boundary))); @@ -4342,6 +4351,7 @@ exports.renderToStream = function (children, options) { flushScheduled: !1, resumableState: bootstrapScriptContent, renderState: bootstrapScripts, + rootFormatContext: bootstrapModules, progressiveChunkSize: void 0 === externalRuntimeConfig ? 12800 : externalRuntimeConfig, status: 0, @@ -4355,6 +4365,7 @@ exports.renderToStream = function (children, options) { clientRenderedBoundaries: [], completedBoundaries: [], partialBoundaries: [], + trackedPostpones: null, onError: void 0 === idPrefix ? defaultErrorHandler : idPrefix, onPostpone: noop, onAllReady: noop,