UNPKG

react-native-reanimated

Version:

More powerful alternative to Animated library for React Native.

95 lines (83 loc) 2.95 kB
/* eslint-disable reanimated/use-reanimated-error */ 'use strict'; import type { WorkletStackDetails } from './commonTypes'; type ReanimatedError = Error & 'ReanimatedError'; // signed type interface ReanimatedErrorConstructor extends Error { new (message?: string): ReanimatedError; (message?: string): ReanimatedError; readonly prototype: ReanimatedError; } const ReanimatedErrorConstructor: ReanimatedErrorConstructor = function ReanimatedError(message?: string) { 'worklet'; const prefix = '[Reanimated]'; const errorInstance = new Error(message ? `${prefix} ${message}` : prefix); errorInstance.name = 'ReanimatedError'; return errorInstance; } as ReanimatedErrorConstructor; export { ReanimatedErrorConstructor as ReanimatedError }; /** * Registers `ReanimatedError` in global scope. Use it only for Worklet * runtimes. */ export function registerReanimatedError() { 'worklet'; if (!_WORKLET) { throw new Error( '[Reanimated] registerReanimatedError() must be called on Worklet runtime' ); } (global as Record<string, unknown>).ReanimatedError = ReanimatedErrorConstructor; } const _workletStackDetails = new Map<number, WorkletStackDetails>(); export function registerWorkletStackDetails( hash: number, stackDetails: WorkletStackDetails ) { _workletStackDetails.set(hash, stackDetails); } function getBundleOffset(error: Error): [string, number, number] { const frame = error.stack?.split('\n')?.[0]; if (frame) { const parsedFrame = /@([^@]+):(\d+):(\d+)/.exec(frame); if (parsedFrame) { const [, file, line, col] = parsedFrame; return [file, Number(line), Number(col)]; } } return ['unknown', 0, 0]; } function processStack(stack: string): string { const workletStackEntries = stack.match(/worklet_(\d+):(\d+):(\d+)/g); let result = stack; workletStackEntries?.forEach((match) => { const [, hash, origLine, origCol] = match.split(/:|_/).map(Number); const errorDetails = _workletStackDetails.get(hash); if (!errorDetails) { return; } const [error, lineOffset, colOffset] = errorDetails; const [bundleFile, bundleLine, bundleCol] = getBundleOffset(error); const line = origLine + bundleLine + lineOffset; const col = origCol + bundleCol + colOffset; result = result.replace(match, `${bundleFile}:${line}:${col}`); }); return result; } export function reportFatalErrorOnJS({ message, stack, }: { message: string; stack?: string; }) { const error = new Error(); error.message = message; error.stack = stack ? processStack(stack) : undefined; error.name = 'ReanimatedError'; // @ts-ignore React Native's ErrorUtils implementation extends the Error type with jsEngine field error.jsEngine = 'reanimated'; // @ts-ignore the reportFatalError method is an internal method of ErrorUtils not exposed in the type definitions global.ErrorUtils.reportFatalError(error); }