diff --git a/packages/router-generator/src/generator.ts b/packages/router-generator/src/generator.ts index 3ec4b46576a..0ecb325ceee 100644 --- a/packages/router-generator/src/generator.ts +++ b/packages/router-generator/src/generator.ts @@ -1357,7 +1357,24 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved prefixMap: RoutePrefixMap, config?: Config, ) { - const parentRoute = hasParentRoute(prefixMap, node, node.routePath) + let parentRoute = hasParentRoute(prefixMap, node, node.routePath) + + // Fallback: check acc.routeNodesByPath for parents not in prefixMap + // This handles virtual routes created from lazy-only files that weren't + // in the initial prefixMap build + if (!parentRoute && node.routePath) { + let searchPath = node.routePath + while (searchPath.length > 0) { + const lastSlash = searchPath.lastIndexOf('/') + if (lastSlash <= 0) break + searchPath = searchPath.substring(0, lastSlash) + const candidate = acc.routeNodesByPath.get(searchPath) + if (candidate && candidate.routePath !== node.routePath) { + parentRoute = candidate + break + } + } + } if (parentRoute) node.parent = parentRoute @@ -1489,7 +1506,11 @@ ${acc.routeTree.map((child) => `${child.variableName}Route: typeof ${getResolved } acc.routeNodes.push(node) - if (node.routePath && !node.isVirtual) { + if (node.routePath) { + // Always register routes by path so child routes can find parents. + // Virtual routes (created from lazy-only files) also need to be registered + // so that index routes like path.index.lazy.tsx can find their parent path.lazy.tsx. + // If a non-virtual route is later processed for the same path, it will overwrite. acc.routeNodesByPath.set(node.routePath, node) } } diff --git a/packages/router-generator/tests/generator/lazy-only-layout-and-index/routeTree.snapshot.ts b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routeTree.snapshot.ts new file mode 100644 index 00000000000..dd385cf7ac4 --- /dev/null +++ b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routeTree.snapshot.ts @@ -0,0 +1,89 @@ +/* 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 { createFileRoute } from '@tanstack/react-router' + +import { Route as rootRouteImport } from './routes/__root' + +const PathLazyRouteImport = createFileRoute('/path')() +const PathIndexLazyRouteImport = createFileRoute('/path/')() + +const PathLazyRoute = PathLazyRouteImport.update({ + id: '/path', + path: '/path', + getParentRoute: () => rootRouteImport, +} as any).lazy(() => import('./routes/path.lazy').then((d) => d.Route)) +const PathIndexLazyRoute = PathIndexLazyRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => PathLazyRoute, +} as any).lazy(() => import('./routes/path.index.lazy').then((d) => d.Route)) + +export interface FileRoutesByFullPath { + '/path': typeof PathLazyRouteWithChildren + '/path/': typeof PathIndexLazyRoute +} +export interface FileRoutesByTo { + '/path': typeof PathIndexLazyRoute +} +export interface FileRoutesById { + __root__: typeof rootRouteImport + '/path': typeof PathLazyRouteWithChildren + '/path/': typeof PathIndexLazyRoute +} +export interface FileRouteTypes { + fileRoutesByFullPath: FileRoutesByFullPath + fullPaths: '/path' | '/path/' + fileRoutesByTo: FileRoutesByTo + to: '/path' + id: '__root__' | '/path' | '/path/' + fileRoutesById: FileRoutesById +} +export interface RootRouteChildren { + PathLazyRoute: typeof PathLazyRouteWithChildren +} + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/path': { + id: '/path' + path: '/path' + fullPath: '/path' + preLoaderRoute: typeof PathLazyRouteImport + parentRoute: typeof rootRouteImport + } + '/path/': { + id: '/path/' + path: '/' + fullPath: '/path/' + preLoaderRoute: typeof PathIndexLazyRouteImport + parentRoute: typeof PathLazyRoute + } + } +} + +interface PathLazyRouteChildren { + PathIndexLazyRoute: typeof PathIndexLazyRoute +} + +const PathLazyRouteChildren: PathLazyRouteChildren = { + PathIndexLazyRoute: PathIndexLazyRoute, +} + +const PathLazyRouteWithChildren = PathLazyRoute._addFileChildren( + PathLazyRouteChildren, +) + +const rootRouteChildren: RootRouteChildren = { + PathLazyRoute: PathLazyRouteWithChildren, +} +export const routeTree = rootRouteImport + ._addFileChildren(rootRouteChildren) + ._addFileTypes() diff --git a/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/__root.tsx b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/__root.tsx new file mode 100644 index 00000000000..87099187f86 --- /dev/null +++ b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/__root.tsx @@ -0,0 +1,2 @@ +import { createRootRoute } from '@tanstack/react-router' +export const Route = createRootRoute() diff --git a/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.index.lazy.tsx b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.index.lazy.tsx new file mode 100644 index 00000000000..2710d8adab8 --- /dev/null +++ b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.index.lazy.tsx @@ -0,0 +1,4 @@ +import { createLazyFileRoute } from '@tanstack/react-router' +export const Route = createLazyFileRoute('/path/')({ + component: () => 'Path Index', +}) diff --git a/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.lazy.tsx b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.lazy.tsx new file mode 100644 index 00000000000..1b707b69948 --- /dev/null +++ b/packages/router-generator/tests/generator/lazy-only-layout-and-index/routes/path.lazy.tsx @@ -0,0 +1,4 @@ +import { createLazyFileRoute } from '@tanstack/react-router' +export const Route = createLazyFileRoute('/path')({ + component: () => 'Path Layout', +})