import type {Fiber, FiberRoot} from './ReactInternalTypes';
import type {CapturedValue} from './ReactCapturedValue';
import getComponentNameFromFiber from 'react-reconciler/src/getComponentNameFromFiber';
import {ClassComponent} from './ReactWorkTags';
import reportGlobalError from 'shared/reportGlobalError';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {enableOwnerStacks} from 'shared/ReactFeatureFlags';
import {bindToConsole} from './ReactFiberConfig';
let componentName: null | string = null;
let errorBoundaryName: null | string = null;
export function defaultOnUncaughtError(
error: mixed,
errorInfo: {+componentStack?: ?string},
): void {
reportGlobalError(error);
if (__DEV__) {
const componentNameMessage = componentName
? `An error occurred in the <${componentName}> component.`
: 'An error occurred in one of your React components.';
const errorBoundaryMessage =
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.';
const prevGetCurrentStack = ReactSharedInternals.getCurrentStack;
if (!enableOwnerStacks) {
const componentStack =
errorInfo.componentStack != null ? errorInfo.componentStack : '';
ReactSharedInternals.getCurrentStack = function () {
return componentStack;
};
}
try {
console.warn(
'%s\n\n%s\n',
componentNameMessage,
errorBoundaryMessage,
);
} finally {
if (!enableOwnerStacks) {
ReactSharedInternals.getCurrentStack = prevGetCurrentStack;
}
}
}
}
export function defaultOnCaughtError(
error: mixed,
errorInfo: {
+componentStack?: ?string,
+errorBoundary?: ?React$Component<any, any>,
},
): void {
if (__DEV__) {
const componentNameMessage = componentName
? `The above error occurred in the <${componentName}> component.`
: 'The above error occurred in one of your React components.';
const recreateMessage =
`React will try to recreate this component tree from scratch ` +
`using the error boundary you provided, ${
errorBoundaryName || 'Anonymous'
}.`;
const prevGetCurrentStack = ReactSharedInternals.getCurrentStack;
if (!enableOwnerStacks) {
const componentStack =
errorInfo.componentStack != null ? errorInfo.componentStack : '';
ReactSharedInternals.getCurrentStack = function () {
return componentStack;
};
}
try {
if (
typeof error === 'object' &&
error !== null &&
typeof error.environmentName === 'string'
) {
bindToConsole(
'error',
[
'%o\n\n%s\n\n%s\n',
error,
componentNameMessage,
recreateMessage,
],
error.environmentName,
)();
} else {
console.error(
'%o\n\n%s\n\n%s\n',
error,
componentNameMessage,
recreateMessage,
);
}
} finally {
if (!enableOwnerStacks) {
ReactSharedInternals.getCurrentStack = prevGetCurrentStack;
}
}
} else {
console['error'](error);
}
}
export function defaultOnRecoverableError(
error: mixed,
errorInfo: {+componentStack?: ?string},
) {
reportGlobalError(error);
}
export function logUncaughtError(
root: FiberRoot,
errorInfo: CapturedValue<mixed>,
): void {
try {
if (__DEV__) {
componentName = errorInfo.source
? getComponentNameFromFiber(errorInfo.source)
: null;
errorBoundaryName = null;
}
const error = (errorInfo.value: any);
if (__DEV__ && ReactSharedInternals.actQueue !== null) {
ReactSharedInternals.thrownErrors.push(error);
return;
}
const onUncaughtError = root.onUncaughtError;
onUncaughtError(error, {
componentStack: errorInfo.stack,
});
} catch (e) {
setTimeout(() => {
throw e;
});
}
}
export function logCaughtError(
root: FiberRoot,
boundary: Fiber,
errorInfo: CapturedValue<mixed>,
): void {
try {
if (__DEV__) {
componentName = errorInfo.source
? getComponentNameFromFiber(errorInfo.source)
: null;
errorBoundaryName = getComponentNameFromFiber(boundary);
}
const error = (errorInfo.value: any);
const onCaughtError = root.onCaughtError;
onCaughtError(error, {
componentStack: errorInfo.stack,
errorBoundary:
boundary.tag === ClassComponent
? boundary.stateNode
: null,
});
} catch (e) {
setTimeout(() => {
throw e;
});
}
}