@tanstack/router-generator 1.52.0 → 1.55.0
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 +21 -19
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/config.d.cts +3 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs +125 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.cjs.map +1 -0
- package/dist/cjs/filesystem/physical/getRouteNodes.d.cts +3 -0
- package/dist/cjs/filesystem/physical/rootPathId.cjs +5 -0
- package/dist/cjs/filesystem/physical/rootPathId.cjs.map +1 -0
- package/dist/cjs/filesystem/physical/rootPathId.d.cts +1 -0
- package/dist/cjs/filesystem/virtual/config.cjs +37 -0
- package/dist/cjs/filesystem/virtual/config.cjs.map +1 -0
- package/dist/cjs/filesystem/virtual/config.d.cts +3 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs +119 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.cjs.map +1 -0
- package/dist/cjs/filesystem/virtual/getRouteNodes.d.cts +5 -0
- package/dist/cjs/generator.cjs +46 -191
- package/dist/cjs/generator.cjs.map +1 -1
- package/dist/cjs/generator.d.cts +1 -27
- package/dist/cjs/types.d.cts +27 -0
- package/dist/cjs/utils.cjs +54 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +9 -0
- package/dist/esm/config.d.ts +3 -0
- package/dist/esm/config.js +2 -0
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/filesystem/physical/getRouteNodes.d.ts +3 -0
- package/dist/esm/filesystem/physical/getRouteNodes.js +108 -0
- package/dist/esm/filesystem/physical/getRouteNodes.js.map +1 -0
- package/dist/esm/filesystem/physical/rootPathId.d.ts +1 -0
- package/dist/esm/filesystem/physical/rootPathId.js +5 -0
- package/dist/esm/filesystem/physical/rootPathId.js.map +1 -0
- package/dist/esm/filesystem/virtual/config.d.ts +3 -0
- package/dist/esm/filesystem/virtual/config.js +37 -0
- package/dist/esm/filesystem/virtual/config.js.map +1 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.d.ts +5 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.js +119 -0
- package/dist/esm/filesystem/virtual/getRouteNodes.js.map +1 -0
- package/dist/esm/generator.d.ts +1 -27
- package/dist/esm/generator.js +29 -174
- package/dist/esm/generator.js.map +1 -1
- package/dist/esm/types.d.ts +27 -0
- package/dist/esm/utils.d.ts +9 -0
- package/dist/esm/utils.js +54 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -2
- package/src/config.ts +2 -0
- package/src/filesystem/physical/getRouteNodes.ts +151 -0
- package/src/filesystem/physical/rootPathId.ts +1 -0
- package/src/filesystem/virtual/config.ts +45 -0
- package/src/filesystem/virtual/getRouteNodes.ts +141 -0
- package/src/generator.ts +46 -269
- package/src/types.ts +28 -0
- package/src/utils.ts +73 -0
package/src/generator.ts
CHANGED
|
@@ -2,175 +2,26 @@ import path from 'node:path'
|
|
|
2
2
|
import * as fs from 'node:fs'
|
|
3
3
|
import * as fsp from 'node:fs/promises'
|
|
4
4
|
import * as prettier from 'prettier'
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
determineInitialRoutePath,
|
|
7
|
+
logging,
|
|
8
|
+
multiSortBy,
|
|
9
|
+
removeExt,
|
|
10
|
+
removeTrailingSlash,
|
|
11
|
+
removeUnderscores,
|
|
12
|
+
replaceBackslash,
|
|
13
|
+
routePathToVariable,
|
|
14
|
+
trimPathLeft,
|
|
15
|
+
} from './utils'
|
|
16
|
+
import { getRouteNodes as physicalGetRouteNodes } from './filesystem/physical/getRouteNodes'
|
|
17
|
+
import { getRouteNodes as virtualGetRouteNodes } from './filesystem/virtual/getRouteNodes'
|
|
18
|
+
import { rootPathId } from './filesystem/physical/rootPathId'
|
|
19
|
+
import type { GetRouteNodesResult, RouteNode } from './types'
|
|
6
20
|
import type { Config } from './config'
|
|
7
21
|
|
|
8
22
|
let latestTask = 0
|
|
9
|
-
export const rootPathId = '__root'
|
|
10
23
|
const routeGroupPatternRegex = /\(.+\)/g
|
|
11
24
|
const possiblyNestedRouteGroupPatternRegex = /\([^/]+\)\/?/g
|
|
12
|
-
const disallowedRouteGroupConfiguration = /\(([^)]+)\).(ts|js|tsx|jsx)/
|
|
13
|
-
|
|
14
|
-
export type RouteNode = {
|
|
15
|
-
filePath: string
|
|
16
|
-
fullPath: string
|
|
17
|
-
variableName: string
|
|
18
|
-
routePath?: string
|
|
19
|
-
cleanedPath?: string
|
|
20
|
-
path?: string
|
|
21
|
-
isNonPath?: boolean
|
|
22
|
-
isNonLayout?: boolean
|
|
23
|
-
isLayout?: boolean
|
|
24
|
-
isVirtualParentRequired?: boolean
|
|
25
|
-
isVirtualParentRoute?: boolean
|
|
26
|
-
isRoute?: boolean
|
|
27
|
-
isAPIRoute?: boolean
|
|
28
|
-
isLoader?: boolean
|
|
29
|
-
isComponent?: boolean
|
|
30
|
-
isErrorComponent?: boolean
|
|
31
|
-
isPendingComponent?: boolean
|
|
32
|
-
isVirtual?: boolean
|
|
33
|
-
isLazy?: boolean
|
|
34
|
-
isRoot?: boolean
|
|
35
|
-
children?: Array<RouteNode>
|
|
36
|
-
parent?: RouteNode
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function getRouteNodes(config: Config) {
|
|
40
|
-
const { routeFilePrefix, routeFileIgnorePrefix, routeFileIgnorePattern } =
|
|
41
|
-
config
|
|
42
|
-
const logger = logging({ disabled: config.disableLogging })
|
|
43
|
-
const routeFileIgnoreRegExp = new RegExp(routeFileIgnorePattern ?? '', 'g')
|
|
44
|
-
|
|
45
|
-
const routeNodes: Array<RouteNode> = []
|
|
46
|
-
|
|
47
|
-
async function recurse(dir: string) {
|
|
48
|
-
const fullDir = path.resolve(config.routesDirectory, dir)
|
|
49
|
-
let dirList = await fsp.readdir(fullDir, { withFileTypes: true })
|
|
50
|
-
|
|
51
|
-
dirList = dirList.filter((d) => {
|
|
52
|
-
if (
|
|
53
|
-
d.name.startsWith('.') ||
|
|
54
|
-
(routeFileIgnorePrefix && d.name.startsWith(routeFileIgnorePrefix))
|
|
55
|
-
) {
|
|
56
|
-
return false
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (routeFilePrefix) {
|
|
60
|
-
return d.name.startsWith(routeFilePrefix)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (routeFileIgnorePattern) {
|
|
64
|
-
return !d.name.match(routeFileIgnoreRegExp)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return true
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
await Promise.all(
|
|
71
|
-
dirList.map(async (dirent) => {
|
|
72
|
-
const fullPath = path.join(fullDir, dirent.name)
|
|
73
|
-
const relativePath = path.join(dir, dirent.name)
|
|
74
|
-
|
|
75
|
-
if (dirent.isDirectory()) {
|
|
76
|
-
await recurse(relativePath)
|
|
77
|
-
} else if (fullPath.match(/\.(tsx|ts|jsx|js)$/)) {
|
|
78
|
-
const filePath = replaceBackslash(path.join(dir, dirent.name))
|
|
79
|
-
const filePathNoExt = removeExt(filePath)
|
|
80
|
-
let routePath = determineInitialRoutePath(filePathNoExt)
|
|
81
|
-
|
|
82
|
-
if (routeFilePrefix) {
|
|
83
|
-
routePath = routePath.replaceAll(routeFilePrefix, '')
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (disallowedRouteGroupConfiguration.test(dirent.name)) {
|
|
87
|
-
const errorMessage = `A route configuration for a route group was found at \`${filePath}\`. This is not supported. Did you mean to use a layout/pathless route instead?`
|
|
88
|
-
logger.error(`ERROR: ${errorMessage}`)
|
|
89
|
-
throw new Error(errorMessage)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const variableName = routePathToVariable(routePath)
|
|
93
|
-
|
|
94
|
-
// Remove the index from the route path and
|
|
95
|
-
// if the route path is empty, use `/'
|
|
96
|
-
|
|
97
|
-
const isLazy = routePath.endsWith('/lazy')
|
|
98
|
-
|
|
99
|
-
if (isLazy) {
|
|
100
|
-
routePath = routePath.replace(/\/lazy$/, '')
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const isRoute = routePath.endsWith(`/${config.routeToken}`)
|
|
104
|
-
const isComponent = routePath.endsWith('/component')
|
|
105
|
-
const isErrorComponent = routePath.endsWith('/errorComponent')
|
|
106
|
-
const isPendingComponent = routePath.endsWith('/pendingComponent')
|
|
107
|
-
const isLoader = routePath.endsWith('/loader')
|
|
108
|
-
const isAPIRoute = routePath.startsWith(
|
|
109
|
-
`${removeTrailingSlash(config.apiBase)}/`,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
const segments = routePath.split('/')
|
|
113
|
-
const lastRouteSegment = segments[segments.length - 1]
|
|
114
|
-
const isLayout =
|
|
115
|
-
(lastRouteSegment !== config.indexToken &&
|
|
116
|
-
lastRouteSegment !== config.routeToken &&
|
|
117
|
-
lastRouteSegment?.startsWith('_')) ||
|
|
118
|
-
false
|
|
119
|
-
|
|
120
|
-
;(
|
|
121
|
-
[
|
|
122
|
-
[isComponent, 'component'],
|
|
123
|
-
[isErrorComponent, 'errorComponent'],
|
|
124
|
-
[isPendingComponent, 'pendingComponent'],
|
|
125
|
-
[isLoader, 'loader'],
|
|
126
|
-
] as const
|
|
127
|
-
).forEach(([isType, type]) => {
|
|
128
|
-
if (isType) {
|
|
129
|
-
logger.warn(
|
|
130
|
-
`WARNING: The \`.${type}.tsx\` suffix used for the ${filePath} file is deprecated. Use the new \`.lazy.tsx\` suffix instead.`,
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
routePath = routePath.replace(
|
|
136
|
-
new RegExp(
|
|
137
|
-
`/(component|errorComponent|pendingComponent|loader|${config.routeToken}|lazy)$`,
|
|
138
|
-
),
|
|
139
|
-
'',
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
if (routePath === config.indexToken) {
|
|
143
|
-
routePath = '/'
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
routePath =
|
|
147
|
-
routePath.replace(new RegExp(`/${config.indexToken}$`), '/') || '/'
|
|
148
|
-
|
|
149
|
-
routeNodes.push({
|
|
150
|
-
filePath,
|
|
151
|
-
fullPath,
|
|
152
|
-
routePath,
|
|
153
|
-
variableName,
|
|
154
|
-
isRoute,
|
|
155
|
-
isComponent,
|
|
156
|
-
isErrorComponent,
|
|
157
|
-
isPendingComponent,
|
|
158
|
-
isLoader,
|
|
159
|
-
isLazy,
|
|
160
|
-
isLayout,
|
|
161
|
-
isAPIRoute,
|
|
162
|
-
})
|
|
163
|
-
}
|
|
164
|
-
}),
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
return routeNodes
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
await recurse('./')
|
|
171
|
-
|
|
172
|
-
return routeNodes
|
|
173
|
-
}
|
|
174
25
|
|
|
175
26
|
let isFirst = false
|
|
176
27
|
let skipMessage = false
|
|
@@ -216,12 +67,18 @@ export async function generator(config: Config) {
|
|
|
216
67
|
parser: 'typescript',
|
|
217
68
|
}
|
|
218
69
|
|
|
219
|
-
|
|
220
|
-
const beforeRouteNodes = await getRouteNodes(config)
|
|
221
|
-
const rootRouteNode = beforeRouteNodes.find(
|
|
222
|
-
(d) => d.routePath === `/${rootPathId}`,
|
|
223
|
-
)
|
|
70
|
+
let getRouteNodesResult: GetRouteNodesResult
|
|
224
71
|
|
|
72
|
+
if (config.virtualRouteConfig) {
|
|
73
|
+
getRouteNodesResult = await virtualGetRouteNodes(config)
|
|
74
|
+
} else {
|
|
75
|
+
getRouteNodesResult = await physicalGetRouteNodes(config)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const { rootRouteNode, routeNodes: beforeRouteNodes } = getRouteNodesResult
|
|
79
|
+
if (rootRouteNode === undefined) {
|
|
80
|
+
throw new Error(`rootRouteNode must not be undefined`)
|
|
81
|
+
}
|
|
225
82
|
const preRouteNodes = multiSortBy(beforeRouteNodes, [
|
|
226
83
|
(d) => (d.routePath === '/' ? -1 : 1),
|
|
227
84
|
(d) => d.routePath?.split('/').length,
|
|
@@ -307,13 +164,11 @@ export const Route = createRootRoute({
|
|
|
307
164
|
const trimmedPath = trimPathLeft(node.path ?? '')
|
|
308
165
|
|
|
309
166
|
const split = trimmedPath.split('/')
|
|
310
|
-
const first = split[0] ?? trimmedPath
|
|
311
167
|
const lastRouteSegment = split[split.length - 1] ?? trimmedPath
|
|
312
168
|
|
|
313
169
|
node.isNonPath =
|
|
314
170
|
lastRouteSegment.startsWith('_') ||
|
|
315
171
|
routeGroupPatternRegex.test(lastRouteSegment)
|
|
316
|
-
node.isNonLayout = first.endsWith('_')
|
|
317
172
|
|
|
318
173
|
node.cleanedPath = removeGroups(
|
|
319
174
|
removeUnderscores(removeLayoutSegments(node.path)) ?? '',
|
|
@@ -571,11 +426,18 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
571
426
|
.map((d) => d[0])
|
|
572
427
|
|
|
573
428
|
const virtualRouteNodes = sortedRouteNodes.filter((d) => d.isVirtual)
|
|
574
|
-
const rootPathIdExtension =
|
|
575
|
-
config.addExtensions && rootRouteNode
|
|
576
|
-
? path.extname(rootRouteNode.filePath)
|
|
577
|
-
: ''
|
|
578
429
|
|
|
430
|
+
function getImportPath(node: RouteNode) {
|
|
431
|
+
return replaceBackslash(
|
|
432
|
+
removeExt(
|
|
433
|
+
path.relative(
|
|
434
|
+
path.dirname(config.generatedRouteTree),
|
|
435
|
+
path.resolve(config.routesDirectory, node.filePath),
|
|
436
|
+
),
|
|
437
|
+
config.addExtensions,
|
|
438
|
+
),
|
|
439
|
+
)
|
|
440
|
+
}
|
|
579
441
|
const routeImports = [
|
|
580
442
|
...config.routeTreeFileHeader,
|
|
581
443
|
'// This file is auto-generated by TanStack Router',
|
|
@@ -584,29 +446,13 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
584
446
|
: '',
|
|
585
447
|
'// Import Routes',
|
|
586
448
|
[
|
|
587
|
-
`import { Route as rootRoute } from './${
|
|
588
|
-
path.relative(
|
|
589
|
-
path.dirname(config.generatedRouteTree),
|
|
590
|
-
path.resolve(
|
|
591
|
-
config.routesDirectory,
|
|
592
|
-
`${routePathIdPrefix}${rootPathId}${rootPathIdExtension}`,
|
|
593
|
-
),
|
|
594
|
-
),
|
|
595
|
-
)}'`,
|
|
449
|
+
`import { Route as rootRoute } from './${getImportPath(rootRouteNode)}'`,
|
|
596
450
|
...sortedRouteNodes
|
|
597
451
|
.filter((d) => !d.isVirtual)
|
|
598
452
|
.map((node) => {
|
|
599
453
|
return `import { Route as ${
|
|
600
454
|
node.variableName
|
|
601
|
-
}Import } from './${
|
|
602
|
-
removeExt(
|
|
603
|
-
path.relative(
|
|
604
|
-
path.dirname(config.generatedRouteTree),
|
|
605
|
-
path.resolve(config.routesDirectory, node.filePath),
|
|
606
|
-
),
|
|
607
|
-
config.addExtensions,
|
|
608
|
-
),
|
|
609
|
-
)}'`
|
|
455
|
+
}Import } from './${getImportPath(node)}'`
|
|
610
456
|
}),
|
|
611
457
|
].join('\n'),
|
|
612
458
|
virtualRouteNodes.length ? '// Create Virtual Routes' : '',
|
|
@@ -704,7 +550,7 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
704
550
|
${routeNodes
|
|
705
551
|
.map((routeNode) => {
|
|
706
552
|
const [filePathId, routeId] = getFilePathIdAndRouteIdFromPath(
|
|
707
|
-
routeNode.routePath
|
|
553
|
+
routeNode.routePath,
|
|
708
554
|
)
|
|
709
555
|
|
|
710
556
|
return `'${filePathId}': {
|
|
@@ -735,14 +581,14 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
735
581
|
const createRouteManifest = () => {
|
|
736
582
|
const routesManifest = {
|
|
737
583
|
__root__: {
|
|
738
|
-
filePath: rootRouteNode
|
|
584
|
+
filePath: rootRouteNode.filePath,
|
|
739
585
|
children: routeTree.map(
|
|
740
|
-
(d) => getFilePathIdAndRouteIdFromPath(d.routePath
|
|
586
|
+
(d) => getFilePathIdAndRouteIdFromPath(d.routePath)[1],
|
|
741
587
|
),
|
|
742
588
|
},
|
|
743
589
|
...Object.fromEntries(
|
|
744
590
|
routeNodes.map((d) => {
|
|
745
|
-
const [_, routeId] = getFilePathIdAndRouteIdFromPath(d.routePath
|
|
591
|
+
const [_, routeId] = getFilePathIdAndRouteIdFromPath(d.routePath)
|
|
746
592
|
|
|
747
593
|
return [
|
|
748
594
|
routeId,
|
|
@@ -753,7 +599,7 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
753
599
|
: undefined,
|
|
754
600
|
children: d.children?.map(
|
|
755
601
|
(childRoute) =>
|
|
756
|
-
getFilePathIdAndRouteIdFromPath(childRoute.routePath
|
|
602
|
+
getFilePathIdAndRouteIdFromPath(childRoute.routePath)[1],
|
|
757
603
|
),
|
|
758
604
|
},
|
|
759
605
|
]
|
|
@@ -820,89 +666,20 @@ export const Route = createAPIFileRoute('${escapedRoutePath}')({
|
|
|
820
666
|
)
|
|
821
667
|
}
|
|
822
668
|
|
|
823
|
-
function routePathToVariable(routePath: string): string {
|
|
824
|
-
return (
|
|
825
|
-
removeUnderscores(routePath)
|
|
826
|
-
?.replace(/\/\$\//g, '/splat/')
|
|
827
|
-
.replace(/\$$/g, 'splat')
|
|
828
|
-
.replace(/\$/g, '')
|
|
829
|
-
.split(/[/-]/g)
|
|
830
|
-
.map((d, i) => (i > 0 ? capitalize(d) : d))
|
|
831
|
-
.join('')
|
|
832
|
-
.replace(/([^a-zA-Z0-9]|[.])/gm, '')
|
|
833
|
-
.replace(/^(\d)/g, 'R$1') ?? ''
|
|
834
|
-
)
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
export function removeExt(d: string, keepExtension: boolean = false) {
|
|
838
|
-
return keepExtension ? d : d.substring(0, d.lastIndexOf('.')) || d
|
|
839
|
-
}
|
|
840
|
-
|
|
841
669
|
function spaces(d: number): string {
|
|
842
670
|
return Array.from({ length: d })
|
|
843
671
|
.map(() => ' ')
|
|
844
672
|
.join('')
|
|
845
673
|
}
|
|
846
674
|
|
|
847
|
-
export function multiSortBy<T>(
|
|
848
|
-
arr: Array<T>,
|
|
849
|
-
accessors: Array<(item: T) => any> = [(d) => d],
|
|
850
|
-
): Array<T> {
|
|
851
|
-
return arr
|
|
852
|
-
.map((d, i) => [d, i] as const)
|
|
853
|
-
.sort(([a, ai], [b, bi]) => {
|
|
854
|
-
for (const accessor of accessors) {
|
|
855
|
-
const ao = accessor(a)
|
|
856
|
-
const bo = accessor(b)
|
|
857
|
-
|
|
858
|
-
if (typeof ao === 'undefined') {
|
|
859
|
-
if (typeof bo === 'undefined') {
|
|
860
|
-
continue
|
|
861
|
-
}
|
|
862
|
-
return 1
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
if (ao === bo) {
|
|
866
|
-
continue
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
return ao > bo ? 1 : -1
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
return ai - bi
|
|
873
|
-
})
|
|
874
|
-
.map(([d]) => d)
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
function capitalize(s: string) {
|
|
878
|
-
if (typeof s !== 'string') return ''
|
|
879
|
-
return s.charAt(0).toUpperCase() + s.slice(1)
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
function removeUnderscores(s?: string) {
|
|
883
|
-
return s?.replaceAll(/(^_|_$)/gi, '').replaceAll(/(\/_|_\/)/gi, '/')
|
|
884
|
-
}
|
|
885
|
-
|
|
886
675
|
function removeTrailingUnderscores(s?: string) {
|
|
887
676
|
return s?.replaceAll(/(_$)/gi, '').replaceAll(/(_\/)/gi, '/')
|
|
888
677
|
}
|
|
889
678
|
|
|
890
|
-
function replaceBackslash(s: string) {
|
|
891
|
-
return s.replaceAll(/\\/gi, '/')
|
|
892
|
-
}
|
|
893
|
-
|
|
894
679
|
function removeGroups(s: string) {
|
|
895
680
|
return s.replace(possiblyNestedRouteGroupPatternRegex, '')
|
|
896
681
|
}
|
|
897
682
|
|
|
898
|
-
function removeTrailingSlash(s: string) {
|
|
899
|
-
return s.replace(/\/$/, '')
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
function determineInitialRoutePath(routePath: string) {
|
|
903
|
-
return cleanPath(`/${routePath.split('.').join('/')}`) || ''
|
|
904
|
-
}
|
|
905
|
-
|
|
906
683
|
/**
|
|
907
684
|
* The `node.path` is used as the `id` in the route definition.
|
|
908
685
|
* This function checks if the given node has a parent and if so, it determines the correct path for the given node.
|
|
@@ -911,7 +688,7 @@ function determineInitialRoutePath(routePath: string) {
|
|
|
911
688
|
*/
|
|
912
689
|
function determineNodePath(node: RouteNode) {
|
|
913
690
|
return (node.path = node.parent
|
|
914
|
-
? node.routePath?.replace(node.parent.routePath
|
|
691
|
+
? node.routePath?.replace(node.parent.routePath ?? '', '') || '/'
|
|
915
692
|
: node.routePath)
|
|
916
693
|
}
|
|
917
694
|
|
|
@@ -995,7 +772,7 @@ export const inferPath = (routeNode: RouteNode): string => {
|
|
|
995
772
|
: (routeNode.cleanedPath?.replace(/\/$/, '') ?? '')
|
|
996
773
|
}
|
|
997
774
|
|
|
998
|
-
function getFilePathIdAndRouteIdFromPath(pathname
|
|
775
|
+
function getFilePathIdAndRouteIdFromPath(pathname?: string) {
|
|
999
776
|
const filePathId = removeTrailingUnderscores(pathname)
|
|
1000
777
|
const id = removeGroups(filePathId ?? '')
|
|
1001
778
|
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type RouteNode = {
|
|
2
|
+
filePath: string
|
|
3
|
+
fullPath: string
|
|
4
|
+
variableName: string
|
|
5
|
+
routePath?: string
|
|
6
|
+
cleanedPath?: string
|
|
7
|
+
path?: string
|
|
8
|
+
isNonPath?: boolean
|
|
9
|
+
isLayout?: boolean
|
|
10
|
+
isVirtualParentRequired?: boolean
|
|
11
|
+
isVirtualParentRoute?: boolean
|
|
12
|
+
isRoute?: boolean
|
|
13
|
+
isAPIRoute?: boolean
|
|
14
|
+
isLoader?: boolean
|
|
15
|
+
isComponent?: boolean
|
|
16
|
+
isErrorComponent?: boolean
|
|
17
|
+
isPendingComponent?: boolean
|
|
18
|
+
isVirtual?: boolean
|
|
19
|
+
isLazy?: boolean
|
|
20
|
+
isRoot?: boolean
|
|
21
|
+
children?: Array<RouteNode>
|
|
22
|
+
parent?: RouteNode
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface GetRouteNodesResult {
|
|
26
|
+
rootRouteNode?: RouteNode
|
|
27
|
+
routeNodes: Array<RouteNode>
|
|
28
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,3 +1,33 @@
|
|
|
1
|
+
export function multiSortBy<T>(
|
|
2
|
+
arr: Array<T>,
|
|
3
|
+
accessors: Array<(item: T) => any> = [(d) => d],
|
|
4
|
+
): Array<T> {
|
|
5
|
+
return arr
|
|
6
|
+
.map((d, i) => [d, i] as const)
|
|
7
|
+
.sort(([a, ai], [b, bi]) => {
|
|
8
|
+
for (const accessor of accessors) {
|
|
9
|
+
const ao = accessor(a)
|
|
10
|
+
const bo = accessor(b)
|
|
11
|
+
|
|
12
|
+
if (typeof ao === 'undefined') {
|
|
13
|
+
if (typeof bo === 'undefined') {
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
return 1
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (ao === bo) {
|
|
20
|
+
continue
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return ao > bo ? 1 : -1
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return ai - bi
|
|
27
|
+
})
|
|
28
|
+
.map(([d]) => d)
|
|
29
|
+
}
|
|
30
|
+
|
|
1
31
|
export function cleanPath(path: string) {
|
|
2
32
|
// remove double slashes
|
|
3
33
|
return path.replace(/\/{2,}/g, '/')
|
|
@@ -26,3 +56,46 @@ export function logging(config: { disabled: boolean }) {
|
|
|
26
56
|
},
|
|
27
57
|
}
|
|
28
58
|
}
|
|
59
|
+
|
|
60
|
+
export function removeLeadingSlash(path: string): string {
|
|
61
|
+
return path.replace(/^\//, '')
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function removeTrailingSlash(s: string) {
|
|
65
|
+
return s.replace(/\/$/, '')
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function determineInitialRoutePath(routePath: string) {
|
|
69
|
+
return cleanPath(`/${routePath.split('.').join('/')}`) || ''
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function replaceBackslash(s: string) {
|
|
73
|
+
return s.replaceAll(/\\/gi, '/')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function routePathToVariable(routePath: string): string {
|
|
77
|
+
return (
|
|
78
|
+
removeUnderscores(routePath)
|
|
79
|
+
?.replace(/\/\$\//g, '/splat/')
|
|
80
|
+
.replace(/\$$/g, 'splat')
|
|
81
|
+
.replace(/\$/g, '')
|
|
82
|
+
.split(/[/-]/g)
|
|
83
|
+
.map((d, i) => (i > 0 ? capitalize(d) : d))
|
|
84
|
+
.join('')
|
|
85
|
+
.replace(/([^a-zA-Z0-9]|[.])/gm, '')
|
|
86
|
+
.replace(/^(\d)/g, 'R$1') ?? ''
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function removeUnderscores(s?: string) {
|
|
91
|
+
return s?.replaceAll(/(^_|_$)/gi, '').replaceAll(/(\/_|_\/)/gi, '/')
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function capitalize(s: string) {
|
|
95
|
+
if (typeof s !== 'string') return ''
|
|
96
|
+
return s.charAt(0).toUpperCase() + s.slice(1)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function removeExt(d: string, keepExtension: boolean = false) {
|
|
100
|
+
return keepExtension ? d : d.substring(0, d.lastIndexOf('.')) || d
|
|
101
|
+
}
|