Files
react/packages/react-server/src/ReactFizzComponentStack.js
Ricky e0fe347967 [flags] remove enableOwnerStacks (#32426)
Bassed off: https://github.com/facebook/react/pull/32425

Wait to land internally.

[Commit to
review.](66aa6a4dbb)

This has landed everywhere
2025-03-04 12:34:34 -05:00

200 lines
6.4 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
import type {ReactComponentInfo} from 'shared/ReactTypes';
import type {LazyComponent} from 'react/src/ReactLazy';
import {
describeBuiltInComponentFrame,
describeFunctionComponentFrame,
describeClassComponentFrame,
describeDebugInfoFrame,
} from 'shared/ReactComponentStackFrame';
import {
REACT_FORWARD_REF_TYPE,
REACT_MEMO_TYPE,
REACT_LAZY_TYPE,
REACT_SUSPENSE_LIST_TYPE,
REACT_SUSPENSE_TYPE,
REACT_VIEW_TRANSITION_TYPE,
} from 'shared/ReactSymbols';
import {enableViewTransition} from 'shared/ReactFeatureFlags';
import {formatOwnerStack} from 'shared/ReactOwnerStackFrames';
export type ComponentStackNode = {
parent: null | ComponentStackNode,
type:
| symbol
| string
| Function
| LazyComponent<any, any>
| ReactComponentInfo,
owner?: null | ReactComponentInfo | ComponentStackNode, // DEV only
stack?: null | string | Error, // DEV only
};
function shouldConstruct(Component: any) {
return Component.prototype && Component.prototype.isReactComponent;
}
function describeComponentStackByType(
type:
| symbol
| string
| Function
| LazyComponent<any, any>
| ReactComponentInfo,
): string {
if (typeof type === 'string') {
return describeBuiltInComponentFrame(type);
}
if (typeof type === 'function') {
if (shouldConstruct(type)) {
return describeClassComponentFrame(type);
} else {
return describeFunctionComponentFrame(type);
}
}
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE: {
return describeFunctionComponentFrame((type: any).render);
}
case REACT_MEMO_TYPE: {
return describeFunctionComponentFrame((type: any).type);
}
case REACT_LAZY_TYPE: {
const lazyComponent: LazyComponent<any, any> = (type: any);
const payload = lazyComponent._payload;
const init = lazyComponent._init;
try {
type = init(payload);
} catch (x) {
// TODO: When we support Thenables as component types we should rename this.
return describeBuiltInComponentFrame('Lazy');
}
return describeComponentStackByType(type);
}
}
if (typeof type.name === 'string') {
return describeDebugInfoFrame(type.name, type.env);
}
}
switch (type) {
case REACT_SUSPENSE_LIST_TYPE: {
return describeBuiltInComponentFrame('SuspenseList');
}
case REACT_SUSPENSE_TYPE: {
return describeBuiltInComponentFrame('Suspense');
}
case REACT_VIEW_TRANSITION_TYPE:
if (enableViewTransition) {
return describeBuiltInComponentFrame('ViewTransition');
}
}
return '';
}
export function getStackByComponentStackNode(
componentStack: ComponentStackNode,
): string {
try {
let info = '';
let node: ComponentStackNode = componentStack;
do {
info += describeComponentStackByType(node.type);
// $FlowFixMe[incompatible-type] we bail out when we get a null
node = node.parent;
} while (node);
return info;
} catch (x) {
return '\nError generating stack: ' + x.message + '\n' + x.stack;
}
}
function describeFunctionComponentFrameWithoutLineNumber(fn: Function): string {
// We use this because we don't actually want to describe the line of the component
// but just the component name.
const name = fn ? fn.displayName || fn.name : '';
return name ? describeBuiltInComponentFrame(name) : '';
}
export function getOwnerStackByComponentStackNodeInDev(
componentStack: ComponentStackNode,
): string {
if (!__DEV__) {
return '';
}
try {
let info = '';
// The owner stack of the current component will be where it was created, i.e. inside its owner.
// There's no actual name of the currently executing component. Instead, that is available
// on the regular stack that's currently executing. However, for built-ins there is no such
// named stack frame and it would be ignored as being internal anyway. Therefore we add
// add one extra frame just to describe the "current" built-in component by name.
// Similarly, if there is no owner at all, then there's no stack frame so we add the name
// of the root component to the stack to know which component is currently executing.
if (typeof componentStack.type === 'string') {
info += describeBuiltInComponentFrame(componentStack.type);
} else if (typeof componentStack.type === 'function') {
if (!componentStack.owner) {
// Only if we have no other data about the callsite do we add
// the component name as the single stack frame.
info += describeFunctionComponentFrameWithoutLineNumber(
componentStack.type,
);
}
} else {
if (!componentStack.owner) {
info += describeComponentStackByType(componentStack.type);
}
}
let owner: void | null | ComponentStackNode | ReactComponentInfo =
componentStack;
while (owner) {
let ownerStack: ?string = null;
if (owner.debugStack != null) {
// Server Component
// TODO: Should we stash this somewhere for caching purposes?
ownerStack = formatOwnerStack(owner.debugStack);
owner = owner.owner;
} else {
// Client Component
const node: ComponentStackNode = (owner: any);
if (node.stack != null) {
if (typeof node.stack !== 'string') {
ownerStack = node.stack = formatOwnerStack(node.stack);
} else {
ownerStack = node.stack;
}
}
owner = owner.owner;
}
// If we don't actually print the stack if there is no owner of this JSX element.
// In a real app it's typically not useful since the root app is always controlled
// by the framework. These also tend to have noisy stacks because they're not rooted
// in a React render but in some imperative bootstrapping code. It could be useful
// if the element was created in module scope. E.g. hoisted. We could add a a single
// stack frame for context for example but it doesn't say much if that's a wrapper.
if (owner && ownerStack) {
info += '\n' + ownerStack;
}
}
return info;
} catch (x) {
return '\nError generating stack: ' + x.message + '\n' + x.stack;
}
}