@@ -17,6 +17,8 @@ import {
1717 buildRouteTreeConfig ,
1818 checkFileExists ,
1919 checkRouteFullPathUniqueness ,
20+ countRoutePathSegments ,
21+ countSlashSeparatedParts ,
2022 createRouteNodesByFullPath ,
2123 createRouteNodesById ,
2224 createRouteNodesByTo ,
@@ -33,9 +35,8 @@ import {
3335 removeExt ,
3436 removeGroups ,
3537 removeLastSegmentFromPath ,
36- removeLayoutSegmentsWithEscape ,
38+ removeLayoutSegmentsAndUnderscoresWithEscape ,
3739 removeTrailingSlash ,
38- removeUnderscoresWithEscape ,
3940 replaceBackslash ,
4041 trimPathLeft ,
4142} from './utils'
@@ -51,6 +52,7 @@ import type {
5152 HandleNodeAccumulator ,
5253 ImportDeclaration ,
5354 RouteNode ,
55+ RoutePathSegmentMetadata ,
5456} from './types'
5557import type { Config } from './config'
5658import type { Logger } from './logger'
@@ -70,6 +72,32 @@ interface fs {
7072 chown : ( filePath : string , uid : number , gid : number ) => Promise < void >
7173}
7274
75+ function getRoutePathSegmentMetadataForPath (
76+ node : RouteNode ,
77+ routePath : string ,
78+ parentRoutePath ?: string ,
79+ ) : Array < RoutePathSegmentMetadata | undefined > | undefined {
80+ if ( ! node . _routePathSegmentMetadata ) return undefined
81+
82+ const segments = routePath . split ( '/' )
83+ const result = new Array < RoutePathSegmentMetadata | undefined > (
84+ segments . length ,
85+ )
86+ const parentSegmentCount = countRoutePathSegments ( parentRoutePath )
87+ let hasMetadata = false
88+ let segmentCount = 0
89+
90+ for ( let i = 0 ; i < segments . length ; i ++ ) {
91+ if ( ! segments [ i ] ) continue
92+ const sourceIndex = parentSegmentCount + segmentCount + 1
93+ result [ i ] = node . _routePathSegmentMetadata [ sourceIndex ]
94+ hasMetadata ||= ! ! result [ i ]
95+ segmentCount ++
96+ }
97+
98+ return hasMetadata ? result : undefined
99+ }
100+
73101const DefaultFileSystem : fs = {
74102 stat : async ( filePath ) => {
75103 const res = await fsp . stat ( filePath , { bigint : true } )
@@ -392,7 +420,10 @@ export class Generator {
392420
393421 const preRouteNodes = multiSortBy ( beforeRouteNodes , [
394422 ( d ) => ( d . routePath === '/' ? - 1 : 1 ) ,
395- ( d ) => d . routePath ?. split ( '/' ) . length ,
423+ ( d ) =>
424+ d . routePath === undefined
425+ ? undefined
426+ : countSlashSeparatedParts ( d . routePath ) ,
396427 ( d ) => ( d . filePath . match ( this . indexTokenFilenameRegex ) ? 1 : - 1 ) ,
397428 ( d ) => ( d . filePath . match ( Generator . componentPieceRegex ) ? 1 : - 1 ) ,
398429 ( d ) => ( d . filePath . match ( this . routeTokenFilenameRegex ) ? - 1 : 1 ) ,
@@ -587,7 +618,10 @@ export class Generator {
587618
588619 const sortedRouteNodes = multiSortBy ( acc . routeNodes , [
589620 ( d ) => ( d . routePath ?. includes ( `/${ rootPathId } ` ) ? - 1 : 1 ) ,
590- ( d ) => d . routePath ?. split ( '/' ) . length ,
621+ ( d ) =>
622+ d . routePath === undefined
623+ ? undefined
624+ : countSlashSeparatedParts ( d . routePath ) ,
591625 ( d ) => {
592626 const segments = d . routePath ?. split ( '/' ) . filter ( Boolean ) ?? [ ]
593627 const last = segments [ segments . length - 1 ] ?? ''
@@ -1430,29 +1464,39 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
14301464 node . path = determineNodePath ( node )
14311465
14321466 const trimmedPath = trimPathLeft ( node . path ?? '' )
1433- const trimmedOriginalPath = trimPathLeft (
1434- node . originalRoutePath ?. replace (
1435- node . parent ?. originalRoutePath ?? '' ,
1436- '' ,
1437- ) ?? '' ,
1467+ const originalPath =
1468+ node . originalRoutePath && node . parent ?. originalRoutePath
1469+ ? node . originalRoutePath . replace ( node . parent . originalRoutePath , '' ) ||
1470+ '/'
1471+ : node . originalRoutePath
1472+ const routePathSegmentMetadata = getRoutePathSegmentMetadataForPath (
1473+ node ,
1474+ node . path ?? '/' ,
1475+ node . parent ?. routePath ,
14381476 )
1477+ const trimmedOriginalPath = trimPathLeft ( originalPath ?? '' )
14391478
14401479 const split = trimmedPath . split ( '/' )
1480+ const pathSplit = ( node . path ?? '' ) . split ( '/' )
14411481 const originalSplit = trimmedOriginalPath . split ( '/' )
14421482 const lastRouteSegment = split [ split . length - 1 ] ?? trimmedPath
14431483 const lastOriginalSegment =
14441484 originalSplit [ originalSplit . length - 1 ] ?? trimmedOriginalPath
1485+ const lastRouteSegmentMetadata =
1486+ routePathSegmentMetadata ?. [ pathSplit . length - 1 ]
14451487
14461488 // A segment is non-path if it starts with underscore AND the underscore is not escaped
14471489 node . isNonPath =
1448- isSegmentPathless ( lastRouteSegment , lastOriginalSegment ) ||
1490+ ( ! lastRouteSegmentMetadata ?. literalLeadingUnderscore &&
1491+ isSegmentPathless ( lastRouteSegment , lastOriginalSegment ) ) ||
14491492 split . every ( ( part ) => this . routeGroupPatternRegex . test ( part ) )
14501493
1451- // Use escape-aware functions to compute cleanedPath
1494+ // Use a single pass so layout removal does not desync escaped underscore checks.
14521495 node . cleanedPath = removeGroups (
1453- removeUnderscoresWithEscape (
1454- removeLayoutSegmentsWithEscape ( node . path , node . originalRoutePath ) ,
1455- node . originalRoutePath ,
1496+ removeLayoutSegmentsAndUnderscoresWithEscape (
1497+ node . path ,
1498+ originalPath ,
1499+ routePathSegmentMetadata ,
14561500 ) ,
14571501 )
14581502
@@ -1530,13 +1574,17 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved
15301574 candidate . originalRoutePath ?? '' ,
15311575 '' ,
15321576 ) || '/'
1577+ const routePathSegmentMetadataRelativeToParent =
1578+ getRoutePathSegmentMetadataForPath (
1579+ node ,
1580+ pathRelativeToParent ,
1581+ candidate . routePath ,
1582+ )
15331583 node . cleanedPath = removeGroups (
1534- removeUnderscoresWithEscape (
1535- removeLayoutSegmentsWithEscape (
1536- pathRelativeToParent ,
1537- originalPathRelativeToParent ,
1538- ) ,
1584+ removeLayoutSegmentsAndUnderscoresWithEscape (
1585+ pathRelativeToParent ,
15391586 originalPathRelativeToParent ,
1587+ routePathSegmentMetadataRelativeToParent ,
15401588 ) ,
15411589 )
15421590 break
0 commit comments