Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fizz] Include a component stack in prod but only lazily generate it #30132

Merged
merged 1 commit into from
Jul 2, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 25 additions & 29 deletions packages/react-server/src/ReactFizzServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -904,27 +904,23 @@ type ThrownInfo = {
export type ErrorInfo = ThrownInfo;
export type PostponeInfo = ThrownInfo;

// While we track component stacks in prod all the time we only produce a reified stack in dev and
// during prerender in Prod. The reason for this is that the stack is useful for prerender where the timeliness
// of the request is less critical than the observability of the execution. For renders and resumes however we
// prioritize speed of the request.
function getThrownInfo(
request: Request,
node: null | ComponentStackNode,
): ThrownInfo {
if (
node &&
// Always produce a stack in dev
(__DEV__ ||
// Produce a stack in prod if we're in a prerender
request.trackedPostpones !== null)
) {
return {
componentStack: getStackFromNode(node),
};
} else {
return {};
function getThrownInfo(node: null | ComponentStackNode): ThrownInfo {
const errorInfo: ThrownInfo = {};
if (node) {
Object.defineProperty(errorInfo, 'componentStack', {
configurable: true,
enumerable: true,
get() {
// Lazyily generate the stack since it's expensive.
const stack = getStackFromNode(node);
Object.defineProperty(errorInfo, 'componentStack', {
value: stack,
});
return stack;
},
});
}
return errorInfo;
}

function encodeErrorForBoundary(
Expand Down Expand Up @@ -1123,7 +1119,7 @@ function renderSuspenseBoundary(
} catch (error: mixed) {
contentRootSegment.status = ERRORED;
newBoundary.status = CLIENT_RENDERED;
const thrownInfo = getThrownInfo(request, task.componentStack);
const thrownInfo = getThrownInfo(task.componentStack);
let errorDigest;
if (
enablePostpone &&
Expand Down Expand Up @@ -1269,7 +1265,7 @@ function replaySuspenseBoundary(
}
} catch (error: mixed) {
resumedBoundary.status = CLIENT_RENDERED;
const thrownInfo = getThrownInfo(request, task.componentStack);
const thrownInfo = getThrownInfo(task.componentStack);
let errorDigest;
if (
enablePostpone &&
Expand Down Expand Up @@ -2333,7 +2329,7 @@ function replayElement(
// in the original prerender. What's unable to complete is the child
// replay nodes which might be Suspense boundaries which are able to
// absorb the error and we can still continue with siblings.
const thrownInfo = getThrownInfo(request, task.componentStack);
const thrownInfo = getThrownInfo(task.componentStack);
erroredReplay(
request,
task.blockedBoundary,
Expand Down Expand Up @@ -2864,7 +2860,7 @@ function replayFragment(
// replay nodes which might be Suspense boundaries which are able to
// absorb the error and we can still continue with siblings.
// This is an error, stash the component stack if it is null.
const thrownInfo = getThrownInfo(request, task.componentStack);
const thrownInfo = getThrownInfo(task.componentStack);
erroredReplay(
request,
task.blockedBoundary,
Expand Down Expand Up @@ -3457,7 +3453,7 @@ function renderNode(
const trackedPostpones = request.trackedPostpones;

const postponeInstance: Postpone = (x: any);
const thrownInfo = getThrownInfo(request, task.componentStack);
const thrownInfo = getThrownInfo(task.componentStack);
const postponedSegment = injectPostponedHole(
request,
((task: any): RenderTask), // We don't use ReplayTasks in prerenders.
Expand Down Expand Up @@ -3768,7 +3764,7 @@ function abortTask(task: Task, request: Request, error: mixed): void {
boundary.status = CLIENT_RENDERED;
// We construct an errorInfo from the boundary's componentStack so the error in dev will indicate which
// boundary the message is referring to
const errorInfo = getThrownInfo(request, task.componentStack);
const errorInfo = getThrownInfo(task.componentStack);
let errorDigest;
if (
enablePostpone &&
Expand Down Expand Up @@ -4060,15 +4056,15 @@ function retryRenderTask(
task.abortSet.delete(task);
const postponeInstance: Postpone = (x: any);

const postponeInfo = getThrownInfo(request, task.componentStack);
const postponeInfo = getThrownInfo(task.componentStack);
logPostpone(request, postponeInstance.message, postponeInfo);
trackPostpone(request, trackedPostpones, task, segment);
finishedTask(request, task.blockedBoundary, segment);
return;
}
}

const errorInfo = getThrownInfo(request, task.componentStack);
const errorInfo = getThrownInfo(task.componentStack);
task.abortSet.delete(task);
segment.status = ERRORED;
erroredTask(request, task.blockedBoundary, x, errorInfo);
Expand Down Expand Up @@ -4142,7 +4138,7 @@ function retryReplayTask(request: Request, task: ReplayTask): void {
}
task.replay.pendingTasks--;
task.abortSet.delete(task);
const errorInfo = getThrownInfo(request, task.componentStack);
const errorInfo = getThrownInfo(task.componentStack);
erroredReplay(
request,
task.blockedBoundary,
Expand Down