@@ -61,49 +61,24 @@ let _enableAsyncRouteHandlers: boolean = false;
6161const CLIENTS_WITH_INSTRUMENT_NAVIGATION = new WeakSet < Client > ( ) ;
6262
6363/**
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.
6566 */
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 || [ ] ;
9369
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+ ) ;
9979
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 ] ;
10782 }
10883}
10984
@@ -185,78 +160,51 @@ export function processResolvedRoutes(
185160 }
186161}
187162
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 ;
230180 }
181+ ) ?. __sentry_navigation_name_set__ ;
231182
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+ ) ;
233193
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 ) ;
256199
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+ }
260208}
261209
262210/**
@@ -551,6 +499,80 @@ export function createV6CompatibleWrapUseRoutes(origUseRoutes: UseRoutes, versio
551499 } ;
552500}
553501
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+
554576export function handleNavigation ( opts : {
555577 location : Location ;
556578 routes : RouteObject [ ] ;
@@ -788,25 +810,3 @@ export function createNewNavigationSpan(
788810 ) ;
789811 }
790812}
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