@@ -61,49 +61,24 @@ let _enableAsyncRouteHandlers: boolean = false;
61
61
const CLIENTS_WITH_INSTRUMENT_NAVIGATION = new WeakSet < Client > ( ) ;
62
62
63
63
/**
64
- * Updates a navigation span with the correct route name after lazy routes have been loaded.
64
+ * Adds resolved routes as children to the parent route.
65
+ * Prevents duplicate routes by checking if they already exist.
65
66
*/
66
- export function updateNavigationSpan (
67
- activeRootSpan : Span ,
68
- location : Location ,
69
- allRoutes : RouteObject [ ] ,
70
- forceUpdate = false ,
71
- matchRoutes : MatchRoutes ,
72
- ) : void {
73
- // Check if this span has already been named to avoid multiple updates
74
- // But allow updates if this is a forced update (e.g., when lazy routes are loaded)
75
- const hasBeenNamed =
76
- ! forceUpdate &&
77
- (
78
- activeRootSpan as {
79
- __sentry_navigation_name_set__ ?: boolean ;
80
- }
81
- ) ?. __sentry_navigation_name_set__ ;
82
-
83
- if ( ! hasBeenNamed ) {
84
- // Get fresh branches for the current location with all loaded routes
85
- const currentBranches = matchRoutes ( allRoutes , location ) ;
86
- const [ name , source ] = resolveRouteNameAndSource (
87
- location ,
88
- allRoutes ,
89
- allRoutes ,
90
- ( currentBranches as RouteMatch [ ] ) || [ ] ,
91
- '' ,
92
- ) ;
67
+ export function addResolvedRoutesToParent ( resolvedRoutes : RouteObject [ ] , parentRoute : RouteObject ) : void {
68
+ const existingChildren = parentRoute . children || [ ] ;
93
69
94
- // Only update if we have a valid name and the span hasn't finished
95
- const spanJson = spanToJSON ( activeRootSpan ) ;
96
- if ( name && ! spanJson . timestamp ) {
97
- activeRootSpan . updateName ( name ) ;
98
- activeRootSpan . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , source ) ;
70
+ const newRoutes = resolvedRoutes . filter (
71
+ newRoute =>
72
+ ! existingChildren . some (
73
+ existing =>
74
+ existing === newRoute ||
75
+ ( newRoute . path && existing . path === newRoute . path ) ||
76
+ ( newRoute . id && existing . id === newRoute . id ) ,
77
+ ) ,
78
+ ) ;
99
79
100
- // Mark this span as having its name set to prevent future updates
101
- addNonEnumerableProperty (
102
- activeRootSpan as { __sentry_navigation_name_set__ ?: boolean } ,
103
- '__sentry_navigation_name_set__' ,
104
- true ,
105
- ) ;
106
- }
80
+ if ( newRoutes . length > 0 ) {
81
+ parentRoute . children = [ ...existingChildren , ...newRoutes ] ;
107
82
}
108
83
}
109
84
@@ -185,78 +160,51 @@ export function processResolvedRoutes(
185
160
}
186
161
}
187
162
188
- function wrapPatchRoutesOnNavigation (
189
- opts : Record < string , unknown > | undefined ,
190
- isMemoryRouter = false ,
191
- ) : Record < string , unknown > {
192
- if ( ! opts || ! ( 'patchRoutesOnNavigation' in opts ) || typeof opts . patchRoutesOnNavigation !== 'function' ) {
193
- return opts || { } ;
194
- }
195
-
196
- const originalPatchRoutes = opts . patchRoutesOnNavigation ;
197
- return {
198
- ...opts ,
199
- patchRoutesOnNavigation : async ( args : unknown ) => {
200
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
201
- const targetPath = ( args as any ) ?. path ;
202
-
203
- // For browser router, wrap the patch function to update span during patching
204
- if ( ! isMemoryRouter ) {
205
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
206
- const originalPatch = ( args as any ) ?. patch ;
207
- if ( originalPatch ) {
208
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
209
- ( args as any ) . patch = ( routeId : string , children : RouteObject [ ] ) => {
210
- addRoutesToAllRoutes ( children ) ;
211
- const activeRootSpan = getActiveRootSpan ( ) ;
212
- if ( activeRootSpan && ( spanToJSON ( activeRootSpan ) as { op ?: string } ) . op === 'navigation' ) {
213
- updateNavigationSpan (
214
- activeRootSpan ,
215
- {
216
- pathname : targetPath ,
217
- search : '' ,
218
- hash : '' ,
219
- state : null ,
220
- key : 'default' ,
221
- } ,
222
- Array . from ( allRoutes ) ,
223
- true ,
224
- _matchRoutes ,
225
- ) ;
226
- }
227
- return originalPatch ( routeId , children ) ;
228
- } ;
229
- }
163
+ /**
164
+ * Updates a navigation span with the correct route name after lazy routes have been loaded.
165
+ */
166
+ export function updateNavigationSpan (
167
+ activeRootSpan : Span ,
168
+ location : Location ,
169
+ allRoutes : RouteObject [ ] ,
170
+ forceUpdate = false ,
171
+ matchRoutes : MatchRoutes ,
172
+ ) : void {
173
+ // Check if this span has already been named to avoid multiple updates
174
+ // But allow updates if this is a forced update (e.g., when lazy routes are loaded)
175
+ const hasBeenNamed =
176
+ ! forceUpdate &&
177
+ (
178
+ activeRootSpan as {
179
+ __sentry_navigation_name_set__ ?: boolean ;
230
180
}
181
+ ) ?. __sentry_navigation_name_set__ ;
231
182
232
- const result = await originalPatchRoutes ( args ) ;
183
+ if ( ! hasBeenNamed ) {
184
+ // Get fresh branches for the current location with all loaded routes
185
+ const currentBranches = matchRoutes ( allRoutes , location ) ;
186
+ const [ name , source ] = resolveRouteNameAndSource (
187
+ location ,
188
+ allRoutes ,
189
+ allRoutes ,
190
+ ( currentBranches as RouteMatch [ ] ) || [ ] ,
191
+ '' ,
192
+ ) ;
233
193
234
- // Update navigation span after routes are patched
235
- const activeRootSpan = getActiveRootSpan ( ) ;
236
- if ( activeRootSpan && ( spanToJSON ( activeRootSpan ) as { op ?: string } ) . op === 'navigation' ) {
237
- // For memory routers, we don't have a reliable way to get the current pathname
238
- // without accessing window.location, so we'll use targetPath for both cases
239
- const pathname = targetPath || ( isMemoryRouter ? getGlobalPathname ( ) : undefined ) ;
240
- if ( pathname ) {
241
- updateNavigationSpan (
242
- activeRootSpan ,
243
- {
244
- pathname,
245
- search : '' ,
246
- hash : '' ,
247
- state : null ,
248
- key : 'default' ,
249
- } ,
250
- Array . from ( allRoutes ) ,
251
- false ,
252
- _matchRoutes ,
253
- ) ;
254
- }
255
- }
194
+ // Only update if we have a valid name and the span hasn't finished
195
+ const spanJson = spanToJSON ( activeRootSpan ) ;
196
+ if ( name && ! spanJson . timestamp ) {
197
+ activeRootSpan . updateName ( name ) ;
198
+ activeRootSpan . setAttribute ( SEMANTIC_ATTRIBUTE_SENTRY_SOURCE , source ) ;
256
199
257
- return result ;
258
- } ,
259
- } ;
200
+ // Mark this span as having its name set to prevent future updates
201
+ addNonEnumerableProperty (
202
+ activeRootSpan as { __sentry_navigation_name_set__ ?: boolean } ,
203
+ '__sentry_navigation_name_set__' ,
204
+ true ,
205
+ ) ;
206
+ }
207
+ }
260
208
}
261
209
262
210
/**
@@ -551,6 +499,80 @@ export function createV6CompatibleWrapUseRoutes(origUseRoutes: UseRoutes, versio
551
499
} ;
552
500
}
553
501
502
+ function wrapPatchRoutesOnNavigation (
503
+ opts : Record < string , unknown > | undefined ,
504
+ isMemoryRouter = false ,
505
+ ) : Record < string , unknown > {
506
+ if ( ! opts || ! ( 'patchRoutesOnNavigation' in opts ) || typeof opts . patchRoutesOnNavigation !== 'function' ) {
507
+ return opts || { } ;
508
+ }
509
+
510
+ const originalPatchRoutes = opts . patchRoutesOnNavigation ;
511
+ return {
512
+ ...opts ,
513
+ patchRoutesOnNavigation : async ( args : unknown ) => {
514
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
515
+ const targetPath = ( args as any ) ?. path ;
516
+
517
+ // For browser router, wrap the patch function to update span during patching
518
+ if ( ! isMemoryRouter ) {
519
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
520
+ const originalPatch = ( args as any ) ?. patch ;
521
+ if ( originalPatch ) {
522
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access
523
+ ( args as any ) . patch = ( routeId : string , children : RouteObject [ ] ) => {
524
+ addRoutesToAllRoutes ( children ) ;
525
+ const activeRootSpan = getActiveRootSpan ( ) ;
526
+ if ( activeRootSpan && ( spanToJSON ( activeRootSpan ) as { op ?: string } ) . op === 'navigation' ) {
527
+ updateNavigationSpan (
528
+ activeRootSpan ,
529
+ {
530
+ pathname : targetPath ,
531
+ search : '' ,
532
+ hash : '' ,
533
+ state : null ,
534
+ key : 'default' ,
535
+ } ,
536
+ Array . from ( allRoutes ) ,
537
+ true ,
538
+ _matchRoutes ,
539
+ ) ;
540
+ }
541
+ return originalPatch ( routeId , children ) ;
542
+ } ;
543
+ }
544
+ }
545
+
546
+ const result = await originalPatchRoutes ( args ) ;
547
+
548
+ // Update navigation span after routes are patched
549
+ const activeRootSpan = getActiveRootSpan ( ) ;
550
+ if ( activeRootSpan && ( spanToJSON ( activeRootSpan ) as { op ?: string } ) . op === 'navigation' ) {
551
+ // For memory routers, we don't have a reliable way to get the current pathname
552
+ // without accessing window.location, so we'll use targetPath for both cases
553
+ const pathname = targetPath || ( isMemoryRouter ? getGlobalPathname ( ) : undefined ) ;
554
+ if ( pathname ) {
555
+ updateNavigationSpan (
556
+ activeRootSpan ,
557
+ {
558
+ pathname,
559
+ search : '' ,
560
+ hash : '' ,
561
+ state : null ,
562
+ key : 'default' ,
563
+ } ,
564
+ Array . from ( allRoutes ) ,
565
+ false ,
566
+ _matchRoutes ,
567
+ ) ;
568
+ }
569
+ }
570
+
571
+ return result ;
572
+ } ,
573
+ } ;
574
+ }
575
+
554
576
export function handleNavigation ( opts : {
555
577
location : Location ;
556
578
routes : RouteObject [ ] ;
@@ -788,25 +810,3 @@ export function createNewNavigationSpan(
788
810
) ;
789
811
}
790
812
}
791
-
792
- /**
793
- * Adds resolved routes as children to the parent route.
794
- * Prevents duplicate routes by checking if they already exist.
795
- */
796
- export function addResolvedRoutesToParent ( resolvedRoutes : RouteObject [ ] , parentRoute : RouteObject ) : void {
797
- const existingChildren = parentRoute . children || [ ] ;
798
-
799
- const newRoutes = resolvedRoutes . filter (
800
- newRoute =>
801
- ! existingChildren . some (
802
- existing =>
803
- existing === newRoute ||
804
- ( newRoute . path && existing . path === newRoute . path ) ||
805
- ( newRoute . id && existing . id === newRoute . id ) ,
806
- ) ,
807
- ) ;
808
-
809
- if ( newRoutes . length > 0 ) {
810
- parentRoute . children = [ ...existingChildren , ...newRoutes ] ;
811
- }
812
- }
0 commit comments