|
1 | 1 | import type { StackFrame as SentryStackFrame } from '@sentry/core'; |
2 | 2 |
|
3 | | -import { debug } from '@sentry/core'; |
| 3 | +import { debug, parseStackFrames } from '@sentry/core'; |
| 4 | +import { defaultStackParser } from '@sentry/react'; |
4 | 5 |
|
5 | 6 | import type * as ReactNative from '../vendor/react-native'; |
6 | 7 |
|
@@ -72,13 +73,65 @@ function getSentryMetroSourceContextUrl(): string | undefined { |
72 | 73 | } |
73 | 74 |
|
74 | 75 | /** |
75 | | - * Loads and calls RN Core Devtools parseErrorStack function. |
| 76 | + * Converts Sentry StackFrames to React Native StackFrames. |
| 77 | + * This is the reverse of convertReactNativeFramesToSentryFrames in debugsymbolicator.ts |
| 78 | + */ |
| 79 | +function convertSentryFramesToReactNativeFrames(frames: SentryStackFrame[]): Array<ReactNative.StackFrame> { |
| 80 | + // Reverse the frames because Sentry parser returns them in reverse order compared to RN |
| 81 | + return frames.reverse().map((frame): ReactNative.StackFrame => { |
| 82 | + const rnFrame: ReactNative.StackFrame = { |
| 83 | + methodName: frame.function || '?', |
| 84 | + }; |
| 85 | + |
| 86 | + if (frame.filename !== undefined) { |
| 87 | + rnFrame.file = frame.filename; |
| 88 | + } |
| 89 | + |
| 90 | + if (frame.lineno !== undefined) { |
| 91 | + rnFrame.lineNumber = frame.lineno; |
| 92 | + } |
| 93 | + |
| 94 | + if (frame.colno !== undefined) { |
| 95 | + rnFrame.column = frame.colno; |
| 96 | + } |
| 97 | + |
| 98 | + return rnFrame; |
| 99 | + }); |
| 100 | +} |
| 101 | + |
| 102 | +/** |
| 103 | + * Parses an error stack string into React Native StackFrames. |
| 104 | + * Uses RN Devtools parseErrorStack by default for compatibility. |
| 105 | + * Falls back to Sentry's built-in stack parser if Devtools is not available. |
| 106 | + * |
| 107 | + * @param errorStack - Raw stack trace string from Error.stack |
| 108 | + * @returns Array of React Native StackFrame objects |
76 | 109 | */ |
77 | 110 | export function parseErrorStack(errorStack: string): Array<ReactNative.StackFrame> { |
78 | | - if (!ReactNativeLibraries.Devtools) { |
79 | | - throw new Error('React Native Devtools not available.'); |
| 111 | + // Try using RN Devtools first for maximum compatibility with existing tooling |
| 112 | + if (ReactNativeLibraries.Devtools?.parseErrorStack) { |
| 113 | + try { |
| 114 | + return ReactNativeLibraries.Devtools.parseErrorStack(errorStack); |
| 115 | + } catch (error) { |
| 116 | + debug.warn('RN Devtools parseErrorStack failed, falling back to Sentry stack parser'); |
| 117 | + } |
| 118 | + } |
| 119 | + |
| 120 | + // Fallback: Use Sentry's stack parser (works without RN Devtools dependency) |
| 121 | + try { |
| 122 | + // Create a temporary Error object with the stack |
| 123 | + const error = new Error(); |
| 124 | + error.stack = errorStack; |
| 125 | + |
| 126 | + // Use Sentry's parser to parse the stack |
| 127 | + const sentryFrames = parseStackFrames(defaultStackParser, error); |
| 128 | + |
| 129 | + // Convert Sentry frames back to RN format |
| 130 | + return convertSentryFramesToReactNativeFrames(sentryFrames); |
| 131 | + } catch (error) { |
| 132 | + debug.error('Failed to parse error stack:', error); |
| 133 | + return []; |
80 | 134 | } |
81 | | - return ReactNativeLibraries.Devtools.parseErrorStack(errorStack); |
82 | 135 | } |
83 | 136 |
|
84 | 137 | /** |
|
0 commit comments