1+ /* eslint-disable complexity */
12/* eslint-disable max-lines */
23// Inspired from Donnie McNeal's solution:
34// https://gist.github.com/wontondon/e8c4bdf2888875e4c755712e99279536
@@ -157,24 +158,49 @@ function sendIndexPath(pathBuilder: string, pathname: string, basename: string):
157158 return [ formattedPath , 'route' ] ;
158159}
159160
160- function pathEndsWithWildcard ( path : string , branch : RouteMatch < string > ) : boolean {
161- return ( path . slice ( - 2 ) === '/*' && branch . route . children && branch . route . children . length > 0 ) || false ;
161+ function pathEndsWithWildcard ( path : string ) : boolean {
162+ return path . endsWith ( '*' ) ;
162163}
163164
164165function pathIsWildcardAndHasChildren ( path : string , branch : RouteMatch < string > ) : boolean {
165- return ( path === '*' && branch . route . children && branch . route . children . length > 0 ) || false ;
166+ return ( pathEndsWithWildcard ( path ) && branch . route . children && branch . route . children . length > 0 ) || false ;
167+ }
168+
169+ function pathIsWildcardWithNoChildren ( path : string , branch : RouteMatch < string > ) : boolean {
170+ return ( pathEndsWithWildcard ( path ) && ( ! branch . route . children || branch . route . children . length === 0 ) ) || false ;
166171}
167172
168173function getNormalizedName (
169174 routes : RouteObject [ ] ,
170175 location : Location ,
171176 branches : RouteMatch [ ] ,
172177 basename : string = '' ,
178+ allRoutes : RouteObject [ ] = routes ,
173179) : [ string , TransactionSource ] {
174180 if ( ! routes || routes . length === 0 ) {
175181 return [ _stripBasename ? stripBasenameFromPathname ( location . pathname , basename ) : location . pathname , 'url' ] ;
176182 }
177183
184+ const matchedRoutes = _matchRoutes ( routes , location ) ;
185+
186+ if ( matchedRoutes ) {
187+ const wildCardRoutes : RouteMatch [ ] = matchedRoutes . filter (
188+ ( match : RouteMatch ) => match . route . path && pathIsWildcardWithNoChildren ( match . route . path , match ) ,
189+ ) ;
190+
191+ for ( const wildCardRoute of wildCardRoutes ) {
192+ const wildCardRouteMatch = _matchRoutes ( allRoutes , location , wildCardRoute . pathnameBase ) ;
193+
194+ if ( wildCardRouteMatch ) {
195+ const [ name , source ] = getNormalizedName ( wildCardRoutes , location , wildCardRouteMatch , basename , allRoutes ) ;
196+
197+ if ( wildCardRoute . pathnameBase && name ) {
198+ return [ wildCardRoute . pathnameBase + name , source ] ;
199+ }
200+ }
201+ }
202+ }
203+
178204 let pathBuilder = '' ;
179205 if ( branches ) {
180206 for ( const branch of branches ) {
@@ -192,20 +218,23 @@ function getNormalizedName(
192218 pathBuilder += newPath ;
193219
194220 // If the path matches the current location, return the path
195- if ( basename + branch . pathname === location . pathname ) {
221+ if (
222+ location . pathname . endsWith ( basename + branch . pathname ) ||
223+ location . pathname . endsWith ( `${ basename } ${ branch . pathname } /` )
224+ ) {
196225 if (
197226 // If the route defined on the element is something like
198227 // <Route path="/stores/:storeId/products/:productId" element={<div>Product</div>} />
199228 // We should check against the branch.pathname for the number of / separators
200229 getNumberOfUrlSegments ( pathBuilder ) !== getNumberOfUrlSegments ( branch . pathname ) &&
201230 // We should not count wildcard operators in the url segments calculation
202- pathBuilder . slice ( - 2 ) !== '/*'
231+ ! pathEndsWithWildcard ( pathBuilder )
203232 ) {
204233 return [ ( _stripBasename ? '' : basename ) + newPath , 'route' ] ;
205234 }
206235
207236 // if the last character of the pathbuilder is a wildcard and there are children, remove the wildcard
208- if ( pathEndsWithWildcard ( pathBuilder , branch ) ) {
237+ if ( pathIsWildcardAndHasChildren ( pathBuilder , branch ) ) {
209238 pathBuilder = pathBuilder . slice ( 0 , - 1 ) ;
210239 }
211240
@@ -225,13 +254,14 @@ function updatePageloadTransaction(
225254 routes : RouteObject [ ] ,
226255 matches ?: AgnosticDataRouteMatch ,
227256 basename ?: string ,
257+ allRoutes ?: RouteObject [ ] ,
228258) : void {
229259 const branches = Array . isArray ( matches )
230260 ? matches
231261 : ( _matchRoutes ( routes , location , basename ) as unknown as RouteMatch [ ] ) ;
232262
233263 if ( branches ) {
234- const [ name , source ] = getNormalizedName ( routes , location , branches , basename ) ;
264+ const [ name , source ] = getNormalizedName ( routes , location , branches , basename , allRoutes ) ;
235265
236266 getCurrentScope ( ) . setTransactionName ( name ) ;
237267
@@ -248,6 +278,7 @@ function handleNavigation(
248278 navigationType : Action ,
249279 matches ?: AgnosticDataRouteMatch ,
250280 basename ?: string ,
281+ allRoutes ?: RouteObject [ ] ,
251282) : void {
252283 const branches = Array . isArray ( matches ) ? matches : _matchRoutes ( routes , location , basename ) ;
253284
@@ -257,7 +288,7 @@ function handleNavigation(
257288 }
258289
259290 if ( ( navigationType === 'PUSH' || navigationType === 'POP' ) && branches ) {
260- const [ name , source ] = getNormalizedName ( routes , location , branches , basename ) ;
291+ const [ name , source ] = getNormalizedName ( routes , location , branches , basename , allRoutes ) ;
261292
262293 startBrowserTracingNavigationSpan ( client , {
263294 name,
@@ -270,6 +301,20 @@ function handleNavigation(
270301 }
271302}
272303
304+ const getChildRoutesRecursively = ( route : RouteObject ) : RouteObject [ ] => {
305+ const routes : RouteObject [ ] = [ ] ;
306+
307+ if ( route . children ) {
308+ route . children . forEach ( child => {
309+ routes . push ( ...getChildRoutesRecursively ( child ) ) ;
310+ } ) ;
311+ }
312+
313+ routes . push ( route ) ;
314+
315+ return routes ;
316+ } ;
317+
273318// eslint-disable-next-line @typescript-eslint/no-explicit-any
274319export function withSentryReactRouterV6Routing < P extends Record < string , any > , R extends React . FC < P > > ( Routes : R ) : R {
275320 if ( ! _useEffect || ! _useLocation || ! _useNavigationType || ! _createRoutesFromChildren || ! _matchRoutes ) {
@@ -281,6 +326,7 @@ export function withSentryReactRouterV6Routing<P extends Record<string, any>, R
281326 return Routes ;
282327 }
283328
329+ const allRoutes : RouteObject [ ] = [ ] ;
284330 let isMountRenderPass : boolean = true ;
285331
286332 const SentryRoutes : React . FC < P > = ( props : P ) => {
@@ -291,11 +337,15 @@ export function withSentryReactRouterV6Routing<P extends Record<string, any>, R
291337 ( ) => {
292338 const routes = _createRoutesFromChildren ( props . children ) as RouteObject [ ] ;
293339
340+ routes . forEach ( route => {
341+ allRoutes . push ( ...getChildRoutesRecursively ( route ) ) ;
342+ } ) ;
343+
294344 if ( isMountRenderPass ) {
295- updatePageloadTransaction ( getActiveRootSpan ( ) , location , routes ) ;
345+ updatePageloadTransaction ( getActiveRootSpan ( ) , location , routes , undefined , undefined , allRoutes ) ;
296346 isMountRenderPass = false ;
297347 } else {
298- handleNavigation ( location , routes , navigationType ) ;
348+ handleNavigation ( location , routes , navigationType , undefined , undefined , allRoutes ) ;
299349 }
300350 } ,
301351 // `props.children` is purposely not included in the dependency array, because we do not want to re-run this effect
@@ -326,6 +376,7 @@ export function wrapUseRoutes(origUseRoutes: UseRoutes): UseRoutes {
326376 }
327377
328378 let isMountRenderPass : boolean = true ;
379+ const allRoutes : RouteObject [ ] = [ ] ;
329380
330381 const SentryRoutes : React . FC < {
331382 children ?: React . ReactNode ;
@@ -349,11 +400,15 @@ export function wrapUseRoutes(origUseRoutes: UseRoutes): UseRoutes {
349400 const normalizedLocation =
350401 typeof stableLocationParam === 'string' ? { pathname : stableLocationParam } : stableLocationParam ;
351402
403+ routes . forEach ( route => {
404+ allRoutes . push ( ...getChildRoutesRecursively ( route ) ) ;
405+ } ) ;
406+
352407 if ( isMountRenderPass ) {
353- updatePageloadTransaction ( getActiveRootSpan ( ) , normalizedLocation , routes ) ;
408+ updatePageloadTransaction ( getActiveRootSpan ( ) , normalizedLocation , routes , undefined , undefined , allRoutes ) ;
354409 isMountRenderPass = false ;
355410 } else {
356- handleNavigation ( normalizedLocation , routes , navigationType ) ;
411+ handleNavigation ( normalizedLocation , routes , navigationType , undefined , undefined , allRoutes ) ;
357412 }
358413 } , [ navigationType , stableLocationParam ] ) ;
359414
0 commit comments