import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import type {CurrentDispatcherRef, WorkTagMap} from '../types';
import type {ReactComponentInfo} from 'shared/ReactTypes';
import {
describeBuiltInComponentFrame,
describeFunctionComponentFrame,
describeClassComponentFrame,
describeDebugInfoFrame,
} from '../shared/DevToolsComponentStackFrame';
import {formatOwnerStack} from '../shared/DevToolsOwnerStack';
export function describeFiber(
workTagMap: WorkTagMap,
workInProgress: Fiber,
currentDispatcherRef: CurrentDispatcherRef,
): string {
const {
HostHoistable,
HostSingleton,
HostComponent,
LazyComponent,
SuspenseComponent,
SuspenseListComponent,
FunctionComponent,
IndeterminateComponent,
SimpleMemoComponent,
ForwardRef,
ClassComponent,
} = workTagMap;
switch (workInProgress.tag) {
case HostHoistable:
case HostSingleton:
case HostComponent:
return describeBuiltInComponentFrame(workInProgress.type);
case LazyComponent:
return describeBuiltInComponentFrame('Lazy');
case SuspenseComponent:
return describeBuiltInComponentFrame('Suspense');
case SuspenseListComponent:
return describeBuiltInComponentFrame('SuspenseList');
case FunctionComponent:
case IndeterminateComponent:
case SimpleMemoComponent:
return describeFunctionComponentFrame(
workInProgress.type,
currentDispatcherRef,
);
case ForwardRef:
return describeFunctionComponentFrame(
workInProgress.type.render,
currentDispatcherRef,
);
case ClassComponent:
return describeClassComponentFrame(
workInProgress.type,
currentDispatcherRef,
);
default:
return '';
}
}
export function getStackByFiberInDevAndProd(
workTagMap: WorkTagMap,
workInProgress: Fiber,
currentDispatcherRef: CurrentDispatcherRef,
): string {
try {
let info = '';
let node: Fiber = workInProgress;
do {
info += describeFiber(workTagMap, node, currentDispatcherRef);
const debugInfo = node._debugInfo;
if (debugInfo) {
for (let i = debugInfo.length - 1; i >= 0; i--) {
const entry = debugInfo[i];
if (typeof entry.name === 'string') {
info += describeDebugInfoFrame(entry.name, entry.env);
}
}
}
node = node.return;
} while (node);
return info;
} catch (x) {
return '\nError generating stack: ' + x.message + '\n' + x.stack;
}
}
export function getSourceLocationByFiber(
workTagMap: WorkTagMap,
fiber: Fiber,
currentDispatcherRef: CurrentDispatcherRef,
): null | string {
try {
const info = describeFiber(workTagMap, fiber, currentDispatcherRef);
if (info !== '') {
return info.slice(1);
}
} catch (x) {
console.error(x);
}
return null;
}
export function supportsConsoleTasks(fiber: Fiber): boolean {
return !!fiber._debugTask;
}
export function supportsOwnerStacks(fiber: Fiber): boolean {
return fiber._debugStack !== undefined;
}
export function getOwnerStackByFiberInDev(
workTagMap: WorkTagMap,
workInProgress: Fiber,
currentDispatcherRef: CurrentDispatcherRef,
): string {
const {
HostHoistable,
HostSingleton,
HostText,
HostComponent,
SuspenseComponent,
SuspenseListComponent,
} = workTagMap;
try {
let info = '';
if (workInProgress.tag === HostText) {
workInProgress = (workInProgress.return: any);
}
switch (workInProgress.tag) {
case HostHoistable:
case HostSingleton:
case HostComponent:
info += describeBuiltInComponentFrame(workInProgress.type);
break;
case SuspenseComponent:
info += describeBuiltInComponentFrame('Suspense');
break;
case SuspenseListComponent:
info += describeBuiltInComponentFrame('SuspenseList');
break;
}
let owner: void | null | Fiber | ReactComponentInfo = workInProgress;
while (owner) {
if (typeof owner.tag === 'number') {
const fiber: Fiber = (owner: any);
owner = fiber._debugOwner;
let debugStack = fiber._debugStack;
if (owner && debugStack) {
if (typeof debugStack !== 'string') {
debugStack = formatOwnerStack(debugStack);
}
if (debugStack !== '') {
info += '\n' + debugStack;
}
}
} else if (owner.debugStack != null) {
const ownerStack: Error = owner.debugStack;
owner = owner.owner;
if (owner && ownerStack) {
info += '\n' + formatOwnerStack(ownerStack);
}
} else {
break;
}
}
return info;
} catch (x) {
return '\nError generating stack: ' + x.message + '\n' + x.stack;
}
}