Skip to content

Commit 70b2225

Browse files
fix: Fix file-based route generation when custom routeToken or `ind… (#7048)
fix: Fix file-based route generation when custom `routeToken` or `indexToken` values start with regex metacharacters like `+`. fixes #7036
1 parent a1ab264 commit 70b2225

10 files changed

Lines changed: 171 additions & 5 deletions

File tree

.changeset/tiny-cars-taste.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/router-generator': patch
3+
---
4+
5+
Fix file-based route generation when custom `routeToken` or `indexToken` values start with regex metacharacters like `+`.

packages/router-generator/src/filesystem/physical/getRouteNodes.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import path from 'node:path'
22
import * as fsp from 'node:fs/promises'
33
import {
44
determineInitialRoutePath,
5+
escapeRegExp,
56
hasEscapedLeadingUnderscore,
67
removeExt,
78
replaceBackslash,
@@ -265,9 +266,12 @@ export async function getRouteNodes(
265266

266267
if (suffixToStrip || shouldStripRouteToken) {
267268
const stripSegment = suffixToStrip ?? lastRouteSegment
268-
routePath = routePath.replace(new RegExp(`/${stripSegment}$`), '')
269+
routePath = routePath.replace(
270+
new RegExp(`/${escapeRegExp(stripSegment)}$`),
271+
'',
272+
)
269273
originalRoutePath = originalRoutePath.replace(
270-
new RegExp(`/${stripSegment}$`),
274+
new RegExp(`/${escapeRegExp(stripSegment)}$`),
271275
'',
272276
)
273277
}
@@ -305,13 +309,13 @@ export async function getRouteNodes(
305309

306310
routePath =
307311
routePath.replace(
308-
new RegExp(`/${updatedLastRouteSegment}$`),
312+
new RegExp(`/${escapeRegExp(updatedLastRouteSegment)}$`),
309313
'/',
310314
) || (isLayoutRoute ? '' : '/')
311315

312316
originalRoutePath =
313317
originalRoutePath.replace(
314-
new RegExp(`/${indexTokenCandidate}$`),
318+
new RegExp(`/${escapeRegExp(indexTokenCandidate)}$`),
315319
'/',
316320
) || (isLayoutRoute ? '' : '/')
317321
}

packages/router-generator/src/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ export function isSegmentPathless(
414414
return !hasEscapedLeadingUnderscore(originalSegment)
415415
}
416416

417-
function escapeRegExp(s: string): string {
417+
export function escapeRegExp(s: string): string {
418418
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
419419
}
420420

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/* eslint-disable */
2+
3+
// @ts-nocheck
4+
5+
// noinspection JSUnusedGlobalSymbols
6+
7+
// This file was automatically generated by TanStack Router.
8+
// You should NOT make any changes in this file as it will be overwritten.
9+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10+
11+
import { Route as rootRouteImport } from './routes/__root'
12+
import { Route as DashboardChar43layoutRouteImport } from './routes/dashboard/+layout'
13+
import { Route as Char43pageRouteImport } from './routes/+page'
14+
import { Route as DashboardChar43pageRouteImport } from './routes/dashboard/+page'
15+
import { Route as DashboardSettingsRouteImport } from './routes/dashboard/settings'
16+
17+
const DashboardChar43layoutRoute = DashboardChar43layoutRouteImport.update({
18+
id: '/dashboard',
19+
path: '/dashboard',
20+
getParentRoute: () => rootRouteImport,
21+
} as any)
22+
const Char43pageRoute = Char43pageRouteImport.update({
23+
id: '/',
24+
path: '/',
25+
getParentRoute: () => rootRouteImport,
26+
} as any)
27+
const DashboardChar43pageRoute = DashboardChar43pageRouteImport.update({
28+
id: '/',
29+
path: '/',
30+
getParentRoute: () => DashboardChar43layoutRoute,
31+
} as any)
32+
const DashboardSettingsRoute = DashboardSettingsRouteImport.update({
33+
id: '/settings',
34+
path: '/settings',
35+
getParentRoute: () => DashboardChar43layoutRoute,
36+
} as any)
37+
38+
export interface FileRoutesByFullPath {
39+
'/': typeof Char43pageRoute
40+
'/dashboard': typeof DashboardChar43layoutRouteWithChildren
41+
'/dashboard/settings': typeof DashboardSettingsRoute
42+
'/dashboard/': typeof DashboardChar43pageRoute
43+
}
44+
export interface FileRoutesByTo {
45+
'/': typeof Char43pageRoute
46+
'/dashboard/settings': typeof DashboardSettingsRoute
47+
'/dashboard': typeof DashboardChar43pageRoute
48+
}
49+
export interface FileRoutesById {
50+
__root__: typeof rootRouteImport
51+
'/': typeof Char43pageRoute
52+
'/dashboard': typeof DashboardChar43layoutRouteWithChildren
53+
'/dashboard/settings': typeof DashboardSettingsRoute
54+
'/dashboard/': typeof DashboardChar43pageRoute
55+
}
56+
export interface FileRouteTypes {
57+
fileRoutesByFullPath: FileRoutesByFullPath
58+
fullPaths: '/' | '/dashboard' | '/dashboard/settings' | '/dashboard/'
59+
fileRoutesByTo: FileRoutesByTo
60+
to: '/' | '/dashboard/settings' | '/dashboard'
61+
id: '__root__' | '/' | '/dashboard' | '/dashboard/settings' | '/dashboard/'
62+
fileRoutesById: FileRoutesById
63+
}
64+
export interface RootRouteChildren {
65+
Char43pageRoute: typeof Char43pageRoute
66+
DashboardChar43layoutRoute: typeof DashboardChar43layoutRouteWithChildren
67+
}
68+
69+
declare module '@tanstack/react-router' {
70+
interface FileRoutesByPath {
71+
'/dashboard': {
72+
id: '/dashboard'
73+
path: '/dashboard'
74+
fullPath: '/dashboard'
75+
preLoaderRoute: typeof DashboardChar43layoutRouteImport
76+
parentRoute: typeof rootRouteImport
77+
}
78+
'/': {
79+
id: '/'
80+
path: '/'
81+
fullPath: '/'
82+
preLoaderRoute: typeof Char43pageRouteImport
83+
parentRoute: typeof rootRouteImport
84+
}
85+
'/dashboard/': {
86+
id: '/dashboard/'
87+
path: '/'
88+
fullPath: '/dashboard/'
89+
preLoaderRoute: typeof DashboardChar43pageRouteImport
90+
parentRoute: typeof DashboardChar43layoutRoute
91+
}
92+
'/dashboard/settings': {
93+
id: '/dashboard/settings'
94+
path: '/settings'
95+
fullPath: '/dashboard/settings'
96+
preLoaderRoute: typeof DashboardSettingsRouteImport
97+
parentRoute: typeof DashboardChar43layoutRoute
98+
}
99+
}
100+
}
101+
102+
interface DashboardChar43layoutRouteChildren {
103+
DashboardSettingsRoute: typeof DashboardSettingsRoute
104+
DashboardChar43pageRoute: typeof DashboardChar43pageRoute
105+
}
106+
107+
const DashboardChar43layoutRouteChildren: DashboardChar43layoutRouteChildren = {
108+
DashboardSettingsRoute: DashboardSettingsRoute,
109+
DashboardChar43pageRoute: DashboardChar43pageRoute,
110+
}
111+
112+
const DashboardChar43layoutRouteWithChildren =
113+
DashboardChar43layoutRoute._addFileChildren(
114+
DashboardChar43layoutRouteChildren,
115+
)
116+
117+
const rootRouteChildren: RootRouteChildren = {
118+
Char43pageRoute: Char43pageRoute,
119+
DashboardChar43layoutRoute: DashboardChar43layoutRouteWithChildren,
120+
}
121+
export const routeTree = rootRouteImport
122+
._addFileChildren(rootRouteChildren)
123+
._addFileTypes<FileRouteTypes>()
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/')({
4+
component: () => <div>Home Page</div>,
5+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { Outlet, createRootRoute } from '@tanstack/react-router'
2+
3+
export const Route = createRootRoute({
4+
component: () => <Outlet />,
5+
})
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Outlet, createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/dashboard')({
4+
component: () => (
5+
<div>
6+
<h1>Dashboard Layout</h1>
7+
<Outlet />
8+
</div>
9+
),
10+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/dashboard/')({
4+
component: () => <div>Dashboard Home</div>,
5+
})
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/dashboard/settings')({
4+
component: () => <div>Dashboard Settings</div>,
5+
})
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"indexToken": { "regex": "\\+page" },
3+
"routeToken": { "regex": "\\+layout" }
4+
}

0 commit comments

Comments
 (0)