@tanstack/router-generator 1.141.7 → 1.141.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/config.cjs +1 -3
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +10 -15
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +2 -4
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -1
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +1 -1
- package/dist/cjs/generator.cjs +5 -18
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/types.d.cts +0 -1
- package/dist/cjs/utils.cjs +15 -132
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +7 -25
- package/dist/esm/config.d.ts +10 -15
- package/dist/esm/config.js +1 -3
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.js +2 -4
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -1
- package/dist/esm/generator.js +6 -19
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/types.d.ts +0 -1
- package/dist/esm/utils.d.ts +7 -25
- package/dist/esm/utils.js +15 -132
- package/dist/esm/utils.js.map +1 -1
- package/package.json +5 -5
- package/src/config.ts +0 -2
- package/src/filesystem/physical/getRouteNodes.ts +1 -4
- package/src/generator.ts +4 -23
- package/src/types.ts +0 -1
- package/src/utils.ts +12 -188
package/src/utils.ts
CHANGED
|
@@ -14,7 +14,6 @@ import type { ImportDeclaration, RouteNode } from './types'
|
|
|
14
14
|
export class RoutePrefixMap {
|
|
15
15
|
private prefixToRoute: Map<string, RouteNode> = new Map()
|
|
16
16
|
private layoutRoutes: Array<RouteNode> = []
|
|
17
|
-
private nonNestedRoutes: Array<RouteNode> = []
|
|
18
17
|
|
|
19
18
|
constructor(routes: Array<RouteNode>) {
|
|
20
19
|
for (const route of routes) {
|
|
@@ -23,7 +22,6 @@ export class RoutePrefixMap {
|
|
|
23
22
|
// Index by exact path for direct lookups
|
|
24
23
|
this.prefixToRoute.set(route.routePath, route)
|
|
25
24
|
|
|
26
|
-
// Track layout routes separately for non-nested route handling
|
|
27
25
|
if (
|
|
28
26
|
route._fsRouteType === 'pathless_layout' ||
|
|
29
27
|
route._fsRouteType === 'layout' ||
|
|
@@ -31,20 +29,12 @@ export class RoutePrefixMap {
|
|
|
31
29
|
) {
|
|
32
30
|
this.layoutRoutes.push(route)
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
// Track non-nested routes separately
|
|
36
|
-
if (route._isExperimentalNonNestedRoute) {
|
|
37
|
-
this.nonNestedRoutes.push(route)
|
|
38
|
-
}
|
|
39
32
|
}
|
|
40
33
|
|
|
41
34
|
// Sort by path length descending for longest-match-first
|
|
42
35
|
this.layoutRoutes.sort(
|
|
43
36
|
(a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),
|
|
44
37
|
)
|
|
45
|
-
this.nonNestedRoutes.sort(
|
|
46
|
-
(a, b) => (b.routePath?.length ?? 0) - (a.routePath?.length ?? 0),
|
|
47
|
-
)
|
|
48
38
|
}
|
|
49
39
|
|
|
50
40
|
/**
|
|
@@ -69,48 +59,6 @@ export class RoutePrefixMap {
|
|
|
69
59
|
return null
|
|
70
60
|
}
|
|
71
61
|
|
|
72
|
-
/**
|
|
73
|
-
* Find parent for non-nested routes (needs layout route matching).
|
|
74
|
-
*/
|
|
75
|
-
findParentForNonNested(
|
|
76
|
-
routePath: string,
|
|
77
|
-
originalRoutePath: string | undefined,
|
|
78
|
-
nonNestedSegments: Array<string>,
|
|
79
|
-
): RouteNode | null {
|
|
80
|
-
// First check for other non-nested routes that are prefixes
|
|
81
|
-
// Use pre-sorted array for longest-match-first
|
|
82
|
-
for (const route of this.nonNestedRoutes) {
|
|
83
|
-
if (
|
|
84
|
-
route.routePath !== routePath &&
|
|
85
|
-
originalRoutePath?.startsWith(`${route.originalRoutePath}/`)
|
|
86
|
-
) {
|
|
87
|
-
return route
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Then check layout routes
|
|
92
|
-
for (const route of this.layoutRoutes) {
|
|
93
|
-
if (route.routePath === '/') continue
|
|
94
|
-
|
|
95
|
-
// Skip if this route's original path + underscore matches a non-nested segment
|
|
96
|
-
if (
|
|
97
|
-
nonNestedSegments.some((seg) => seg === `${route.originalRoutePath}_`)
|
|
98
|
-
) {
|
|
99
|
-
continue
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Check if this layout route is a prefix of the path we're looking for
|
|
103
|
-
if (
|
|
104
|
-
routePath.startsWith(`${route.routePath}/`) &&
|
|
105
|
-
route.routePath !== routePath
|
|
106
|
-
) {
|
|
107
|
-
return route
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
return null
|
|
112
|
-
}
|
|
113
|
-
|
|
114
62
|
/**
|
|
115
63
|
* Check if a route exists at the given path.
|
|
116
64
|
*/
|
|
@@ -192,10 +140,7 @@ export function removeTrailingSlash(s: string) {
|
|
|
192
140
|
const BRACKET_CONTENT_RE = /\[(.*?)\]/g
|
|
193
141
|
const SPLIT_REGEX = /(?<!\[)\.(?!\])/g
|
|
194
142
|
|
|
195
|
-
export function determineInitialRoutePath(
|
|
196
|
-
routePath: string,
|
|
197
|
-
config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>,
|
|
198
|
-
) {
|
|
143
|
+
export function determineInitialRoutePath(routePath: string) {
|
|
199
144
|
const DISALLOWED_ESCAPE_CHARS = new Set([
|
|
200
145
|
'/',
|
|
201
146
|
'\\',
|
|
@@ -209,6 +154,7 @@ export function determineInitialRoutePath(
|
|
|
209
154
|
'!',
|
|
210
155
|
'$',
|
|
211
156
|
'%',
|
|
157
|
+
'_',
|
|
212
158
|
])
|
|
213
159
|
|
|
214
160
|
const originalRoutePath =
|
|
@@ -216,31 +162,7 @@ export function determineInitialRoutePath(
|
|
|
216
162
|
`/${(cleanPath(routePath) || '').split(SPLIT_REGEX).join('/')}`,
|
|
217
163
|
) || ''
|
|
218
164
|
|
|
219
|
-
|
|
220
|
-
// TODO with new major rename to reflect not experimental anymore
|
|
221
|
-
const isExperimentalNonNestedRoute = isValidNonNestedRoute(
|
|
222
|
-
originalRoutePath,
|
|
223
|
-
config,
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
let cleanedRoutePath = routePath
|
|
227
|
-
|
|
228
|
-
// we already identified the path as non-nested and can now remove the trailing underscores
|
|
229
|
-
// we need to do this now before we encounter any escaped trailing underscores
|
|
230
|
-
// this way we can be sure any remaining trailing underscores should remain
|
|
231
|
-
// TODO with new major we can remove check and always remove leading underscores
|
|
232
|
-
if (config?.experimental?.nonNestedRoutes) {
|
|
233
|
-
// we should leave trailing underscores if the route path is the root path
|
|
234
|
-
if (originalRoutePath !== `/${rootPathId}`) {
|
|
235
|
-
// remove trailing underscores on various path segments
|
|
236
|
-
cleanedRoutePath = removeTrailingUnderscores(
|
|
237
|
-
originalRoutePath,
|
|
238
|
-
config.routeToken,
|
|
239
|
-
)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const parts = cleanedRoutePath.split(SPLIT_REGEX)
|
|
165
|
+
const parts = routePath.split(SPLIT_REGEX)
|
|
244
166
|
|
|
245
167
|
// Escape any characters that in square brackets
|
|
246
168
|
// we keep the original path untouched
|
|
@@ -275,7 +197,6 @@ export function determineInitialRoutePath(
|
|
|
275
197
|
|
|
276
198
|
return {
|
|
277
199
|
routePath: final,
|
|
278
|
-
isExperimentalNonNestedRoute,
|
|
279
200
|
originalRoutePath,
|
|
280
201
|
}
|
|
281
202
|
}
|
|
@@ -518,30 +439,6 @@ export function removeLastSegmentFromPath(routePath: string = '/'): string {
|
|
|
518
439
|
return segments.join('/')
|
|
519
440
|
}
|
|
520
441
|
|
|
521
|
-
const nonNestedSegmentRegex = /_(?=\/|$)/g
|
|
522
|
-
const openBracketRegex = /\[/g
|
|
523
|
-
const closeBracketRegex = /\]/g
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Extracts non-nested segments from a route path.
|
|
527
|
-
* Used for determining parent routes in non-nested route scenarios.
|
|
528
|
-
*/
|
|
529
|
-
export function getNonNestedSegments(routePath: string): Array<string> {
|
|
530
|
-
nonNestedSegmentRegex.lastIndex = 0
|
|
531
|
-
const result: Array<string> = []
|
|
532
|
-
for (const match of routePath.matchAll(nonNestedSegmentRegex)) {
|
|
533
|
-
const beforeStr = routePath.substring(0, match.index)
|
|
534
|
-
openBracketRegex.lastIndex = 0
|
|
535
|
-
closeBracketRegex.lastIndex = 0
|
|
536
|
-
const openBrackets = beforeStr.match(openBracketRegex)?.length ?? 0
|
|
537
|
-
const closeBrackets = beforeStr.match(closeBracketRegex)?.length ?? 0
|
|
538
|
-
if (openBrackets === closeBrackets) {
|
|
539
|
-
result.push(routePath.substring(0, match.index + 1))
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
return result.reverse()
|
|
543
|
-
}
|
|
544
|
-
|
|
545
442
|
/**
|
|
546
443
|
* Find parent route using RoutePrefixMap for O(k) lookups instead of O(n).
|
|
547
444
|
*/
|
|
@@ -549,21 +446,11 @@ export function hasParentRoute(
|
|
|
549
446
|
prefixMap: RoutePrefixMap,
|
|
550
447
|
node: RouteNode,
|
|
551
448
|
routePathToCheck: string | undefined,
|
|
552
|
-
originalRoutePathToCheck: string | undefined,
|
|
553
449
|
): RouteNode | null {
|
|
554
450
|
if (!routePathToCheck || routePathToCheck === '/') {
|
|
555
451
|
return null
|
|
556
452
|
}
|
|
557
453
|
|
|
558
|
-
if (node._isExperimentalNonNestedRoute && originalRoutePathToCheck) {
|
|
559
|
-
const nonNestedSegments = getNonNestedSegments(originalRoutePathToCheck)
|
|
560
|
-
return prefixMap.findParentForNonNested(
|
|
561
|
-
routePathToCheck,
|
|
562
|
-
originalRoutePathToCheck,
|
|
563
|
-
nonNestedSegments,
|
|
564
|
-
)
|
|
565
|
-
}
|
|
566
|
-
|
|
567
454
|
return prefixMap.findParent(routePathToCheck)
|
|
568
455
|
}
|
|
569
456
|
|
|
@@ -606,16 +493,9 @@ export const inferPath = (routeNode: RouteNode): string => {
|
|
|
606
493
|
/**
|
|
607
494
|
* Infers the full path for use by TS
|
|
608
495
|
*/
|
|
609
|
-
export const inferFullPath = (
|
|
610
|
-
routeNode: RouteNode,
|
|
611
|
-
config?: Pick<Config, 'experimental' | 'routeToken'>,
|
|
612
|
-
): string => {
|
|
613
|
-
// with new nonNestedPaths feature we can be sure any remaining trailing underscores are escaped and should remain
|
|
614
|
-
// TODO with new major we can remove check and only remove leading underscores
|
|
496
|
+
export const inferFullPath = (routeNode: RouteNode): string => {
|
|
615
497
|
const fullPath = removeGroups(
|
|
616
|
-
(
|
|
617
|
-
? removeLayoutSegments(routeNode.routePath)
|
|
618
|
-
: removeUnderscores(removeLayoutSegments(routeNode.routePath))) ?? '',
|
|
498
|
+
removeUnderscores(removeLayoutSegments(routeNode.routePath)) ?? '',
|
|
619
499
|
)
|
|
620
500
|
|
|
621
501
|
return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\/$/, '')
|
|
@@ -626,13 +506,9 @@ export const inferFullPath = (
|
|
|
626
506
|
*/
|
|
627
507
|
export const createRouteNodesByFullPath = (
|
|
628
508
|
routeNodes: Array<RouteNode>,
|
|
629
|
-
config?: Pick<Config, 'experimental' | 'routeToken'>,
|
|
630
509
|
): Map<string, RouteNode> => {
|
|
631
510
|
return new Map(
|
|
632
|
-
routeNodes.map((routeNode) => [
|
|
633
|
-
inferFullPath(routeNode, config),
|
|
634
|
-
routeNode,
|
|
635
|
-
]),
|
|
511
|
+
routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode]),
|
|
636
512
|
)
|
|
637
513
|
}
|
|
638
514
|
|
|
@@ -641,11 +517,10 @@ export const createRouteNodesByFullPath = (
|
|
|
641
517
|
*/
|
|
642
518
|
export const createRouteNodesByTo = (
|
|
643
519
|
routeNodes: Array<RouteNode>,
|
|
644
|
-
config?: Pick<Config, 'experimental' | 'routeToken'>,
|
|
645
520
|
): Map<string, RouteNode> => {
|
|
646
521
|
return new Map(
|
|
647
522
|
dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [
|
|
648
|
-
inferTo(routeNode
|
|
523
|
+
inferTo(routeNode),
|
|
649
524
|
routeNode,
|
|
650
525
|
]),
|
|
651
526
|
)
|
|
@@ -668,11 +543,8 @@ export const createRouteNodesById = (
|
|
|
668
543
|
/**
|
|
669
544
|
* Infers to path
|
|
670
545
|
*/
|
|
671
|
-
export const inferTo = (
|
|
672
|
-
routeNode
|
|
673
|
-
config?: Pick<Config, 'experimental' | 'routeToken'>,
|
|
674
|
-
): string => {
|
|
675
|
-
const fullPath = inferFullPath(routeNode, config)
|
|
546
|
+
export const inferTo = (routeNode: RouteNode): string => {
|
|
547
|
+
const fullPath = inferFullPath(routeNode)
|
|
676
548
|
|
|
677
549
|
if (fullPath === '/') return fullPath
|
|
678
550
|
|
|
@@ -711,7 +583,7 @@ export function checkRouteFullPathUniqueness(
|
|
|
711
583
|
config: Config,
|
|
712
584
|
) {
|
|
713
585
|
const routes = _routes.map((d) => {
|
|
714
|
-
const inferredFullPath = inferFullPath(d
|
|
586
|
+
const inferredFullPath = inferFullPath(d)
|
|
715
587
|
return { ...d, inferredFullPath }
|
|
716
588
|
})
|
|
717
589
|
|
|
@@ -849,7 +721,7 @@ export function buildFileRoutesByPathInterface(opts: {
|
|
|
849
721
|
routeNodes: Array<RouteNode>
|
|
850
722
|
module: string
|
|
851
723
|
interfaceName: string
|
|
852
|
-
config?: Pick<Config, '
|
|
724
|
+
config?: Pick<Config, 'routeToken'>
|
|
853
725
|
}): string {
|
|
854
726
|
return `declare module '${opts.module}' {
|
|
855
727
|
interface ${opts.interfaceName} {
|
|
@@ -863,7 +735,7 @@ export function buildFileRoutesByPathInterface(opts: {
|
|
|
863
735
|
return `'${filePathId}': {
|
|
864
736
|
id: '${filePathId}'
|
|
865
737
|
path: '${inferPath(routeNode)}'
|
|
866
|
-
fullPath: '${inferFullPath(routeNode
|
|
738
|
+
fullPath: '${inferFullPath(routeNode)}'
|
|
867
739
|
preLoaderRoute: ${preloaderRoute}
|
|
868
740
|
parentRoute: typeof ${parent}
|
|
869
741
|
}`
|
|
@@ -916,51 +788,3 @@ export function getImportForRouteNode(
|
|
|
916
788
|
],
|
|
917
789
|
} satisfies ImportDeclaration
|
|
918
790
|
}
|
|
919
|
-
|
|
920
|
-
/**
|
|
921
|
-
* Used to validate if a route is a pathless layout route
|
|
922
|
-
* @param normalizedRoutePath Normalized route path, i.e `/foo/_layout/route.tsx` and `/foo._layout.route.tsx` to `/foo/_layout/route`
|
|
923
|
-
* @param config The `router-generator` configuration object
|
|
924
|
-
* @returns Boolean indicating if the route is a pathless layout route
|
|
925
|
-
*/
|
|
926
|
-
export function isValidNonNestedRoute(
|
|
927
|
-
normalizedRoutePath: string,
|
|
928
|
-
config?: Pick<Config, 'experimental' | 'routeToken' | 'indexToken'>,
|
|
929
|
-
): boolean {
|
|
930
|
-
if (!config?.experimental?.nonNestedRoutes) {
|
|
931
|
-
return false
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
const segments = normalizedRoutePath.split('/').filter(Boolean)
|
|
935
|
-
|
|
936
|
-
if (segments.length === 0) {
|
|
937
|
-
return false
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
const lastRouteSegment = segments[segments.length - 1]!
|
|
941
|
-
|
|
942
|
-
// If segment === __root, then exit as false
|
|
943
|
-
if (lastRouteSegment === rootPathId) {
|
|
944
|
-
return false
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
if (
|
|
948
|
-
lastRouteSegment !== config.indexToken &&
|
|
949
|
-
lastRouteSegment !== config.routeToken &&
|
|
950
|
-
lastRouteSegment.endsWith('_')
|
|
951
|
-
) {
|
|
952
|
-
return true
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
for (const segment of segments.slice(0, -1).reverse()) {
|
|
956
|
-
if (segment === config.routeToken) {
|
|
957
|
-
return false
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
if (segment.endsWith('_')) {
|
|
961
|
-
return true
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
return false
|
|
966
|
-
}
|