import type {Source} from 'shared/ReactElementType';
import type {LazyComponent} from 'react/src/ReactLazy';
import {
enableComponentStackLocations,
disableNativeComponentFrames,
} from 'shared/ReactFeatureFlags';
import {
REACT_SUSPENSE_TYPE,
REACT_SUSPENSE_LIST_TYPE,
REACT_FORWARD_REF_TYPE,
REACT_MEMO_TYPE,
REACT_LAZY_TYPE,
} from 'shared/ReactSymbols';
import {disableLogs, reenableLogs} from 'shared/ConsolePatchingDev';
import ReactSharedInternals from 'shared/ReactSharedInternals';
const {ReactCurrentDispatcher} = ReactSharedInternals;
let prefix;
export function describeBuiltInComponentFrame(
name: string,
source: void | null | Source,
ownerFn: void | null | Function,
): string {
if (enableComponentStackLocations) {
if (prefix === undefined) {
try {
throw Error();
} catch (x) {
const match = x.stack.trim().match(/\n( *(at )?)/);
prefix = (match && match[1]) || '';
}
}
return '\n' + prefix + name;
} else {
let ownerName = null;
if (__DEV__ && ownerFn) {
ownerName = ownerFn.displayName || ownerFn.name || null;
}
return describeComponentFrame(name, source, ownerName);
}
}
let reentry = false;
let componentFrameCache;
if (__DEV__) {
const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
componentFrameCache = new PossiblyWeakMap<Function, string>();
}
export function describeNativeComponentFrame(
fn: Function,
construct: boolean,
): string {
if (disableNativeComponentFrames || !fn || reentry) {
return '';
}
if (__DEV__) {
const frame = componentFrameCache.get(fn);
if (frame !== undefined) {
return frame;
}
}
let control;
reentry = true;
const previousPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = undefined;
let previousDispatcher;
if (__DEV__) {
previousDispatcher = ReactCurrentDispatcher.current;
ReactCurrentDispatcher.current = null;
disableLogs();
}
try {
if (construct) {
const Fake = function () {
throw Error();
};
Object.defineProperty(Fake.prototype, 'props', {
set: function () {
throw Error();
},
});
if (typeof Reflect === 'object' && Reflect.construct) {
try {
Reflect.construct(Fake, []);
} catch (x) {
control = x;
}
Reflect.construct(fn, [], Fake);
} else {
try {
Fake.call();
} catch (x) {
control = x;
}
fn.call(Fake.prototype);
}
} else {
try {
throw Error();
} catch (x) {
control = x;
}
fn();
}
} catch (sample) {
if (sample && control && typeof sample.stack === 'string') {
const sampleLines = sample.stack.split('\n');
const controlLines = control.stack.split('\n');
let s = sampleLines.length - 1;
let c = controlLines.length - 1;
while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {
c--;
}
for (; s >= 1 && c >= 0; s--, c--) {
if (sampleLines[s] !== controlLines[c]) {
if (s !== 1 || c !== 1) {
do {
s--;
c--;
if (c < 0 || sampleLines[s] !== controlLines[c]) {
let frame = '\n' + sampleLines[s].replace(' at new ', ' at ');
if (fn.displayName && frame.includes('<anonymous>')) {
frame = frame.replace('<anonymous>', fn.displayName);
}
if (__DEV__) {
if (typeof fn === 'function') {
componentFrameCache.set(fn, frame);
}
}
return frame;
}
} while (s >= 1 && c >= 0);
}
break;
}
}
}
} finally {
reentry = false;
if (__DEV__) {
ReactCurrentDispatcher.current = previousDispatcher;
reenableLogs();
}
Error.prepareStackTrace = previousPrepareStackTrace;
}
const name = fn ? fn.displayName || fn.name : '';
const syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';
if (__DEV__) {
if (typeof fn === 'function') {
componentFrameCache.set(fn, syntheticFrame);
}
}
return syntheticFrame;
}
const BEFORE_SLASH_RE = /^(.*)[\\\/]/;
function describeComponentFrame(
name: null | string,
source: void | null | Source,
ownerName: null | string,
) {
let sourceInfo = '';
if (__DEV__ && source) {
const path = source.fileName;
let fileName = path.replace(BEFORE_SLASH_RE, '');
if (/^index\./.test(fileName)) {
const match = path.match(BEFORE_SLASH_RE);
if (match) {
const pathBeforeSlash = match[1];
if (pathBeforeSlash) {
const folderName = pathBeforeSlash.replace(BEFORE_SLASH_RE, '');
fileName = folderName + '/' + fileName;
}
}
}
sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
} else if (ownerName) {
sourceInfo = ' (created by ' + ownerName + ')';
}
return '\n in ' + (name || 'Unknown') + sourceInfo;
}
export function describeClassComponentFrame(
ctor: Function,
source: void | null | Source,
ownerFn: void | null | Function,
): string {
if (enableComponentStackLocations) {
return describeNativeComponentFrame(ctor, true);
} else {
return describeFunctionComponentFrame(ctor, source, ownerFn);
}
}
export function describeFunctionComponentFrame(
fn: Function,
source: void | null | Source,
ownerFn: void | null | Function,
): string {
if (enableComponentStackLocations) {
return describeNativeComponentFrame(fn, false);
} else {
if (!fn) {
return '';
}
const name = fn.displayName || fn.name || null;
let ownerName = null;
if (__DEV__ && ownerFn) {
ownerName = ownerFn.displayName || ownerFn.name || null;
}
return describeComponentFrame(name, source, ownerName);
}
}
function shouldConstruct(Component: Function) {
const prototype = Component.prototype;
return !!(prototype && prototype.isReactComponent);
}
export function describeUnknownElementTypeFrameInDEV(
type: any,
source: void | null | Source,
ownerFn: void | null | Function,
): string {
if (!__DEV__) {
return '';
}
if (type == null) {
return '';
}
if (typeof type === 'function') {
if (enableComponentStackLocations) {
return describeNativeComponentFrame(type, shouldConstruct(type));
} else {
return describeFunctionComponentFrame(type, source, ownerFn);
}
}
if (typeof type === 'string') {
return describeBuiltInComponentFrame(type, source, ownerFn);
}
switch (type) {
case REACT_SUSPENSE_TYPE:
return describeBuiltInComponentFrame('Suspense', source, ownerFn);
case REACT_SUSPENSE_LIST_TYPE:
return describeBuiltInComponentFrame('SuspenseList', source, ownerFn);
}
if (typeof type === 'object') {
switch (type.$$typeof) {
case REACT_FORWARD_REF_TYPE:
return describeFunctionComponentFrame(type.render, source, ownerFn);
case REACT_MEMO_TYPE:
return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
case REACT_LAZY_TYPE: {
const lazyComponent: LazyComponent<any, any> = (type: any);
const payload = lazyComponent._payload;
const init = lazyComponent._init;
try {
return describeUnknownElementTypeFrameInDEV(
init(payload),
source,
ownerFn,
);
} catch (x) {}
}
}
}
return '';
}