Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,10 @@ export async function getRouteNodes(
`${dir}/${removeExt(node.filePath)}`,
)
node.routePath = routePath
// Keep originalRoutePath aligned with routePath for escape detection
if (node.originalRoutePath) {
node.originalRoutePath = `/${dir}${node.originalRoutePath}`
}
node.filePath = filePath
})

Expand Down
27 changes: 25 additions & 2 deletions packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path, { join, resolve } from 'node:path'
import {
determineInitialRoutePath,
removeExt,
removeLeadingSlash,
removeTrailingSlash,
Expand Down Expand Up @@ -156,6 +157,10 @@ export async function getRouteNodesRecursive(
`${node.pathPrefix}/${removeExt(subtreeNode.filePath)}`,
)
subtreeNode.routePath = `${parent?.routePath ?? ''}${node.pathPrefix}${subtreeNode.routePath}`
// Keep originalRoutePath aligned with routePath for escape detection
if (subtreeNode.originalRoutePath) {
subtreeNode.originalRoutePath = `${parent?.routePath ?? ''}${node.pathPrefix}${subtreeNode.originalRoutePath}`
}
subtreeNode.filePath = `${node.directory}/${subtreeNode.filePath}`
})
return routeNodes
Expand Down Expand Up @@ -186,14 +191,23 @@ export async function getRouteNodesRecursive(
const lastSegment = node.path
let routeNode: RouteNode

const routePath = `${parentRoutePath}/${removeLeadingSlash(lastSegment)}`
// Process the segment to handle escape sequences like [_]
const {
routePath: escapedSegment,
originalRoutePath: originalSegment,
} = determineInitialRoutePath(removeLeadingSlash(lastSegment))
const routePath = `${parentRoutePath}${escapedSegment}`
// Store the original path with brackets for escape detection
const originalRoutePath = `${parentRoutePath}${originalSegment}`

if (node.file) {
const { filePath, variableName, fullPath } = getFile(node.file)
routeNode = {
filePath,
fullPath,
variableName,
routePath,
originalRoutePath,
_fsRouteType: 'static',
}
} else {
Expand All @@ -202,6 +216,7 @@ export async function getRouteNodesRecursive(
fullPath: '',
variableName: routePathToVariable(routePath),
routePath,
originalRoutePath,
isVirtual: true,
_fsRouteType: 'static',
}
Expand Down Expand Up @@ -235,13 +250,21 @@ export async function getRouteNodesRecursive(
node.id = ensureLeadingUnderScore(fileNameWithoutExt)
}
const lastSegment = node.id
const routePath = `${parentRoutePath}/${removeLeadingSlash(lastSegment)}`
// Process the segment to handle escape sequences like [_]
const {
routePath: escapedSegment,
originalRoutePath: originalSegment,
} = determineInitialRoutePath(removeLeadingSlash(lastSegment))
const routePath = `${parentRoutePath}${escapedSegment}`
// Store the original path with brackets for escape detection
const originalRoutePath = `${parentRoutePath}${originalSegment}`

const routeNode: RouteNode = {
fullPath,
filePath,
variableName,
routePath,
originalRoutePath,
_fsRouteType: 'pathless_layout',
}

Expand Down
11 changes: 11 additions & 0 deletions packages/router-generator/tests/generator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,17 @@ function rewriteConfigByFolderName(folderName: string, config: Config) {
case 'virtual-config-file-default-export':
config.virtualRouteConfig = './routes.ts'
break
case 'virtual-with-escaped-underscore':
{
// Test case for escaped underscores in physical routes mounted via virtual config
// This ensures originalRoutePath is correctly prefixed when paths are updated
const virtualRouteConfig = rootRoute('__root.tsx', [
index('index.tsx'),
physical('/api', 'physical-routes'),
])
config.virtualRouteConfig = virtualRouteConfig
}
break
case 'types-disabled':
config.disableTypes = true
config.generatedRouteTree =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from './routes/__root'
import { Route as IndexRouteImport } from './routes/index'
import { Route as nestedCallbackRouteImport } from './routes/nested/callback'
import { Route as nestedAuthRouteImport } from './routes/nested/auth'
import { Route as nestedHomeRouteImport } from './routes/nested/home'

const IndexRoute = IndexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const nestedCallbackRoute = nestedCallbackRouteImport.update({
id: '/nested/_callback',
path: '/nested/_callback',
getParentRoute: () => rootRouteImport,
} as any)
const nestedAuthRoute = nestedAuthRouteImport.update({
id: '/nested/_auth',
path: '/nested/_auth',
getParentRoute: () => rootRouteImport,
} as any)
const nestedHomeRoute = nestedHomeRouteImport.update({
id: '/nested/',
path: '/nested/',
getParentRoute: () => rootRouteImport,
} as any)

export interface FileRoutesByFullPath {
'/': typeof IndexRoute
'/nested': typeof nestedHomeRoute
'/nested/_auth': typeof nestedAuthRoute
'/nested/_callback': typeof nestedCallbackRoute
}
export interface FileRoutesByTo {
'/': typeof IndexRoute
'/nested': typeof nestedHomeRoute
'/nested/_auth': typeof nestedAuthRoute
'/nested/_callback': typeof nestedCallbackRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof IndexRoute
'/nested/': typeof nestedHomeRoute
'/nested/_auth': typeof nestedAuthRoute
'/nested/_callback': typeof nestedCallbackRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/nested' | '/nested/_auth' | '/nested/_callback'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/nested' | '/nested/_auth' | '/nested/_callback'
id: '__root__' | '/' | '/nested/' | '/nested/_auth' | '/nested/_callback'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
IndexRoute: typeof IndexRoute
nestedHomeRoute: typeof nestedHomeRoute
nestedAuthRoute: typeof nestedAuthRoute
nestedCallbackRoute: typeof nestedCallbackRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof IndexRouteImport
parentRoute: typeof rootRouteImport
}
'/nested/_callback': {
id: '/nested/_callback'
path: '/nested/_callback'
fullPath: '/nested/_callback'
preLoaderRoute: typeof nestedCallbackRouteImport
parentRoute: typeof rootRouteImport
}
'/nested/_auth': {
id: '/nested/_auth'
path: '/nested/_auth'
fullPath: '/nested/_auth'
preLoaderRoute: typeof nestedAuthRouteImport
parentRoute: typeof rootRouteImport
}
'/nested/': {
id: '/nested/'
path: '/nested'
fullPath: '/nested'
preLoaderRoute: typeof nestedHomeRouteImport
parentRoute: typeof rootRouteImport
}
}
}

const rootRouteChildren: RootRouteChildren = {
IndexRoute: IndexRoute,
nestedHomeRoute: nestedHomeRoute,
nestedAuthRoute: nestedAuthRoute,
nestedCallbackRoute: nestedCallbackRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { createRootRoute } from '@tanstack/react-router'
export const Route = createRootRoute()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/')()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {
defineVirtualSubtreeConfig,
index,
route,
} from '@tanstack/virtual-file-routes'

export default defineVirtualSubtreeConfig([
index('home.tsx'),
route('[_]auth', 'auth.tsx'),
route('[_]callback', 'callback.tsx'),
])
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/nested/_auth')()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/nested/_callback')()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { createFileRoute } from '@tanstack/react-router'
export const Route = createFileRoute('/nested/')()
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from './routes/__root'
import { Route as indexRouteImport } from './routes/index'
import { Route as ApiIndexRouteImport } from './routes/physical-routes/index'
import { Route as ApiChar91_Char93helloRouteImport } from './routes/physical-routes/[_]hello'
import { Route as ApiChar91_Char93authDotrouteRouteImport } from './routes/physical-routes/[_]auth.route'

const indexRoute = indexRouteImport.update({
id: '/',
path: '/',
getParentRoute: () => rootRouteImport,
} as any)
const ApiIndexRoute = ApiIndexRouteImport.update({
id: '/api/',
path: '/api/',
getParentRoute: () => rootRouteImport,
} as any)
const ApiChar91_Char93helloRoute = ApiChar91_Char93helloRouteImport.update({
id: '/api/_hello',
path: '/api/_hello',
getParentRoute: () => rootRouteImport,
} as any)
const ApiChar91_Char93authDotrouteRoute =
ApiChar91_Char93authDotrouteRouteImport.update({
id: '/api/_auth',
path: '/api/_auth',
getParentRoute: () => rootRouteImport,
} as any)

export interface FileRoutesByFullPath {
'/': typeof indexRoute
'/api/_auth': typeof ApiChar91_Char93authDotrouteRoute
'/api/_hello': typeof ApiChar91_Char93helloRoute
'/api': typeof ApiIndexRoute
}
export interface FileRoutesByTo {
'/': typeof indexRoute
'/api/_auth': typeof ApiChar91_Char93authDotrouteRoute
'/api/_hello': typeof ApiChar91_Char93helloRoute
'/api': typeof ApiIndexRoute
}
export interface FileRoutesById {
__root__: typeof rootRouteImport
'/': typeof indexRoute
'/api/_auth': typeof ApiChar91_Char93authDotrouteRoute
'/api/_hello': typeof ApiChar91_Char93helloRoute
'/api/': typeof ApiIndexRoute
}
export interface FileRouteTypes {
fileRoutesByFullPath: FileRoutesByFullPath
fullPaths: '/' | '/api/_auth' | '/api/_hello' | '/api'
fileRoutesByTo: FileRoutesByTo
to: '/' | '/api/_auth' | '/api/_hello' | '/api'
id: '__root__' | '/' | '/api/_auth' | '/api/_hello' | '/api/'
fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
indexRoute: typeof indexRoute
ApiChar91_Char93authDotrouteRoute: typeof ApiChar91_Char93authDotrouteRoute
ApiChar91_Char93helloRoute: typeof ApiChar91_Char93helloRoute
ApiIndexRoute: typeof ApiIndexRoute
}

declare module '@tanstack/react-router' {
interface FileRoutesByPath {
'/': {
id: '/'
path: '/'
fullPath: '/'
preLoaderRoute: typeof indexRouteImport
parentRoute: typeof rootRouteImport
}
'/api/': {
id: '/api/'
path: '/api'
fullPath: '/api'
preLoaderRoute: typeof ApiIndexRouteImport
parentRoute: typeof rootRouteImport
}
'/api/_hello': {
id: '/api/_hello'
path: '/api/_hello'
fullPath: '/api/_hello'
preLoaderRoute: typeof ApiChar91_Char93helloRouteImport
parentRoute: typeof rootRouteImport
}
'/api/_auth': {
id: '/api/_auth'
path: '/api/_auth'
fullPath: '/api/_auth'
preLoaderRoute: typeof ApiChar91_Char93authDotrouteRouteImport
parentRoute: typeof rootRouteImport
}
}
}

const rootRouteChildren: RootRouteChildren = {
indexRoute: indexRoute,
ApiChar91_Char93authDotrouteRoute: ApiChar91_Char93authDotrouteRoute,
ApiChar91_Char93helloRoute: ApiChar91_Char93helloRoute,
ApiIndexRoute: ApiIndexRoute,
}
export const routeTree = rootRouteImport
._addFileChildren(rootRouteChildren)
._addFileTypes<FileRouteTypes>()
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute()
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/')({
component: () => 'Hello',
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/_auth')({
component: () => 'Auth Route',
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/_hello')({
component: () => 'Hello API',
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createFileRoute } from '@tanstack/react-router'

export const Route = createFileRoute('/api/')({
component: () => 'API Index',
})
Loading