From b73dae34905251541b1628a138048a624d5097b3 Mon Sep 17 00:00:00 2001 From: Sebastian Markbage Date: Mon, 9 Sep 2024 18:44:54 -0400 Subject: [PATCH] Add react-stack-bottom-frame to console replaying --- .../react-client/src/ReactFlightClient.js | 105 ++++++++++++------ 1 file changed, 69 insertions(+), 36 deletions(-) diff --git a/packages/react-client/src/ReactFlightClient.js b/packages/react-client/src/ReactFlightClient.js index fe23d642a8043..b909567be4a31 100644 --- a/packages/react-client/src/ReactFlightClient.js +++ b/packages/react-client/src/ReactFlightClient.js @@ -2332,6 +2332,61 @@ function getCurrentStackInDEV(): string { return ''; } +const replayConsoleWithCallStack = { + 'react-stack-bottom-frame': function ( + response: Response, + methodName: string, + stackTrace: ReactStackTrace, + owner: null | ReactComponentInfo, + env: string, + args: Array, + ): void { + // There really shouldn't be anything else on the stack atm. + const prevStack = ReactSharedInternals.getCurrentStack; + ReactSharedInternals.getCurrentStack = getCurrentStackInDEV; + currentOwnerInDEV = owner; + + try { + const callStack = buildFakeCallStack( + response, + stackTrace, + env, + bindToConsole(methodName, args, env), + ); + if (owner != null) { + const task = initializeFakeTask(response, owner, env); + initializeFakeStack(response, owner); + if (task !== null) { + task.run(callStack); + return; + } + } + const rootTask = getRootTask(response, env); + if (rootTask != null) { + rootTask.run(callStack); + return; + } + callStack(); + } finally { + ReactSharedInternals.getCurrentStack = prevStack; + } + }, +}; + +const replayConsoleWithCallStackInDEV: ( + response: Response, + methodName: string, + stackTrace: ReactStackTrace, + owner: null | ReactComponentInfo, + env: string, + args: Array, +) => void = __DEV__ + ? // We use this technique to trick minifiers to preserve the function name. + (replayConsoleWithCallStack['react-stack-bottom-frame'].bind( + replayConsoleWithCallStack, + ): any) + : (null: any); + function resolveConsoleEntry( response: Response, value: UninitializedModel, @@ -2361,43 +2416,21 @@ function resolveConsoleEntry( const env = payload[3]; const args = payload.slice(4); - // There really shouldn't be anything else on the stack atm. - const prevStack = ReactSharedInternals.getCurrentStack; - ReactSharedInternals.getCurrentStack = getCurrentStackInDEV; - currentOwnerInDEV = owner; - - try { - if (!enableOwnerStacks) { - // Printing with stack isn't really limited to owner stacks but - // we gate it behind the same flag for now while iterating. - bindToConsole(methodName, args, env)(); - return; - } - const callStack = buildFakeCallStack( - response, - stackTrace, - env, - bindToConsole(methodName, args, env), - ); - if (owner != null) { - const task = initializeFakeTask(response, owner, env); - initializeFakeStack(response, owner); - if (task !== null) { - task.run(callStack); - return; - } - // TODO: Set the current owner so that captureOwnerStack() adds the component - // stack during the replay - if needed. - } - const rootTask = getRootTask(response, env); - if (rootTask != null) { - rootTask.run(callStack); - return; - } - callStack(); - } finally { - ReactSharedInternals.getCurrentStack = prevStack; + if (!enableOwnerStacks) { + // Printing with stack isn't really limited to owner stacks but + // we gate it behind the same flag for now while iterating. + bindToConsole(methodName, args, env)(); + return; } + + replayConsoleWithCallStackInDEV( + response, + methodName, + stackTrace, + owner, + env, + args, + ); } function mergeBuffer(