@@ -76,7 +76,7 @@ export class ReactNativeErrorPlugin implements IEventPlugin {
7676 }
7777
7878 const frames : StackFrameInfo [ ] = [ ] ;
79- const lines = stack . split ( / \r ? \n / ) ;
79+ const lines = stack . replaceAll ( "\r\n" , "\n" ) . replaceAll ( "\r" , "\n" ) . split ( "\n" ) ;
8080 for ( const line of lines ) {
8181 const frame = this . parseStackFrame ( line . trim ( ) ) ;
8282 if ( frame ) {
@@ -92,24 +92,27 @@ export class ReactNativeErrorPlugin implements IEventPlugin {
9292 return null ;
9393 }
9494
95- const reactNativeFrame = / ^ a t \s + ( .* ?) \( \) \s + i n \s + ( .* ) $ / . exec ( line ) ;
96- if ( reactNativeFrame ) {
97- return this . createStackFrame ( reactNativeFrame [ 1 ] , reactNativeFrame [ 2 ] ) ;
98- }
95+ if ( line . startsWith ( "at " ) ) {
96+ const frame = line . slice ( 3 ) . trim ( ) ;
97+ const reactNativeMarker = "() in " ;
98+ const reactNativeMarkerIndex = frame . indexOf ( reactNativeMarker ) ;
99+ if ( reactNativeMarkerIndex > 0 ) {
100+ return this . createStackFrame ( frame . slice ( 0 , reactNativeMarkerIndex ) , frame . slice ( reactNativeMarkerIndex + reactNativeMarker . length ) ) ;
101+ }
99102
100- const atFrame = / ^ a t \s + ( .* ?) \s + \( ( .* ) \) $ / . exec ( line ) ;
101- if ( atFrame ) {
102- return this . createStackFrame ( atFrame [ 1 ] , atFrame [ 2 ] ) ;
103- }
103+ if ( frame . endsWith ( ")" ) ) {
104+ const locationStartIndex = frame . lastIndexOf ( " (" ) ;
105+ if ( locationStartIndex > 0 ) {
106+ return this . createStackFrame ( frame . slice ( 0 , locationStartIndex ) , frame . slice ( locationStartIndex + 2 , - 1 ) ) ;
107+ }
108+ }
104109
105- const anonymousAtFrame = / ^ a t \s + ( .* ) $ / . exec ( line ) ;
106- if ( anonymousAtFrame ) {
107- return this . createStackFrame ( "<anonymous>" , anonymousAtFrame [ 1 ] ) ;
110+ return this . createStackFrame ( "<anonymous>" , frame ) ;
108111 }
109112
110- const hermesFrame = / ^ ( . * ? ) @ ( . * ) $ / . exec ( line ) ;
111- if ( hermesFrame ) {
112- return this . createStackFrame ( hermesFrame [ 1 ] || "<anonymous>" , hermesFrame [ 2 ] ) ;
113+ const hermesLocationIndex = line . indexOf ( "@" ) ;
114+ if ( hermesLocationIndex >= 0 ) {
115+ return this . createStackFrame ( line . slice ( 0 , hermesLocationIndex ) || "<anonymous>" , line . slice ( hermesLocationIndex + 1 ) ) ;
113116 }
114117
115118 return null ;
@@ -130,7 +133,7 @@ export class ReactNativeErrorPlugin implements IEventPlugin {
130133 }
131134
132135 private parseLocation ( location : string ) : ParsedLocation {
133- const trimmedLocation = location . trim ( ) ;
136+ let trimmedLocation = location . trim ( ) ;
134137 if ( trimmedLocation === "native" || trimmedLocation === "<anonymous>" ) {
135138 return {
136139 fileName : trimmedLocation ,
@@ -140,53 +143,138 @@ export class ReactNativeErrorPlugin implements IEventPlugin {
140143 } ;
141144 }
142145
143- const reactNativeLineAndColumn = / ^ (?: a d d r e s s a t ) ? ( .* ) : l i n e ( \d + ) : c o l ( \d + ) $ / . exec ( trimmedLocation ) ;
144- if ( reactNativeLineAndColumn ) {
145- return {
146- fileName : reactNativeLineAndColumn [ 1 ] ,
147- lineNumber : Number ( reactNativeLineAndColumn [ 2 ] ) ,
148- column : Number ( reactNativeLineAndColumn [ 3 ] ) ,
149- isNative : false
150- } ;
146+ if ( trimmedLocation . startsWith ( "address at " ) ) {
147+ trimmedLocation = trimmedLocation . slice ( "address at " . length ) ;
151148 }
152149
153- const lineAndColumn = / ^ ( .* ) : ( \d + ) : ( \d + ) $ / . exec ( trimmedLocation ) ;
150+ const reactNativeLocation = this . tryParseReactNativeLocation ( trimmedLocation ) ;
151+ if ( reactNativeLocation ) {
152+ return reactNativeLocation ;
153+ }
154+
155+ const lineAndColumn = this . tryParseDelimitedLocation ( trimmedLocation , true ) ;
154156 if ( lineAndColumn ) {
155- return {
156- fileName : lineAndColumn [ 1 ] ,
157- lineNumber : Number ( lineAndColumn [ 2 ] ) ,
158- column : Number ( lineAndColumn [ 3 ] ) ,
159- isNative : false
160- } ;
157+ return lineAndColumn ;
161158 }
162159
163- const lineOnly = / ^ ( . * ) : ( \d + ) $ / . exec ( trimmedLocation ) ;
160+ const lineOnly = this . tryParseDelimitedLocation ( trimmedLocation , false ) ;
164161 if ( lineOnly ) {
162+ return lineOnly ;
163+ }
164+
165+ return {
166+ fileName : trimmedLocation ,
167+ lineNumber : 0 ,
168+ column : 0 ,
169+ isNative : false
170+ } ;
171+ }
172+
173+ private tryParseReactNativeLocation ( location : string ) : ParsedLocation | null {
174+ const columnMarker = ":col " ;
175+ const lineMarker = ":line " ;
176+ const columnMarkerIndex = location . lastIndexOf ( columnMarker ) ;
177+ if ( columnMarkerIndex <= 0 ) {
178+ return null ;
179+ }
180+
181+ const lineMarkerIndex = location . lastIndexOf ( lineMarker , columnMarkerIndex ) ;
182+ if ( lineMarkerIndex <= 0 ) {
183+ return null ;
184+ }
185+
186+ const lineNumber = this . tryParseNonNegativeInteger ( location . slice ( lineMarkerIndex + lineMarker . length , columnMarkerIndex ) ) ;
187+ const column = this . tryParseNonNegativeInteger ( location . slice ( columnMarkerIndex + columnMarker . length ) ) ;
188+ if ( lineNumber === null || column === null ) {
189+ return null ;
190+ }
191+
192+ return {
193+ fileName : location . slice ( 0 , lineMarkerIndex ) ,
194+ lineNumber,
195+ column,
196+ isNative : false
197+ } ;
198+ }
199+
200+ private tryParseDelimitedLocation ( location : string , hasColumn : boolean ) : ParsedLocation | null {
201+ const lastDelimiterIndex = location . lastIndexOf ( ":" ) ;
202+ if ( lastDelimiterIndex <= 0 ) {
203+ return null ;
204+ }
205+
206+ const lastNumber = this . tryParseNonNegativeInteger ( location . slice ( lastDelimiterIndex + 1 ) ) ;
207+ if ( lastNumber === null ) {
208+ return null ;
209+ }
210+
211+ if ( ! hasColumn ) {
165212 return {
166- fileName : lineOnly [ 1 ] ,
167- lineNumber : Number ( lineOnly [ 2 ] ) ,
213+ fileName : location . slice ( 0 , lastDelimiterIndex ) ,
214+ lineNumber : lastNumber ,
168215 column : 0 ,
169216 isNative : false
170217 } ;
171218 }
172219
220+ const lineDelimiterIndex = location . lastIndexOf ( ":" , lastDelimiterIndex - 1 ) ;
221+ if ( lineDelimiterIndex <= 0 ) {
222+ return null ;
223+ }
224+
225+ const lineNumber = this . tryParseNonNegativeInteger ( location . slice ( lineDelimiterIndex + 1 , lastDelimiterIndex ) ) ;
226+ if ( lineNumber === null ) {
227+ return null ;
228+ }
229+
173230 return {
174- fileName : trimmedLocation ,
175- lineNumber : 0 ,
176- column : 0 ,
231+ fileName : location . slice ( 0 , lineDelimiterIndex ) ,
232+ lineNumber,
233+ column : lastNumber ,
177234 isNative : false
178235 } ;
179236 }
180237
238+ private tryParseNonNegativeInteger ( value : string ) : number | null {
239+ if ( ! value ) {
240+ return null ;
241+ }
242+
243+ for ( let i = 0 ; i < value . length ; i ++ ) {
244+ const charCode = value . charCodeAt ( i ) ;
245+ if ( charCode < 48 || charCode > 57 ) {
246+ return null ;
247+ }
248+ }
249+
250+ return Number ( value ) ;
251+ }
252+
181253 private normalizeFrameName ( name : string ) : string {
182- const trimmedName = name . trim ( ) . replace ( / \( \) $ / , "" ) ;
254+ const trimmed = name . trim ( ) ;
255+ const trimmedName = trimmed . endsWith ( "()" ) ? trimmed . slice ( 0 , - 2 ) : trimmed ;
183256 if ( ! trimmedName || trimmedName === "?" ) {
184257 return "<anonymous>" ;
185258 }
186259
187- const syntheticHermesName = / ^ \? a n o n _ \d + _ ? ( .* ) $ / . exec ( trimmedName ) ;
188- if ( syntheticHermesName ) {
189- return syntheticHermesName [ 1 ] || "<anonymous>" ;
260+ const syntheticHermesPrefix = "?anon_" ;
261+ if ( trimmedName . startsWith ( syntheticHermesPrefix ) ) {
262+ let suffixIndex = syntheticHermesPrefix . length ;
263+ while ( suffixIndex < trimmedName . length ) {
264+ const charCode = trimmedName . charCodeAt ( suffixIndex ) ;
265+ if ( charCode < 48 || charCode > 57 ) {
266+ break ;
267+ }
268+
269+ suffixIndex ++ ;
270+ }
271+
272+ if ( trimmedName [ suffixIndex ] === "_" ) {
273+ const suffix = trimmedName . slice ( suffixIndex + 1 ) ;
274+ return suffix || "<anonymous>" ;
275+ }
276+
277+ return "<anonymous>" ;
190278 }
191279
192280 return trimmedName ;
0 commit comments