diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts index c1503cfdaa0..ad5f737ec76 100644 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-file-based/src/routeTree.gen.ts @@ -23,6 +23,7 @@ import { Route as Char45824Char54620Char48124Char44397RouteRouteImport } from '. import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' import { Route as PathlessLayoutRouteRouteImport } from './routes/pathless-layout/route' import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' +import { Route as FullpathTestRouteRouteImport } from './routes/fullpath-test/route' import { Route as IndexRouteImport } from './routes/index' import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' import { Route as RelativeIndexRouteImport } from './routes/relative/index' @@ -47,10 +48,12 @@ import { Route as NonNestedPrefixRouteRouteImport } from './routes/non-nested/pr import { Route as NonNestedPathRouteRouteImport } from './routes/non-nested/path/route' import { Route as NonNestedNamedRouteRouteImport } from './routes/non-nested/named/route' import { Route as NonNestedDeepRouteRouteImport } from './routes/non-nested/deep/route' +import { Route as FullpathTestLayoutRouteRouteImport } from './routes/fullpath-test/_layout/route' import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' import { Route as PathlessLayoutLayoutIndexRouteImport } from './routes/pathless-layout/_layout/index' import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' +import { Route as FullpathTestLayoutIndexRouteImport } from './routes/fullpath-test/_layout/index' import { Route as Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport } from './routes/λŒ€ν•œλ―Όκ΅­/πŸš€.$id' import { Route as Char45824Char54620Char48124Char44397WildcardSplatRouteImport } from './routes/λŒ€ν•œλ―Όκ΅­/wildcard.$' import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' @@ -79,6 +82,7 @@ import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './rout import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' import { Route as MasksPublicUsernameRouteImport } from './routes/masks.public.$username' import { Route as MasksAdminUserIdRouteImport } from './routes/masks.admin.$userId' +import { Route as FullpathTestLayoutIdRouteImport } from './routes/fullpath-test/_layout/$id' import { Route as LayoutLayout2LayoutBRouteImport } from './routes/_layout/_layout-2/layout-b' import { Route as LayoutLayout2LayoutARouteImport } from './routes/_layout/_layout-2/layout-a' import { Route as groupSubfolderInsideRouteImport } from './routes/(group)/subfolder/inside' @@ -193,6 +197,11 @@ const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ path: '/non-nested', getParentRoute: () => rootRouteImport, } as any) +const FullpathTestRouteRoute = FullpathTestRouteRouteImport.update({ + id: '/fullpath-test', + path: '/fullpath-test', + getParentRoute: () => rootRouteImport, +} as any) const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', @@ -316,6 +325,10 @@ const NonNestedDeepRouteRoute = NonNestedDeepRouteRouteImport.update({ path: '/deep', getParentRoute: () => NonNestedRouteRoute, } as any) +const FullpathTestLayoutRouteRoute = FullpathTestLayoutRouteRouteImport.update({ + id: '/_layout', + getParentRoute: () => FullpathTestRouteRoute, +} as any) const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ id: '/', path: '/', @@ -337,6 +350,11 @@ const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ path: '/params-ps/named/', getParentRoute: () => rootRouteImport, } as any) +const FullpathTestLayoutIndexRoute = FullpathTestLayoutIndexRouteImport.update({ + id: '/', + path: '/', + getParentRoute: () => FullpathTestLayoutRouteRoute, +} as any) const Char45824Char54620Char48124Char44397Char55357Char56960IdRoute = Char45824Char54620Char48124Char44397Char55357Char56960IdRouteImport.update({ id: '/πŸš€/$id', @@ -496,6 +514,11 @@ const MasksAdminUserIdRoute = MasksAdminUserIdRouteImport.update({ path: '/admin/$userId', getParentRoute: () => MasksRoute, } as any) +const FullpathTestLayoutIdRoute = FullpathTestLayoutIdRouteImport.update({ + id: '/$id', + path: '/$id', + getParentRoute: () => FullpathTestLayoutRouteRoute, +} as any) const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ id: '/layout-b', path: '/layout-b', @@ -737,6 +760,7 @@ const NonNestedDeepBazBarFooQuxRoute = export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/fullpath-test': typeof FullpathTestLayoutRouteRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutLayoutRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren @@ -765,10 +789,10 @@ export interface FileRoutesByFullPath { '/redirect/$target': typeof RedirectTargetRouteWithChildren '/search-params/default': typeof SearchParamsDefaultRoute '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute - '/params-ps': typeof ParamsPsIndexRoute + '/params-ps/': typeof ParamsPsIndexRoute '/posts/': typeof PostsIndexRoute - '/redirect': typeof RedirectIndexRoute - '/relative': typeof RelativeIndexRoute + '/redirect/': typeof RedirectIndexRoute + '/relative/': typeof RelativeIndexRoute '/search-params/': typeof SearchParamsIndexRoute '/non-nested/deep/$baz': typeof NonNestedDeepBazRouteRouteWithChildren '/non-nested/named/$baz': typeof NonNestedNamedBazRouteRouteWithChildren @@ -781,6 +805,7 @@ export interface FileRoutesByFullPath { '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute + '/fullpath-test/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route @@ -809,8 +834,9 @@ export interface FileRoutesByFullPath { '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute '/λŒ€ν•œλ―Όκ΅­/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute - '/params-ps/named': typeof ParamsPsNamedIndexRoute - '/params-ps/wildcard': typeof ParamsPsWildcardIndexRoute + '/fullpath-test/': typeof FullpathTestLayoutIndexRoute + '/params-ps/named/': typeof ParamsPsNamedIndexRoute + '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute '/pathless-layout/': typeof PathlessLayoutLayoutIndexRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/non-nested/deep/$baz/bar': typeof NonNestedDeepBazBarRouteRouteWithChildren @@ -829,25 +855,26 @@ export interface FileRoutesByFullPath { '/non-nested/path/baz/': typeof NonNestedPathBazIndexRoute '/non-nested/prefix/prefix{$baz}/': typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute '/non-nested/suffix/{$baz}suffix/': typeof NonNestedSuffixChar123bazChar125suffixIndexRoute - '/relative/link/nested': typeof RelativeLinkNestedIndexRoute - '/relative/link/path': typeof RelativeLinkPathIndexRoute - '/relative/link/with-search': typeof RelativeLinkWithSearchIndexRoute - '/relative/useNavigate/nested': typeof RelativeUseNavigateNestedIndexRoute - '/relative/useNavigate/path': typeof RelativeUseNavigatePathIndexRoute - '/relative/useNavigate/with-search': typeof RelativeUseNavigateWithSearchIndexRoute + '/relative/link/nested/': typeof RelativeLinkNestedIndexRoute + '/relative/link/path/': typeof RelativeLinkPathIndexRoute + '/relative/link/with-search/': typeof RelativeLinkWithSearchIndexRoute + '/relative/useNavigate/nested/': typeof RelativeUseNavigateNestedIndexRoute + '/relative/useNavigate/path/': typeof RelativeUseNavigatePathIndexRoute + '/relative/useNavigate/with-search/': typeof RelativeUseNavigateWithSearchIndexRoute '/non-nested/deep/$baz/bar/$foo': typeof NonNestedDeepBazBarFooRouteRouteWithChildren '/non-nested/deep/$baz/bar/qux': typeof NonNestedDeepBazBarQuxRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute '/non-nested/deep/$baz/bar/': typeof NonNestedDeepBazBarIndexRoute - '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute - '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute - '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute - '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute + '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute + '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute + '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute + '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute '/non-nested/deep/$baz/bar/$foo/qux': typeof NonNestedDeepBazBarFooQuxRoute '/non-nested/deep/$baz/bar/$foo/': typeof NonNestedDeepBazBarFooIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/fullpath-test': typeof FullpathTestLayoutIndexRoute '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutLayoutIndexRoute '/λŒ€ν•œλ―Όκ΅­': typeof Char45824Char54620Char48124Char44397RouteRouteWithChildren @@ -884,6 +911,7 @@ export interface FileRoutesByTo { '/subfolder/inside': typeof groupSubfolderInsideRoute '/layout-a': typeof LayoutLayout2LayoutARoute '/layout-b': typeof LayoutLayout2LayoutBRoute + '/fullpath-test/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route @@ -949,6 +977,7 @@ export interface FileRoutesByTo { export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/fullpath-test': typeof FullpathTestRouteRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren '/pathless-layout': typeof PathlessLayoutRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren @@ -963,6 +992,7 @@ export interface FileRoutesById { '/notRemountDeps': typeof NotRemountDepsRoute '/posts': typeof PostsRouteWithChildren '/remountDeps': typeof RemountDepsRoute + '/fullpath-test/_layout': typeof FullpathTestLayoutRouteRouteWithChildren '/non-nested/deep': typeof NonNestedDeepRouteRouteWithChildren '/non-nested/named': typeof NonNestedNamedRouteRouteWithChildren '/non-nested/path': typeof NonNestedPathRouteRouteWithChildren @@ -997,6 +1027,7 @@ export interface FileRoutesById { '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute + '/fullpath-test/_layout/$id': typeof FullpathTestLayoutIdRoute '/masks/admin/$userId': typeof MasksAdminUserIdRoute '/masks/public/$username': typeof MasksPublicUsernameRoute '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route @@ -1025,6 +1056,7 @@ export interface FileRoutesById { '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute '/λŒ€ν•œλ―Όκ΅­/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute + '/fullpath-test/_layout/': typeof FullpathTestLayoutIndexRoute '/params-ps/named/': typeof ParamsPsNamedIndexRoute '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute '/pathless-layout/_layout/': typeof PathlessLayoutLayoutIndexRoute @@ -1066,6 +1098,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' + | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/search-params' @@ -1094,10 +1127,10 @@ export interface FileRouteTypes { | '/redirect/$target' | '/search-params/default' | '/structural-sharing/$enabled' - | '/params-ps' + | '/params-ps/' | '/posts/' - | '/redirect' - | '/relative' + | '/redirect/' + | '/relative/' | '/search-params/' | '/non-nested/deep/$baz' | '/non-nested/named/$baz' @@ -1110,6 +1143,7 @@ export interface FileRouteTypes { | '/subfolder/inside' | '/layout-a' | '/layout-b' + | '/fullpath-test/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' @@ -1138,8 +1172,9 @@ export interface FileRouteTypes { | '/relative/useNavigate/relative-useNavigate-b' | '/λŒ€ν•œλ―Όκ΅­/wildcard/$' | '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id' - | '/params-ps/named' - | '/params-ps/wildcard' + | '/fullpath-test/' + | '/params-ps/named/' + | '/params-ps/wildcard/' | '/pathless-layout/' | '/redirect/$target/' | '/non-nested/deep/$baz/bar' @@ -1158,25 +1193,26 @@ export interface FileRouteTypes { | '/non-nested/path/baz/' | '/non-nested/prefix/prefix{$baz}/' | '/non-nested/suffix/{$baz}suffix/' - | '/relative/link/nested' - | '/relative/link/path' - | '/relative/link/with-search' - | '/relative/useNavigate/nested' - | '/relative/useNavigate/path' - | '/relative/useNavigate/with-search' + | '/relative/link/nested/' + | '/relative/link/path/' + | '/relative/link/with-search/' + | '/relative/useNavigate/nested/' + | '/relative/useNavigate/path/' + | '/relative/useNavigate/with-search/' | '/non-nested/deep/$baz/bar/$foo' | '/non-nested/deep/$baz/bar/qux' | '/params-ps/named/$foo/$bar/$baz' | '/non-nested/deep/$baz/bar/' - | '/relative/link/nested/deep' - | '/relative/link/path/$path' - | '/relative/useNavigate/nested/deep' - | '/relative/useNavigate/path/$path' + | '/relative/link/nested/deep/' + | '/relative/link/path/$path/' + | '/relative/useNavigate/nested/deep/' + | '/relative/useNavigate/path/$path/' | '/non-nested/deep/$baz/bar/$foo/qux' | '/non-nested/deep/$baz/bar/$foo/' fileRoutesByTo: FileRoutesByTo to: | '/' + | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/λŒ€ν•œλ―Όκ΅­' @@ -1213,6 +1249,7 @@ export interface FileRouteTypes { | '/subfolder/inside' | '/layout-a' | '/layout-b' + | '/fullpath-test/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' @@ -1277,6 +1314,7 @@ export interface FileRouteTypes { id: | '__root__' | '/' + | '/fullpath-test' | '/non-nested' | '/pathless-layout' | '/search-params' @@ -1291,6 +1329,7 @@ export interface FileRouteTypes { | '/notRemountDeps' | '/posts' | '/remountDeps' + | '/fullpath-test/_layout' | '/non-nested/deep' | '/non-nested/named' | '/non-nested/path' @@ -1325,6 +1364,7 @@ export interface FileRouteTypes { | '/(group)/subfolder/inside' | '/_layout/_layout-2/layout-a' | '/_layout/_layout-2/layout-b' + | '/fullpath-test/_layout/$id' | '/masks/admin/$userId' | '/masks/public/$username' | '/params-ps/named/prefix{$foo}' @@ -1353,6 +1393,7 @@ export interface FileRouteTypes { | '/relative/useNavigate/relative-useNavigate-b' | '/λŒ€ν•œλ―Όκ΅­/wildcard/$' | '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id' + | '/fullpath-test/_layout/' | '/params-ps/named/' | '/params-ps/wildcard/' | '/pathless-layout/_layout/' @@ -1393,6 +1434,7 @@ export interface FileRouteTypes { } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + FullpathTestRouteRoute: typeof FullpathTestRouteRouteWithChildren NonNestedRouteRoute: typeof NonNestedRouteRouteWithChildren PathlessLayoutRouteRoute: typeof PathlessLayoutRouteRouteWithChildren SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren @@ -1505,7 +1547,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } @@ -1537,6 +1579,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedRouteRouteImport parentRoute: typeof rootRouteImport } + '/fullpath-test': { + id: '/fullpath-test' + path: '/fullpath-test' + fullPath: '/fullpath-test' + preLoaderRoute: typeof FullpathTestRouteRouteImport + parentRoute: typeof rootRouteImport + } '/': { id: '/' path: '/' @@ -1554,14 +1603,14 @@ declare module '@tanstack/react-router' { '/relative/': { id: '/relative/' path: '/relative' - fullPath: '/relative' + fullPath: '/relative/' preLoaderRoute: typeof RelativeIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect/': { id: '/redirect/' path: '/redirect' - fullPath: '/redirect' + fullPath: '/redirect/' preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1575,7 +1624,7 @@ declare module '@tanstack/react-router' { '/params-ps/': { id: '/params-ps/' path: '/params-ps' - fullPath: '/params-ps' + fullPath: '/params-ps/' preLoaderRoute: typeof ParamsPsIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1610,7 +1659,7 @@ declare module '@tanstack/react-router' { '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } @@ -1705,6 +1754,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NonNestedDeepRouteRouteImport parentRoute: typeof NonNestedRouteRoute } + '/fullpath-test/_layout': { + id: '/fullpath-test/_layout' + path: '' + fullPath: '/fullpath-test' + preLoaderRoute: typeof FullpathTestLayoutRouteRouteImport + parentRoute: typeof FullpathTestRouteRoute + } '/redirect/$target/': { id: '/redirect/$target/' path: '/' @@ -1722,17 +1778,24 @@ declare module '@tanstack/react-router' { '/params-ps/wildcard/': { id: '/params-ps/wildcard/' path: '/params-ps/wildcard' - fullPath: '/params-ps/wildcard' + fullPath: '/params-ps/wildcard/' preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport parentRoute: typeof rootRouteImport } '/params-ps/named/': { id: '/params-ps/named/' path: '/params-ps/named' - fullPath: '/params-ps/named' + fullPath: '/params-ps/named/' preLoaderRoute: typeof ParamsPsNamedIndexRouteImport parentRoute: typeof rootRouteImport } + '/fullpath-test/_layout/': { + id: '/fullpath-test/_layout/' + path: '/' + fullPath: '/fullpath-test/' + preLoaderRoute: typeof FullpathTestLayoutIndexRouteImport + parentRoute: typeof FullpathTestLayoutRouteRoute + } '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id': { id: '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id' path: '/πŸš€/$id' @@ -1929,6 +1992,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MasksAdminUserIdRouteImport parentRoute: typeof MasksRoute } + '/fullpath-test/_layout/$id': { + id: '/fullpath-test/_layout/$id' + path: '/$id' + fullPath: '/fullpath-test/$id' + preLoaderRoute: typeof FullpathTestLayoutIdRouteImport + parentRoute: typeof FullpathTestLayoutRouteRoute + } '/_layout/_layout-2/layout-b': { id: '/_layout/_layout-2/layout-b' path: '/layout-b' @@ -2009,42 +2079,42 @@ declare module '@tanstack/react-router' { '/relative/useNavigate/with-search/': { id: '/relative/useNavigate/with-search/' path: '/with-search' - fullPath: '/relative/useNavigate/with-search' + fullPath: '/relative/useNavigate/with-search/' preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/path/': { id: '/relative/useNavigate/path/' path: '/path' - fullPath: '/relative/useNavigate/path' + fullPath: '/relative/useNavigate/path/' preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/': { id: '/relative/useNavigate/nested/' path: '/nested' - fullPath: '/relative/useNavigate/nested' + fullPath: '/relative/useNavigate/nested/' preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/with-search/': { id: '/relative/link/with-search/' path: '/with-search' - fullPath: '/relative/link/with-search' + fullPath: '/relative/link/with-search/' preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/path/': { id: '/relative/link/path/' path: '/path' - fullPath: '/relative/link/path' + fullPath: '/relative/link/path/' preLoaderRoute: typeof RelativeLinkPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/': { id: '/relative/link/nested/' path: '/nested' - fullPath: '/relative/link/nested' + fullPath: '/relative/link/nested/' preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } @@ -2163,28 +2233,28 @@ declare module '@tanstack/react-router' { '/relative/useNavigate/path/$path/': { id: '/relative/useNavigate/path/$path/' path: '/path/$path' - fullPath: '/relative/useNavigate/path/$path' + fullPath: '/relative/useNavigate/path/$path/' preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/deep/': { id: '/relative/useNavigate/nested/deep/' path: '/nested/deep' - fullPath: '/relative/useNavigate/nested/deep' + fullPath: '/relative/useNavigate/nested/deep/' preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/path/$path/': { id: '/relative/link/path/$path/' path: '/path/$path' - fullPath: '/relative/link/path/$path' + fullPath: '/relative/link/path/$path/' preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/deep/': { id: '/relative/link/nested/deep/' path: '/nested/deep' - fullPath: '/relative/link/nested/deep' + fullPath: '/relative/link/nested/deep/' preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } @@ -2233,6 +2303,33 @@ declare module '@tanstack/react-router' { } } +interface FullpathTestLayoutRouteRouteChildren { + FullpathTestLayoutIdRoute: typeof FullpathTestLayoutIdRoute + FullpathTestLayoutIndexRoute: typeof FullpathTestLayoutIndexRoute +} + +const FullpathTestLayoutRouteRouteChildren: FullpathTestLayoutRouteRouteChildren = + { + FullpathTestLayoutIdRoute: FullpathTestLayoutIdRoute, + FullpathTestLayoutIndexRoute: FullpathTestLayoutIndexRoute, + } + +const FullpathTestLayoutRouteRouteWithChildren = + FullpathTestLayoutRouteRoute._addFileChildren( + FullpathTestLayoutRouteRouteChildren, + ) + +interface FullpathTestRouteRouteChildren { + FullpathTestLayoutRouteRoute: typeof FullpathTestLayoutRouteRouteWithChildren +} + +const FullpathTestRouteRouteChildren: FullpathTestRouteRouteChildren = { + FullpathTestLayoutRouteRoute: FullpathTestLayoutRouteRouteWithChildren, +} + +const FullpathTestRouteRouteWithChildren = + FullpathTestRouteRoute._addFileChildren(FullpathTestRouteRouteChildren) + interface NonNestedDeepBazRouteRouteChildren { NonNestedDeepBazIndexRoute: typeof NonNestedDeepBazIndexRoute } @@ -2697,6 +2794,7 @@ const ParamsPsNamedFooRouteRouteWithChildren = const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + FullpathTestRouteRoute: FullpathTestRouteRouteWithChildren, NonNestedRouteRoute: NonNestedRouteRouteWithChildren, PathlessLayoutRouteRoute: PathlessLayoutRouteRouteWithChildren, SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren, diff --git a/e2e/react-router/basic-file-based/src/routes/__root.tsx b/e2e/react-router/basic-file-based/src/routes/__root.tsx index dde8139a6e2..298f97c1bae 100644 --- a/e2e/react-router/basic-file-based/src/routes/__root.tsx +++ b/e2e/react-router/basic-file-based/src/routes/__root.tsx @@ -164,6 +164,15 @@ function RootComponent() { }} > Pathless Layout + {' '} + + FullPath Test
diff --git a/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/$id.tsx b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/$id.tsx new file mode 100644 index 00000000000..44b1f077ff8 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/$id.tsx @@ -0,0 +1,23 @@ +import { createFileRoute, useMatches } from '@tanstack/react-router' + +export const Route = createFileRoute('/fullpath-test/_layout/$id')({ + component: IdComponent, +}) + +function IdComponent() { + const matches = useMatches() + const { id } = Route.useParams() + // Find this route's match by routeId + const idMatch = matches.find( + (m) => m.routeId === '/fullpath-test/_layout/$id', + ) + + return ( +
+
+ {idMatch?.fullPath ?? 'undefined'} +
+
Param: {id}
+
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/index.tsx b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/index.tsx new file mode 100644 index 00000000000..e84b81047aa --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/index.tsx @@ -0,0 +1,23 @@ +import { createFileRoute, useMatches } from '@tanstack/react-router' + +export const Route = createFileRoute('/fullpath-test/_layout/')({ + component: IndexComponent, +}) + +function IndexComponent() { + const matches = useMatches() + // Find this index route's match by routeId + const indexMatch = matches.find( + (m) => m.routeId === '/fullpath-test/_layout/', + ) + + return ( +
+
+ {indexMatch?.fullPath ?? 'undefined'} +
+
{Route.to}
+
FullPath Test Index
+
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/route.tsx b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/route.tsx new file mode 100644 index 00000000000..770888e62a2 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/fullpath-test/_layout/route.tsx @@ -0,0 +1,22 @@ +import { Outlet, createFileRoute, useMatches } from '@tanstack/react-router' + +export const Route = createFileRoute('/fullpath-test/_layout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + const matches = useMatches() + // Find this layout's match by routeId + const layoutMatch = matches.find( + (m) => m.routeId === '/fullpath-test/_layout', + ) + + return ( +
+
+ {layoutMatch?.fullPath ?? 'undefined'} +
+ +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/fullpath-test/route.tsx b/e2e/react-router/basic-file-based/src/routes/fullpath-test/route.tsx new file mode 100644 index 00000000000..e876f36cbca --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/fullpath-test/route.tsx @@ -0,0 +1,10 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/fullpath-test')({ + component: () => ( +
+

FullPath Test Section

+ +
+ ), +}) diff --git a/e2e/react-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx b/e2e/react-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx index 30b13cb09d2..36e95665bce 100644 --- a/e2e/react-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx +++ b/e2e/react-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx @@ -17,7 +17,7 @@ function RouteComponent() {
navigate({ - from: '/relative/useNavigate/path', + from: '/relative/useNavigate/path/', to: './$path', params: { path: path === 'a' ? 'b' : 'a' }, }) diff --git a/e2e/react-router/basic-file-based/tests/fullpath-types.spec.ts b/e2e/react-router/basic-file-based/tests/fullpath-types.spec.ts new file mode 100644 index 00000000000..d1bd9c1c413 --- /dev/null +++ b/e2e/react-router/basic-file-based/tests/fullpath-types.spec.ts @@ -0,0 +1,81 @@ +import { expect, test } from '@playwright/test' + +test.describe('FullPath type/runtime match (Issues #4892, #2675, #6403)', () => { + test.describe('Pathless layout routes should have parent fullPath, not empty string', () => { + test('direct navigation to pathless layout shows correct fullPath', async ({ + page, + }) => { + await page.goto('/fullpath-test') + // The pathless layout's fullPath should be '/fullpath-test' (same as parent), not '' + await expect(page.getByTestId('pathless-layout-fullpath')).toHaveText( + '/fullpath-test', + ) + }) + + test('client-side navigation to pathless layout shows correct fullPath', async ({ + page, + }) => { + await page.goto('/') + await page.getByTestId('link-to-fullpath-test').click() + await expect(page.getByTestId('pathless-layout-fullpath')).toHaveText( + '/fullpath-test', + ) + }) + }) + + test.describe('Index routes should have trailing slash in fullPath', () => { + test('direct navigation to index route shows fullPath with trailing slash', async ({ + page, + }) => { + await page.goto('/fullpath-test') + // The index route's fullPath should be '/fullpath-test/' (with trailing slash) + await expect(page.getByTestId('index-route-fullpath')).toHaveText( + '/fullpath-test/', + ) + }) + + test('param route under pathless layout shows correct fullPath', async ({ + page, + }) => { + await page.goto('/fullpath-test/123') + // The param route's fullPath should be '/fullpath-test/$id' + await expect(page.getByTestId('param-route-fullpath')).toHaveText( + '/fullpath-test/$id', + ) + await expect(page.getByTestId('fullpath-test-param')).toHaveText( + 'Param: 123', + ) + }) + }) + + test.describe('Route.to should NOT have trailing slash (Issue #3005)', () => { + test('index route Route.to should not have trailing slash', async ({ + page, + }) => { + await page.goto('/fullpath-test') + // Route.to should be '/fullpath-test' (without trailing slash) + // while Route.fullPath is '/fullpath-test/' (with trailing slash) + await expect(page.getByTestId('index-route-to')).toHaveText( + '/fullpath-test', + ) + }) + }) + + test.describe('Existing pathless layout routes', () => { + test('existing pathless layout index shows correct fullPath at runtime', async ({ + page, + }) => { + // This tests the existing /pathless-layout route which has a pathless _layout + await page.goto('/pathless-layout') + await expect(page.getByTestId('pathless-layout-header')).toContainText( + 'Pathless Layout Section', + ) + await expect(page.getByTestId('pathless-layout-wrapper')).toContainText( + 'Pathless Layout Wrapper', + ) + await expect(page.getByTestId('pathless-layout-index')).toContainText( + 'Pathless Layout Index', + ) + }) + }) +}) diff --git a/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts index ab3e216c966..d7f30c2cd15 100644 --- a/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-virtual-file-based/src/routeTree.gen.ts @@ -178,7 +178,7 @@ declare module '@tanstack/react-router' { '/_first': { id: '/_first' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof layoutFirstLayoutRouteImport parentRoute: typeof rootRouteImport } @@ -199,7 +199,7 @@ declare module '@tanstack/react-router' { '/_first/_second': { id: '/_first/_second' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof layoutSecondLayoutRouteImport parentRoute: typeof layoutFirstLayoutRoute } diff --git a/e2e/react-start/basic/src/routeTree.gen.ts b/e2e/react-start/basic/src/routeTree.gen.ts index 27d5e911452..21841bff704 100644 --- a/e2e/react-start/basic/src/routeTree.gen.ts +++ b/e2e/react-start/basic/src/routeTree.gen.ts @@ -354,11 +354,11 @@ export interface FileRoutesByFullPath { '/search-params/default': typeof SearchParamsDefaultRoute '/search-params/loader-throws-redirect': typeof SearchParamsLoaderThrowsRedirectRoute '/users/$userId': typeof UsersUserIdRoute - '/multi-cookie-redirect': typeof MultiCookieRedirectIndexRoute + '/multi-cookie-redirect/': typeof MultiCookieRedirectIndexRoute '/not-found/': typeof NotFoundIndexRoute '/posts/': typeof PostsIndexRoute '/raw-stream/': typeof RawStreamIndexRoute - '/redirect': typeof RedirectIndexRoute + '/redirect/': typeof RedirectIndexRoute '/search-params/': typeof SearchParamsIndexRoute '/users/': typeof UsersIndexRoute '/layout-a': typeof LayoutLayout2LayoutARoute @@ -372,7 +372,7 @@ export interface FileRoutesByFullPath { '/redirect/$target/serverFn/via-beforeLoad': typeof RedirectTargetServerFnViaBeforeLoadRoute '/redirect/$target/serverFn/via-loader': typeof RedirectTargetServerFnViaLoaderRoute '/redirect/$target/serverFn/via-useServerFn': typeof RedirectTargetServerFnViaUseServerFnRoute - '/redirect/$target/serverFn': typeof RedirectTargetServerFnIndexRoute + '/redirect/$target/serverFn/': typeof RedirectTargetServerFnIndexRoute '/foo/$bar/$qux/': typeof FooBarQuxHereIndexRoute } export interface FileRoutesByTo { @@ -505,11 +505,11 @@ export interface FileRouteTypes { | '/search-params/default' | '/search-params/loader-throws-redirect' | '/users/$userId' - | '/multi-cookie-redirect' + | '/multi-cookie-redirect/' | '/not-found/' | '/posts/' | '/raw-stream/' - | '/redirect' + | '/redirect/' | '/search-params/' | '/users/' | '/layout-a' @@ -523,7 +523,7 @@ export interface FileRouteTypes { | '/redirect/$target/serverFn/via-beforeLoad' | '/redirect/$target/serverFn/via-loader' | '/redirect/$target/serverFn/via-useServerFn' - | '/redirect/$target/serverFn' + | '/redirect/$target/serverFn/' | '/foo/$bar/$qux/' fileRoutesByTo: FileRoutesByTo to: @@ -731,7 +731,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } @@ -773,7 +773,7 @@ declare module '@tanstack/react-router' { '/redirect/': { id: '/redirect/' path: '/redirect' - fullPath: '/redirect' + fullPath: '/redirect/' preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -801,7 +801,7 @@ declare module '@tanstack/react-router' { '/multi-cookie-redirect/': { id: '/multi-cookie-redirect/' path: '/multi-cookie-redirect' - fullPath: '/multi-cookie-redirect' + fullPath: '/multi-cookie-redirect/' preLoaderRoute: typeof MultiCookieRedirectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -913,7 +913,7 @@ declare module '@tanstack/react-router' { '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } @@ -969,7 +969,7 @@ declare module '@tanstack/react-router' { '/redirect/$target/serverFn/': { id: '/redirect/$target/serverFn/' path: '/serverFn' - fullPath: '/redirect/$target/serverFn' + fullPath: '/redirect/$target/serverFn/' preLoaderRoute: typeof RedirectTargetServerFnIndexRouteImport parentRoute: typeof RedirectTargetRoute } diff --git a/e2e/react-start/server-functions/src/routeTree.gen.ts b/e2e/react-start/server-functions/src/routeTree.gen.ts index e71028621c7..f4c74f657b7 100644 --- a/e2e/react-start/server-functions/src/routeTree.gen.ts +++ b/e2e/react-start/server-functions/src/routeTree.gen.ts @@ -288,19 +288,19 @@ export interface FileRoutesByFullPath { '/middleware/unhandled-exception': typeof MiddlewareUnhandledExceptionRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute - '/abort-signal': typeof AbortSignalIndexRoute - '/cookies': typeof CookiesIndexRoute - '/factory': typeof FactoryIndexRoute - '/formdata-redirect': typeof FormdataRedirectIndexRoute - '/function-metadata': typeof FunctionMetadataIndexRoute - '/function-method': typeof FunctionMethodIndexRoute - '/middleware': typeof MiddlewareIndexRoute - '/primitives': typeof PrimitivesIndexRoute - '/redirect-test-ssr': typeof RedirectTestSsrIndexRoute - '/redirect-test': typeof RedirectTestIndexRoute + '/abort-signal/': typeof AbortSignalIndexRoute + '/cookies/': typeof CookiesIndexRoute + '/factory/': typeof FactoryIndexRoute + '/formdata-redirect/': typeof FormdataRedirectIndexRoute + '/function-metadata/': typeof FunctionMetadataIndexRoute + '/function-method/': typeof FunctionMethodIndexRoute + '/middleware/': typeof MiddlewareIndexRoute + '/primitives/': typeof PrimitivesIndexRoute + '/redirect-test-ssr/': typeof RedirectTestSsrIndexRoute + '/redirect-test/': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute '/middleware/redirect-with-middleware/target': typeof MiddlewareRedirectWithMiddlewareTargetRoute - '/middleware/redirect-with-middleware': typeof MiddlewareRedirectWithMiddlewareIndexRoute + '/middleware/redirect-with-middleware/': typeof MiddlewareRedirectWithMiddlewareIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -417,19 +417,19 @@ export interface FileRouteTypes { | '/middleware/unhandled-exception' | '/redirect-test-ssr/target' | '/redirect-test/target' - | '/abort-signal' - | '/cookies' - | '/factory' - | '/formdata-redirect' - | '/function-metadata' - | '/function-method' - | '/middleware' - | '/primitives' - | '/redirect-test-ssr' - | '/redirect-test' + | '/abort-signal/' + | '/cookies/' + | '/factory/' + | '/formdata-redirect/' + | '/function-metadata/' + | '/function-method/' + | '/middleware/' + | '/primitives/' + | '/redirect-test-ssr/' + | '/redirect-test/' | '/formdata-redirect/target/$name' | '/middleware/redirect-with-middleware/target' - | '/middleware/redirect-with-middleware' + | '/middleware/redirect-with-middleware/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -676,70 +676,70 @@ declare module '@tanstack/react-router' { '/redirect-test/': { id: '/redirect-test/' path: '/redirect-test' - fullPath: '/redirect-test' + fullPath: '/redirect-test/' preLoaderRoute: typeof RedirectTestIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test-ssr/': { id: '/redirect-test-ssr/' path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } '/primitives/': { id: '/primitives/' path: '/primitives' - fullPath: '/primitives' + fullPath: '/primitives/' preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } '/middleware/': { id: '/middleware/' path: '/middleware' - fullPath: '/middleware' + fullPath: '/middleware/' preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } '/function-method/': { id: '/function-method/' path: '/function-method' - fullPath: '/function-method' + fullPath: '/function-method/' preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { id: '/function-metadata/' path: '/function-metadata' - fullPath: '/function-metadata' + fullPath: '/function-metadata/' preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/': { id: '/formdata-redirect/' path: '/formdata-redirect' - fullPath: '/formdata-redirect' + fullPath: '/formdata-redirect/' preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/factory/': { id: '/factory/' path: '/factory' - fullPath: '/factory' + fullPath: '/factory/' preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } '/cookies/': { id: '/cookies/' path: '/cookies' - fullPath: '/cookies' + fullPath: '/cookies/' preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } '/abort-signal/': { id: '/abort-signal/' path: '/abort-signal' - fullPath: '/abort-signal' + fullPath: '/abort-signal/' preLoaderRoute: typeof AbortSignalIndexRouteImport parentRoute: typeof rootRouteImport } @@ -823,7 +823,7 @@ declare module '@tanstack/react-router' { '/middleware/redirect-with-middleware/': { id: '/middleware/redirect-with-middleware/' path: '/middleware/redirect-with-middleware' - fullPath: '/middleware/redirect-with-middleware' + fullPath: '/middleware/redirect-with-middleware/' preLoaderRoute: typeof MiddlewareRedirectWithMiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/react-start/server-functions/src/routes/cookies/index.tsx b/e2e/react-start/server-functions/src/routes/cookies/index.tsx index ae42043959c..005be0d9fa4 100644 --- a/e2e/react-start/server-functions/src/routes/cookies/index.tsx +++ b/e2e/react-start/server-functions/src/routes/cookies/index.tsx @@ -12,7 +12,7 @@ export const Route = createFileRoute('/cookies/')({ function RouteComponent() { const search = Route.useSearch() return ( - + got to route that sets the cookies with {JSON.stringify(search)} ) diff --git a/e2e/react-start/website/src/routeTree.gen.ts b/e2e/react-start/website/src/routeTree.gen.ts index 33b0aa6c4b3..e26402107fe 100644 --- a/e2e/react-start/website/src/routeTree.gen.ts +++ b/e2e/react-start/website/src/routeTree.gen.ts @@ -83,10 +83,11 @@ const ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute = } as any) export interface FileRoutesByFullPath { - '/$project': typeof ProjectIndexRoute '/': typeof LibraryIndexRoute - '/$project/$version/docs': typeof ProjectVersionDocsIndexRoute - '/$project/$version': typeof LibraryProjectVersionIndexRoute + '/$project': typeof LibraryProjectRouteWithChildren + '/$project/': typeof ProjectIndexRoute + '/$project/$version/docs/': typeof ProjectVersionDocsIndexRoute + '/$project/$version/': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework/{$}.md': typeof ProjectVersionDocsFrameworkFrameworkChar123Char125DotmdRoute @@ -120,10 +121,11 @@ export interface FileRoutesById { export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: - | '/$project' | '/' - | '/$project/$version/docs' - | '/$project/$version' + | '/$project' + | '/$project/' + | '/$project/$version/docs/' + | '/$project/$version/' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework/{$}.md' @@ -166,7 +168,7 @@ declare module '@tanstack/react-router' { '/_library': { id: '/_library' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } @@ -180,7 +182,7 @@ declare module '@tanstack/react-router' { '/$project/': { id: '/$project/' path: '/$project' - fullPath: '/$project' + fullPath: '/$project/' preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -194,14 +196,14 @@ declare module '@tanstack/react-router' { '/_library/$project/$version/': { id: '/_library/$project/$version/' path: '/$version' - fullPath: '/$project/$version' + fullPath: '/$project/$version/' preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport parentRoute: typeof LibraryProjectRoute } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' - fullPath: '/$project/$version/docs' + fullPath: '/$project/$version/docs/' preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/react-start/website/src/routes/$project.$version.docs.index.tsx b/e2e/react-start/website/src/routes/$project.$version.docs.index.tsx index 518a2bf4665..d6e3abbbf64 100644 --- a/e2e/react-start/website/src/routes/$project.$version.docs.index.tsx +++ b/e2e/react-start/website/src/routes/$project.$version.docs.index.tsx @@ -3,7 +3,7 @@ import { redirect, createFileRoute } from '@tanstack/react-router' export const Route = createFileRoute('/$project/$version/docs/')({ beforeLoad: () => { throw redirect({ - from: '/$project/$version/docs', + from: '/$project/$version/docs/', to: '/$project/$version/docs/framework/$framework/$', params: { framework: 'react', diff --git a/e2e/react-start/website/src/routes/_library.$project.$version.index.tsx b/e2e/react-start/website/src/routes/_library.$project.$version.index.tsx index a45a2775ace..31248b72a02 100644 --- a/e2e/react-start/website/src/routes/_library.$project.$version.index.tsx +++ b/e2e/react-start/website/src/routes/_library.$project.$version.index.tsx @@ -16,7 +16,7 @@ function Page() {

Get started with our documentation. diff --git a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts index 0b0315d3498..04fe5873747 100644 --- a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts @@ -709,10 +709,10 @@ export interface FileRoutesByFullPath { '/posts/$postId': typeof PostsPostIdRoute '/redirect/$target': typeof RedirectTargetRouteWithChildren '/search-params/default': typeof SearchParamsDefaultRoute - '/params-ps': typeof ParamsPsIndexRoute + '/params-ps/': typeof ParamsPsIndexRoute '/posts/': typeof PostsIndexRoute - '/redirect': typeof RedirectIndexRoute - '/relative': typeof RelativeIndexRoute + '/redirect/': typeof RedirectIndexRoute + '/relative/': typeof RelativeIndexRoute '/search-params/': typeof SearchParamsIndexRoute '/non-nested/deep/$baz': typeof NonNestedDeepBazRouteRouteWithChildren '/non-nested/named/$baz': typeof NonNestedNamedBazRouteRouteWithChildren @@ -749,8 +749,8 @@ export interface FileRoutesByFullPath { '/transition/typing/create-resource': typeof TransitionTypingCreateResourceRoute '/λŒ€ν•œλ―Όκ΅­/wildcard/$': typeof Char45824Char54620Char48124Char44397WildcardSplatRoute '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id': typeof Char45824Char54620Char48124Char44397Char55357Char56960IdRoute - '/params-ps/named': typeof ParamsPsNamedIndexRoute - '/params-ps/wildcard': typeof ParamsPsWildcardIndexRoute + '/params-ps/named/': typeof ParamsPsNamedIndexRoute + '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute '/redirect/$target/': typeof RedirectTargetIndexRoute '/non-nested/deep/$baz/bar': typeof NonNestedDeepBazBarRouteRouteWithChildren '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren @@ -768,20 +768,20 @@ export interface FileRoutesByFullPath { '/non-nested/path/baz/': typeof NonNestedPathBazIndexRoute '/non-nested/prefix/prefix{$baz}/': typeof NonNestedPrefixPrefixChar123bazChar125IndexRoute '/non-nested/suffix/{$baz}suffix/': typeof NonNestedSuffixChar123bazChar125suffixIndexRoute - '/relative/link/nested': typeof RelativeLinkNestedIndexRoute - '/relative/link/path': typeof RelativeLinkPathIndexRoute - '/relative/link/with-search': typeof RelativeLinkWithSearchIndexRoute - '/relative/useNavigate/nested': typeof RelativeUseNavigateNestedIndexRoute - '/relative/useNavigate/path': typeof RelativeUseNavigatePathIndexRoute - '/relative/useNavigate/with-search': typeof RelativeUseNavigateWithSearchIndexRoute + '/relative/link/nested/': typeof RelativeLinkNestedIndexRoute + '/relative/link/path/': typeof RelativeLinkPathIndexRoute + '/relative/link/with-search/': typeof RelativeLinkWithSearchIndexRoute + '/relative/useNavigate/nested/': typeof RelativeUseNavigateNestedIndexRoute + '/relative/useNavigate/path/': typeof RelativeUseNavigatePathIndexRoute + '/relative/useNavigate/with-search/': typeof RelativeUseNavigateWithSearchIndexRoute '/non-nested/deep/$baz/bar/$foo': typeof NonNestedDeepBazBarFooRouteRouteWithChildren '/non-nested/deep/$baz/bar/qux': typeof NonNestedDeepBazBarQuxRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute '/non-nested/deep/$baz/bar/': typeof NonNestedDeepBazBarIndexRoute - '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute - '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute - '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute - '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute + '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute + '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute + '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute + '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute '/non-nested/deep/$baz/bar/$foo/qux': typeof NonNestedDeepBazBarFooQuxRoute '/non-nested/deep/$baz/bar/$foo/': typeof NonNestedDeepBazBarFooIndexRoute } @@ -1017,10 +1017,10 @@ export interface FileRouteTypes { | '/posts/$postId' | '/redirect/$target' | '/search-params/default' - | '/params-ps' + | '/params-ps/' | '/posts/' - | '/redirect' - | '/relative' + | '/redirect/' + | '/relative/' | '/search-params/' | '/non-nested/deep/$baz' | '/non-nested/named/$baz' @@ -1057,8 +1057,8 @@ export interface FileRouteTypes { | '/transition/typing/create-resource' | '/λŒ€ν•œλ―Όκ΅­/wildcard/$' | '/λŒ€ν•œλ―Όκ΅­/πŸš€/$id' - | '/params-ps/named' - | '/params-ps/wildcard' + | '/params-ps/named/' + | '/params-ps/wildcard/' | '/redirect/$target/' | '/non-nested/deep/$baz/bar' | '/params-ps/named/$foo/$bar' @@ -1076,20 +1076,20 @@ export interface FileRouteTypes { | '/non-nested/path/baz/' | '/non-nested/prefix/prefix{$baz}/' | '/non-nested/suffix/{$baz}suffix/' - | '/relative/link/nested' - | '/relative/link/path' - | '/relative/link/with-search' - | '/relative/useNavigate/nested' - | '/relative/useNavigate/path' - | '/relative/useNavigate/with-search' + | '/relative/link/nested/' + | '/relative/link/path/' + | '/relative/link/with-search/' + | '/relative/useNavigate/nested/' + | '/relative/useNavigate/path/' + | '/relative/useNavigate/with-search/' | '/non-nested/deep/$baz/bar/$foo' | '/non-nested/deep/$baz/bar/qux' | '/params-ps/named/$foo/$bar/$baz' | '/non-nested/deep/$baz/bar/' - | '/relative/link/nested/deep' - | '/relative/link/path/$path' - | '/relative/useNavigate/nested/deep' - | '/relative/useNavigate/path/$path' + | '/relative/link/nested/deep/' + | '/relative/link/path/$path/' + | '/relative/useNavigate/nested/deep/' + | '/relative/useNavigate/path/$path/' | '/non-nested/deep/$baz/bar/$foo/qux' | '/non-nested/deep/$baz/bar/$foo/' fileRoutesByTo: FileRoutesByTo @@ -1409,7 +1409,7 @@ declare module '@tanstack/solid-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutRouteImport parentRoute: typeof rootRouteImport } @@ -1451,14 +1451,14 @@ declare module '@tanstack/solid-router' { '/relative/': { id: '/relative/' path: '/relative' - fullPath: '/relative' + fullPath: '/relative/' preLoaderRoute: typeof RelativeIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect/': { id: '/redirect/' path: '/redirect' - fullPath: '/redirect' + fullPath: '/redirect/' preLoaderRoute: typeof RedirectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1472,7 +1472,7 @@ declare module '@tanstack/solid-router' { '/params-ps/': { id: '/params-ps/' path: '/params-ps' - fullPath: '/params-ps' + fullPath: '/params-ps/' preLoaderRoute: typeof ParamsPsIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1500,7 +1500,7 @@ declare module '@tanstack/solid-router' { '/_layout/_layout-2': { id: '/_layout/_layout-2' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutLayout2RouteImport parentRoute: typeof LayoutRoute } @@ -1598,14 +1598,14 @@ declare module '@tanstack/solid-router' { '/params-ps/wildcard/': { id: '/params-ps/wildcard/' path: '/params-ps/wildcard' - fullPath: '/params-ps/wildcard' + fullPath: '/params-ps/wildcard/' preLoaderRoute: typeof ParamsPsWildcardIndexRouteImport parentRoute: typeof rootRouteImport } '/params-ps/named/': { id: '/params-ps/named/' path: '/params-ps/named' - fullPath: '/params-ps/named' + fullPath: '/params-ps/named/' preLoaderRoute: typeof ParamsPsNamedIndexRouteImport parentRoute: typeof rootRouteImport } @@ -1857,42 +1857,42 @@ declare module '@tanstack/solid-router' { '/relative/useNavigate/with-search/': { id: '/relative/useNavigate/with-search/' path: '/with-search' - fullPath: '/relative/useNavigate/with-search' + fullPath: '/relative/useNavigate/with-search/' preLoaderRoute: typeof RelativeUseNavigateWithSearchIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/path/': { id: '/relative/useNavigate/path/' path: '/path' - fullPath: '/relative/useNavigate/path' + fullPath: '/relative/useNavigate/path/' preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/': { id: '/relative/useNavigate/nested/' path: '/nested' - fullPath: '/relative/useNavigate/nested' + fullPath: '/relative/useNavigate/nested/' preLoaderRoute: typeof RelativeUseNavigateNestedIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/with-search/': { id: '/relative/link/with-search/' path: '/with-search' - fullPath: '/relative/link/with-search' + fullPath: '/relative/link/with-search/' preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/path/': { id: '/relative/link/path/' path: '/path' - fullPath: '/relative/link/path' + fullPath: '/relative/link/path/' preLoaderRoute: typeof RelativeLinkPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/': { id: '/relative/link/nested/' path: '/nested' - fullPath: '/relative/link/nested' + fullPath: '/relative/link/nested/' preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } @@ -2011,28 +2011,28 @@ declare module '@tanstack/solid-router' { '/relative/useNavigate/path/$path/': { id: '/relative/useNavigate/path/$path/' path: '/path/$path' - fullPath: '/relative/useNavigate/path/$path' + fullPath: '/relative/useNavigate/path/$path/' preLoaderRoute: typeof RelativeUseNavigatePathPathIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/useNavigate/nested/deep/': { id: '/relative/useNavigate/nested/deep/' path: '/nested/deep' - fullPath: '/relative/useNavigate/nested/deep' + fullPath: '/relative/useNavigate/nested/deep/' preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport parentRoute: typeof RelativeUseNavigateRouteRoute } '/relative/link/path/$path/': { id: '/relative/link/path/$path/' path: '/path/$path' - fullPath: '/relative/link/path/$path' + fullPath: '/relative/link/path/$path/' preLoaderRoute: typeof RelativeLinkPathPathIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } '/relative/link/nested/deep/': { id: '/relative/link/nested/deep/' path: '/nested/deep' - fullPath: '/relative/link/nested/deep' + fullPath: '/relative/link/nested/deep/' preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } diff --git a/e2e/solid-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx b/e2e/solid-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx index 1cf079d4b82..bdfcafee622 100644 --- a/e2e/solid-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx +++ b/e2e/solid-router/basic-file-based/src/routes/relative/link/path/$path/index.tsx @@ -17,7 +17,7 @@ function RouteComponent() {

navigate({ - from: '/relative/useNavigate/path', + from: '/relative/useNavigate/path/', to: './$path', params: { path: params().path === 'a' ? 'b' : 'a' }, }) diff --git a/e2e/solid-start/server-functions/src/routeTree.gen.ts b/e2e/solid-start/server-functions/src/routeTree.gen.ts index 0dcd95b34a9..329a70726e5 100644 --- a/e2e/solid-start/server-functions/src/routeTree.gen.ts +++ b/e2e/solid-start/server-functions/src/routeTree.gen.ts @@ -214,16 +214,16 @@ export interface FileRoutesByFullPath { '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute - '/abort-signal': typeof AbortSignalIndexRoute - '/cookies': typeof CookiesIndexRoute - '/factory': typeof FactoryIndexRoute - '/formdata-redirect': typeof FormdataRedirectIndexRoute - '/function-metadata': typeof FunctionMetadataIndexRoute - '/function-method': typeof FunctionMethodIndexRoute - '/middleware': typeof MiddlewareIndexRoute - '/primitives': typeof PrimitivesIndexRoute - '/redirect-test-ssr': typeof RedirectTestSsrIndexRoute - '/redirect-test': typeof RedirectTestIndexRoute + '/abort-signal/': typeof AbortSignalIndexRoute + '/cookies/': typeof CookiesIndexRoute + '/factory/': typeof FactoryIndexRoute + '/formdata-redirect/': typeof FormdataRedirectIndexRoute + '/function-metadata/': typeof FunctionMetadataIndexRoute + '/function-method/': typeof FunctionMethodIndexRoute + '/middleware/': typeof MiddlewareIndexRoute + '/primitives/': typeof PrimitivesIndexRoute + '/redirect-test-ssr/': typeof RedirectTestSsrIndexRoute + '/redirect-test/': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute } export interface FileRoutesByTo { @@ -313,16 +313,16 @@ export interface FileRouteTypes { | '/middleware/send-serverFn' | '/redirect-test-ssr/target' | '/redirect-test/target' - | '/abort-signal' - | '/cookies' - | '/factory' - | '/formdata-redirect' - | '/function-metadata' - | '/function-method' - | '/middleware' - | '/primitives' - | '/redirect-test-ssr' - | '/redirect-test' + | '/abort-signal/' + | '/cookies/' + | '/factory/' + | '/formdata-redirect/' + | '/function-metadata/' + | '/function-method/' + | '/middleware/' + | '/primitives/' + | '/redirect-test-ssr/' + | '/redirect-test/' | '/formdata-redirect/target/$name' fileRoutesByTo: FileRoutesByTo to: @@ -512,70 +512,70 @@ declare module '@tanstack/solid-router' { '/redirect-test/': { id: '/redirect-test/' path: '/redirect-test' - fullPath: '/redirect-test' + fullPath: '/redirect-test/' preLoaderRoute: typeof RedirectTestIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test-ssr/': { id: '/redirect-test-ssr/' path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } '/primitives/': { id: '/primitives/' path: '/primitives' - fullPath: '/primitives' + fullPath: '/primitives/' preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } '/middleware/': { id: '/middleware/' path: '/middleware' - fullPath: '/middleware' + fullPath: '/middleware/' preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } '/function-method/': { id: '/function-method/' path: '/function-method' - fullPath: '/function-method' + fullPath: '/function-method/' preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { id: '/function-metadata/' path: '/function-metadata' - fullPath: '/function-metadata' + fullPath: '/function-metadata/' preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/': { id: '/formdata-redirect/' path: '/formdata-redirect' - fullPath: '/formdata-redirect' + fullPath: '/formdata-redirect/' preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/factory/': { id: '/factory/' path: '/factory' - fullPath: '/factory' + fullPath: '/factory/' preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } '/cookies/': { id: '/cookies/' path: '/cookies' - fullPath: '/cookies' + fullPath: '/cookies/' preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } '/abort-signal/': { id: '/abort-signal/' path: '/abort-signal' - fullPath: '/abort-signal' + fullPath: '/abort-signal/' preLoaderRoute: typeof AbortSignalIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/solid-start/server-functions/src/routes/cookies/index.tsx b/e2e/solid-start/server-functions/src/routes/cookies/index.tsx index 5cccd18057c..9abb86d0f79 100644 --- a/e2e/solid-start/server-functions/src/routes/cookies/index.tsx +++ b/e2e/solid-start/server-functions/src/routes/cookies/index.tsx @@ -14,7 +14,7 @@ function RouteComponent() { return ( diff --git a/e2e/solid-start/website/src/routeTree.gen.ts b/e2e/solid-start/website/src/routeTree.gen.ts index 0b9ca527e54..c37e78d2f18 100644 --- a/e2e/solid-start/website/src/routeTree.gen.ts +++ b/e2e/solid-start/website/src/routeTree.gen.ts @@ -76,10 +76,11 @@ const ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute = } as any) export interface FileRoutesByFullPath { - '/$project': typeof ProjectIndexRoute '/': typeof LibraryIndexRoute - '/$project/$version/docs': typeof ProjectVersionDocsIndexRoute - '/$project/$version': typeof LibraryProjectVersionIndexRoute + '/$project': typeof LibraryProjectRouteWithChildren + '/$project/': typeof ProjectIndexRoute + '/$project/$version/docs/': typeof ProjectVersionDocsIndexRoute + '/$project/$version/': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework/': typeof ProjectVersionDocsFrameworkFrameworkIndexRoute @@ -110,10 +111,11 @@ export interface FileRoutesById { export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: - | '/$project' | '/' - | '/$project/$version/docs' - | '/$project/$version' + | '/$project' + | '/$project/' + | '/$project/$version/docs/' + | '/$project/$version/' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework/' @@ -153,7 +155,7 @@ declare module '@tanstack/solid-router' { '/_library': { id: '/_library' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } @@ -167,7 +169,7 @@ declare module '@tanstack/solid-router' { '/$project/': { id: '/$project/' path: '/$project' - fullPath: '/$project' + fullPath: '/$project/' preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -181,14 +183,14 @@ declare module '@tanstack/solid-router' { '/_library/$project/$version/': { id: '/_library/$project/$version/' path: '/$version' - fullPath: '/$project/$version' + fullPath: '/$project/$version/' preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport parentRoute: typeof LibraryProjectRoute } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' - fullPath: '/$project/$version/docs' + fullPath: '/$project/$version/docs/' preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/solid-start/website/src/routes/$project.$version.docs.index.tsx b/e2e/solid-start/website/src/routes/$project.$version.docs.index.tsx index a80b132e2ef..c5005f8ec71 100644 --- a/e2e/solid-start/website/src/routes/$project.$version.docs.index.tsx +++ b/e2e/solid-start/website/src/routes/$project.$version.docs.index.tsx @@ -3,7 +3,7 @@ import { redirect, createFileRoute } from '@tanstack/solid-router' export const Route = createFileRoute('/$project/$version/docs/')({ beforeLoad: () => { throw redirect({ - from: '/$project/$version/docs', + from: '/$project/$version/docs/', to: '/$project/$version/docs/framework/$framework/$', params: { framework: 'solid', diff --git a/e2e/solid-start/website/src/routes/_library.$project.$version.index.tsx b/e2e/solid-start/website/src/routes/_library.$project.$version.index.tsx index 191c6bf1654..61187e80cdf 100644 --- a/e2e/solid-start/website/src/routes/_library.$project.$version.index.tsx +++ b/e2e/solid-start/website/src/routes/_library.$project.$version.index.tsx @@ -14,7 +14,7 @@ function Page() {

version: {params().version}

- + Get started with our documentation.

diff --git a/e2e/vue-start/server-functions/src/routeTree.gen.ts b/e2e/vue-start/server-functions/src/routeTree.gen.ts index 25a71dff282..43a6eb4ae6a 100644 --- a/e2e/vue-start/server-functions/src/routeTree.gen.ts +++ b/e2e/vue-start/server-functions/src/routeTree.gen.ts @@ -208,15 +208,15 @@ export interface FileRoutesByFullPath { '/middleware/send-serverFn': typeof MiddlewareSendServerFnRoute '/redirect-test-ssr/target': typeof RedirectTestSsrTargetRoute '/redirect-test/target': typeof RedirectTestTargetRoute - '/cookies': typeof CookiesIndexRoute - '/factory': typeof FactoryIndexRoute - '/formdata-redirect': typeof FormdataRedirectIndexRoute - '/function-metadata': typeof FunctionMetadataIndexRoute - '/function-method': typeof FunctionMethodIndexRoute - '/middleware': typeof MiddlewareIndexRoute - '/primitives': typeof PrimitivesIndexRoute - '/redirect-test-ssr': typeof RedirectTestSsrIndexRoute - '/redirect-test': typeof RedirectTestIndexRoute + '/cookies/': typeof CookiesIndexRoute + '/factory/': typeof FactoryIndexRoute + '/formdata-redirect/': typeof FormdataRedirectIndexRoute + '/function-metadata/': typeof FunctionMetadataIndexRoute + '/function-method/': typeof FunctionMethodIndexRoute + '/middleware/': typeof MiddlewareIndexRoute + '/primitives/': typeof PrimitivesIndexRoute + '/redirect-test-ssr/': typeof RedirectTestSsrIndexRoute + '/redirect-test/': typeof RedirectTestIndexRoute '/formdata-redirect/target/$name': typeof FormdataRedirectTargetNameRoute } export interface FileRoutesByTo { @@ -304,15 +304,15 @@ export interface FileRouteTypes { | '/middleware/send-serverFn' | '/redirect-test-ssr/target' | '/redirect-test/target' - | '/cookies' - | '/factory' - | '/formdata-redirect' - | '/function-metadata' - | '/function-method' - | '/middleware' - | '/primitives' - | '/redirect-test-ssr' - | '/redirect-test' + | '/cookies/' + | '/factory/' + | '/formdata-redirect/' + | '/function-metadata/' + | '/function-method/' + | '/middleware/' + | '/primitives/' + | '/redirect-test-ssr/' + | '/redirect-test/' | '/formdata-redirect/target/$name' fileRoutesByTo: FileRoutesByTo to: @@ -506,63 +506,63 @@ declare module '@tanstack/vue-router' { '/redirect-test/': { id: '/redirect-test/' path: '/redirect-test' - fullPath: '/redirect-test' + fullPath: '/redirect-test/' preLoaderRoute: typeof RedirectTestIndexRouteImport parentRoute: typeof rootRouteImport } '/redirect-test-ssr/': { id: '/redirect-test-ssr/' path: '/redirect-test-ssr' - fullPath: '/redirect-test-ssr' + fullPath: '/redirect-test-ssr/' preLoaderRoute: typeof RedirectTestSsrIndexRouteImport parentRoute: typeof rootRouteImport } '/primitives/': { id: '/primitives/' path: '/primitives' - fullPath: '/primitives' + fullPath: '/primitives/' preLoaderRoute: typeof PrimitivesIndexRouteImport parentRoute: typeof rootRouteImport } '/middleware/': { id: '/middleware/' path: '/middleware' - fullPath: '/middleware' + fullPath: '/middleware/' preLoaderRoute: typeof MiddlewareIndexRouteImport parentRoute: typeof rootRouteImport } '/function-method/': { id: '/function-method/' path: '/function-method' - fullPath: '/function-method' + fullPath: '/function-method/' preLoaderRoute: typeof FunctionMethodIndexRouteImport parentRoute: typeof rootRouteImport } '/function-metadata/': { id: '/function-metadata/' path: '/function-metadata' - fullPath: '/function-metadata' + fullPath: '/function-metadata/' preLoaderRoute: typeof FunctionMetadataIndexRouteImport parentRoute: typeof rootRouteImport } '/formdata-redirect/': { id: '/formdata-redirect/' path: '/formdata-redirect' - fullPath: '/formdata-redirect' + fullPath: '/formdata-redirect/' preLoaderRoute: typeof FormdataRedirectIndexRouteImport parentRoute: typeof rootRouteImport } '/factory/': { id: '/factory/' path: '/factory' - fullPath: '/factory' + fullPath: '/factory/' preLoaderRoute: typeof FactoryIndexRouteImport parentRoute: typeof rootRouteImport } '/cookies/': { id: '/cookies/' path: '/cookies' - fullPath: '/cookies' + fullPath: '/cookies/' preLoaderRoute: typeof CookiesIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/vue-start/server-functions/src/routes/cookies/index.tsx b/e2e/vue-start/server-functions/src/routes/cookies/index.tsx index c47897c1096..5da8860ac6e 100644 --- a/e2e/vue-start/server-functions/src/routes/cookies/index.tsx +++ b/e2e/vue-start/server-functions/src/routes/cookies/index.tsx @@ -14,7 +14,7 @@ function RouteComponent() { return ( diff --git a/e2e/vue-start/website/src/routeTree.gen.ts b/e2e/vue-start/website/src/routeTree.gen.ts index 6b390c0fcf7..e7b58aae2a6 100644 --- a/e2e/vue-start/website/src/routeTree.gen.ts +++ b/e2e/vue-start/website/src/routeTree.gen.ts @@ -76,10 +76,11 @@ const ProjectVersionDocsFrameworkFrameworkExamplesSplatRoute = } as any) export interface FileRoutesByFullPath { - '/$project': typeof ProjectIndexRoute '/': typeof LibraryIndexRoute - '/$project/$version/docs': typeof ProjectVersionDocsIndexRoute - '/$project/$version': typeof LibraryProjectVersionIndexRoute + '/$project': typeof LibraryProjectRouteWithChildren + '/$project/': typeof ProjectIndexRoute + '/$project/$version/docs/': typeof ProjectVersionDocsIndexRoute + '/$project/$version/': typeof LibraryProjectVersionIndexRoute '/$project/$version/docs/framework/$framework': typeof ProjectVersionDocsFrameworkFrameworkRouteWithChildren '/$project/$version/docs/framework/$framework/$': typeof ProjectVersionDocsFrameworkFrameworkSplatRoute '/$project/$version/docs/framework/$framework/': typeof ProjectVersionDocsFrameworkFrameworkIndexRoute @@ -110,10 +111,11 @@ export interface FileRoutesById { export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: - | '/$project' | '/' - | '/$project/$version/docs' - | '/$project/$version' + | '/$project' + | '/$project/' + | '/$project/$version/docs/' + | '/$project/$version/' | '/$project/$version/docs/framework/$framework' | '/$project/$version/docs/framework/$framework/$' | '/$project/$version/docs/framework/$framework/' @@ -153,7 +155,7 @@ declare module '@tanstack/vue-router' { '/_library': { id: '/_library' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LibraryRouteImport parentRoute: typeof rootRouteImport } @@ -167,7 +169,7 @@ declare module '@tanstack/vue-router' { '/$project/': { id: '/$project/' path: '/$project' - fullPath: '/$project' + fullPath: '/$project/' preLoaderRoute: typeof ProjectIndexRouteImport parentRoute: typeof rootRouteImport } @@ -181,14 +183,14 @@ declare module '@tanstack/vue-router' { '/_library/$project/$version/': { id: '/_library/$project/$version/' path: '/$version' - fullPath: '/$project/$version' + fullPath: '/$project/$version/' preLoaderRoute: typeof LibraryProjectVersionIndexRouteImport parentRoute: typeof LibraryProjectRoute } '/$project/$version/docs/': { id: '/$project/$version/docs/' path: '/$project/$version/docs' - fullPath: '/$project/$version/docs' + fullPath: '/$project/$version/docs/' preLoaderRoute: typeof ProjectVersionDocsIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/e2e/vue-start/website/src/routes/$project.$version.docs.index.tsx b/e2e/vue-start/website/src/routes/$project.$version.docs.index.tsx index b033d538656..8c067c8c867 100644 --- a/e2e/vue-start/website/src/routes/$project.$version.docs.index.tsx +++ b/e2e/vue-start/website/src/routes/$project.$version.docs.index.tsx @@ -3,7 +3,7 @@ import { redirect, createFileRoute } from '@tanstack/vue-router' export const Route = createFileRoute('/$project/$version/docs/')({ beforeLoad: () => { throw redirect({ - from: '/$project/$version/docs', + from: '/$project/$version/docs/', to: '/$project/$version/docs/framework/$framework/$', params: { framework: 'vue', diff --git a/e2e/vue-start/website/src/routes/_library.$project.$version.index.tsx b/e2e/vue-start/website/src/routes/_library.$project.$version.index.tsx index 67986b09c6f..ed8e4d0bb4e 100644 --- a/e2e/vue-start/website/src/routes/_library.$project.$version.index.tsx +++ b/e2e/vue-start/website/src/routes/_library.$project.$version.index.tsx @@ -14,7 +14,7 @@ function Page() {

version: {params.value.version}

- + Get started with our documentation.

diff --git a/examples/react/search-validator-adapters/src/routeTree.gen.ts b/examples/react/search-validator-adapters/src/routeTree.gen.ts index 3312abe25e8..c3d792c4118 100644 --- a/examples/react/search-validator-adapters/src/routeTree.gen.ts +++ b/examples/react/search-validator-adapters/src/routeTree.gen.ts @@ -39,9 +39,9 @@ const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute - '/users/arktype': typeof UsersArktypeIndexRoute - '/users/valibot': typeof UsersValibotIndexRoute - '/users/zod': typeof UsersZodIndexRoute + '/users/arktype/': typeof UsersArktypeIndexRoute + '/users/valibot/': typeof UsersValibotIndexRoute + '/users/zod/': typeof UsersZodIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -58,7 +58,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/users/arktype' | '/users/valibot' | '/users/zod' + fullPaths: '/' | '/users/arktype/' | '/users/valibot/' | '/users/zod/' fileRoutesByTo: FileRoutesByTo to: '/' | '/users/arktype' | '/users/valibot' | '/users/zod' id: '__root__' | '/' | '/users/arktype/' | '/users/valibot/' | '/users/zod/' @@ -83,21 +83,21 @@ declare module '@tanstack/react-router' { '/users/zod/': { id: '/users/zod/' path: '/users/zod' - fullPath: '/users/zod' + fullPath: '/users/zod/' preLoaderRoute: typeof UsersZodIndexRouteImport parentRoute: typeof rootRouteImport } '/users/valibot/': { id: '/users/valibot/' path: '/users/valibot' - fullPath: '/users/valibot' + fullPath: '/users/valibot/' preLoaderRoute: typeof UsersValibotIndexRouteImport parentRoute: typeof rootRouteImport } '/users/arktype/': { id: '/users/arktype/' path: '/users/arktype' - fullPath: '/users/arktype' + fullPath: '/users/arktype/' preLoaderRoute: typeof UsersArktypeIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/examples/react/search-validator-adapters/tests/arktype.test.tsx b/examples/react/search-validator-adapters/tests/arktype.test.tsx index da700851149..1c3a1baf764 100644 --- a/examples/react/search-validator-adapters/tests/arktype.test.tsx +++ b/examples/react/search-validator-adapters/tests/arktype.test.tsx @@ -26,7 +26,7 @@ test('can navigate to the route', async () => { return (
{search}
- + Update
diff --git a/examples/react/search-validator-adapters/tests/valibot.test.tsx b/examples/react/search-validator-adapters/tests/valibot.test.tsx index 30dc6da7518..3680d2302c8 100644 --- a/examples/react/search-validator-adapters/tests/valibot.test.tsx +++ b/examples/react/search-validator-adapters/tests/valibot.test.tsx @@ -25,7 +25,7 @@ test('can navigate to the route', async () => { return (
{search}
- + Update
diff --git a/examples/solid/search-validator-adapters/src/routeTree.gen.ts b/examples/solid/search-validator-adapters/src/routeTree.gen.ts index 3226feeab26..d7aab613247 100644 --- a/examples/solid/search-validator-adapters/src/routeTree.gen.ts +++ b/examples/solid/search-validator-adapters/src/routeTree.gen.ts @@ -39,9 +39,9 @@ const UsersArktypeIndexRoute = UsersArktypeIndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute - '/users/arktype': typeof UsersArktypeIndexRoute - '/users/valibot': typeof UsersValibotIndexRoute - '/users/zod': typeof UsersZodIndexRoute + '/users/arktype/': typeof UsersArktypeIndexRoute + '/users/valibot/': typeof UsersValibotIndexRoute + '/users/zod/': typeof UsersZodIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -58,7 +58,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/users/arktype' | '/users/valibot' | '/users/zod' + fullPaths: '/' | '/users/arktype/' | '/users/valibot/' | '/users/zod/' fileRoutesByTo: FileRoutesByTo to: '/' | '/users/arktype' | '/users/valibot' | '/users/zod' id: '__root__' | '/' | '/users/arktype/' | '/users/valibot/' | '/users/zod/' @@ -83,21 +83,21 @@ declare module '@tanstack/solid-router' { '/users/zod/': { id: '/users/zod/' path: '/users/zod' - fullPath: '/users/zod' + fullPath: '/users/zod/' preLoaderRoute: typeof UsersZodIndexRouteImport parentRoute: typeof rootRouteImport } '/users/valibot/': { id: '/users/valibot/' path: '/users/valibot' - fullPath: '/users/valibot' + fullPath: '/users/valibot/' preLoaderRoute: typeof UsersValibotIndexRouteImport parentRoute: typeof rootRouteImport } '/users/arktype/': { id: '/users/arktype/' path: '/users/arktype' - fullPath: '/users/arktype' + fullPath: '/users/arktype/' preLoaderRoute: typeof UsersArktypeIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/examples/solid/search-validator-adapters/tests/arktype.test.tsx b/examples/solid/search-validator-adapters/tests/arktype.test.tsx index 7e130132bd1..4ec555fc7bf 100644 --- a/examples/solid/search-validator-adapters/tests/arktype.test.tsx +++ b/examples/solid/search-validator-adapters/tests/arktype.test.tsx @@ -26,7 +26,7 @@ test('can navigate to the route', async () => { return (
{search().search}
- + Update
diff --git a/examples/solid/search-validator-adapters/tests/valibot.test.tsx b/examples/solid/search-validator-adapters/tests/valibot.test.tsx index 882a73d574f..cd4545397ce 100644 --- a/examples/solid/search-validator-adapters/tests/valibot.test.tsx +++ b/examples/solid/search-validator-adapters/tests/valibot.test.tsx @@ -25,7 +25,7 @@ test('can navigate to the route', async () => { return (
{search().search}
- + Update
diff --git a/packages/react-router/tests/routeApi.test-d.tsx b/packages/react-router/tests/routeApi.test-d.tsx index a51a50b10ff..587c189fb03 100644 --- a/packages/react-router/tests/routeApi.test-d.tsx +++ b/packages/react-router/tests/routeApi.test-d.tsx @@ -106,3 +106,35 @@ describe('createRoute', () => { }) }) }) + +describe('fullPath type correctness', () => { + // Create a layout route (pathless route) + const layoutRoute = createRoute({ + getParentRoute: () => rootRoute, + id: '_layout', + }) + + const layoutChildRoute = createRoute({ + getParentRoute: () => layoutRoute, + path: 'dashboard', + }) + + const routeTreeWithLayout = rootRoute.addChildren([ + layoutRoute.addChildren([layoutChildRoute]), + indexRoute, + ]) + + test('index route fullPath should have trailing slash to match runtime', () => { + // At runtime, Route.fullPath returns '/invoices/' for an index route + // The type should match this behavior + const fullPath = invoicesIndexRoute.fullPath + expectTypeOf(fullPath).toEqualTypeOf<'/invoices/'>() + }) + + test('layout route fullPath should be "/" not empty string', () => { + // At runtime, a pathless layout route has fullPath of '/' + // The type should be '/' not '' + const fullPath = layoutRoute.fullPath + expectTypeOf(fullPath).toEqualTypeOf<'/'>() + }) +}) diff --git a/packages/router-core/src/route.ts b/packages/router-core/src/route.ts index c06ee9fcc5e..faadfd2c79e 100644 --- a/packages/router-core/src/route.ts +++ b/packages/router-core/src/route.ts @@ -1,5 +1,5 @@ import invariant from 'tiny-invariant' -import { joinPaths, trimPathLeft } from './path' +import { joinPaths, trimPathLeft, trimPathRight } from './path' import { notFound } from './not-found' import { redirect } from './redirect' import { rootRouteId } from './root' @@ -1755,7 +1755,7 @@ export class BaseRoute< this._path = path as TPath this._id = id as TId this._fullPath = fullPath as TFullPath - this._to = fullPath as TrimPathRight + this._to = trimPathRight(fullPath) as TrimPathRight } addChildren: RouteAddChildrenFn< diff --git a/packages/router-generator/src/utils.ts b/packages/router-generator/src/utils.ts index 366a4e84f6f..094a50924a8 100644 --- a/packages/router-generator/src/utils.ts +++ b/packages/router-generator/src/utils.ts @@ -700,9 +700,10 @@ export function isRouteNodeValidForAugmentation( * Infers the path for use by TS */ export const inferPath = (routeNode: RouteNode): string => { - return routeNode.cleanedPath === '/' - ? routeNode.cleanedPath - : (routeNode.cleanedPath?.replace(/\/$/, '') ?? '') + if (routeNode.cleanedPath === '/') { + return routeNode.cleanedPath ?? '' + } + return routeNode.cleanedPath?.replace(/\/$/, '') ?? '' } /** @@ -719,7 +720,25 @@ export const inferFullPath = (routeNode: RouteNode): string => { ), ) - return routeNode.cleanedPath === '/' ? fullPath : fullPath.replace(/\/$/, '') + if (fullPath === '') { + return '/' + } + + // Preserve trailing slash for index routes (routePath ends with '/') + // This ensures types match runtime behavior + const isIndexRoute = routeNode.routePath?.endsWith('/') + if (isIndexRoute) { + return fullPath + } + + return fullPath.replace(/\/$/, '') +} + +const shouldPreferIndexRoute = ( + current: RouteNode, + existing: RouteNode, +): boolean => { + return existing.cleanedPath === '/' && current.cleanedPath !== '/' } /** @@ -728,9 +747,22 @@ export const inferFullPath = (routeNode: RouteNode): string => { export const createRouteNodesByFullPath = ( routeNodes: Array, ): Map => { - return new Map( - routeNodes.map((routeNode) => [inferFullPath(routeNode), routeNode]), - ) + const map = new Map() + + for (const routeNode of routeNodes) { + const fullPath = inferFullPath(routeNode) + + if (fullPath === '/' && map.has('/')) { + const existing = map.get('/')! + if (shouldPreferIndexRoute(routeNode, existing)) { + continue + } + } + + map.set(fullPath, routeNode) + } + + return map } /** @@ -739,12 +771,22 @@ export const createRouteNodesByFullPath = ( export const createRouteNodesByTo = ( routeNodes: Array, ): Map => { - return new Map( - dedupeBranchesAndIndexRoutes(routeNodes).map((routeNode) => [ - inferTo(routeNode), - routeNode, - ]), - ) + const map = new Map() + + for (const routeNode of dedupeBranchesAndIndexRoutes(routeNodes)) { + const to = inferTo(routeNode) + + if (to === '/' && map.has('/')) { + const existing = map.get('/')! + if (shouldPreferIndexRoute(routeNode, existing)) { + continue + } + } + + map.set(to, routeNode) + } + + return map } /** @@ -803,6 +845,15 @@ export function checkRouteFullPathUniqueness( _routes: Array, config: Config, ) { + const emptyPathRoutes = _routes.filter((d) => d.routePath === '') + if (emptyPathRoutes.length) { + const errorMessage = `Invalid route path "" was found. Root routes must be defined via __root.tsx (createRootRoute), not createFileRoute('') or a route file that resolves to an empty path. +Conflicting files: \n ${emptyPathRoutes + .map((d) => path.resolve(config.routesDirectory, d.filePath)) + .join('\n ')}\n` + throw new Error(errorMessage) + } + const routes = _routes.map((d) => { const inferredFullPath = inferFullPath(d) return { ...d, inferredFullPath } diff --git a/packages/router-generator/tests/generator.test.ts b/packages/router-generator/tests/generator.test.ts index 1fd787cda1e..485767e51aa 100644 --- a/packages/router-generator/tests/generator.test.ts +++ b/packages/router-generator/tests/generator.test.ts @@ -252,7 +252,7 @@ function shouldThrow(folderName: string) { return `Conflicting configuration paths were found for the following routes: "/", "/".` } if (folderName === 'virtual-physical-empty-path-conflict-root') { - return `Conflicting configuration paths were found for the following routes: "/__root", "/__root".` + return 'Invalid route path "" was found.' } if (folderName === 'virtual-physical-empty-path-conflict-virtual') { return `Conflicting configuration paths were found for the following routes: "/about", "/about".` diff --git a/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts b/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts index c82afab07df..f49d059f202 100644 --- a/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/custom-tokens/routeTree.snapshot.ts @@ -67,7 +67,7 @@ export interface FileRoutesByFullPath { '/blog/': typeof Blog1nd3xRoute '/posts/': typeof Posts1nd3xRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute - '/posts/$postId': typeof PostsPostId1nd3xRoute + '/posts/$postId/': typeof PostsPostId1nd3xRoute } export interface FileRoutesByTo { '/': typeof R1nd3xRoute @@ -98,7 +98,7 @@ export interface FileRouteTypes { | '/blog/' | '/posts/' | '/posts/$postId/deep' - | '/posts/$postId' + | '/posts/$postId/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -172,7 +172,7 @@ declare module '@tanstack/react-router' { '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' - fullPath: '/posts/$postId' + fullPath: '/posts/$postId/' preLoaderRoute: typeof PostsPostId1nd3xRouteImport parentRoute: typeof PostsR0ut3Route } diff --git a/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts b/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts index abfa5604c96..b1bf6587fcb 100644 --- a/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/flat/routeTree.snapshot.ts @@ -100,8 +100,8 @@ export interface FileRoutesByFullPath { '/blog/$blogId/$slug': typeof BlogBlogIdSlugRouteRoute '/blog/$blogId/edit': typeof BlogBlogIdEditRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute - '/blog/$slug': typeof BlogSlugIndexRoute - '/posts/$postId': typeof PostsPostIdIndexRoute + '/blog/$slug/': typeof BlogSlugIndexRoute + '/posts/$postId/': typeof PostsPostIdIndexRoute '/blog/$blogId/$slug/bar': typeof BlogBlogIdSlugBarRoute } export interface FileRoutesByTo { @@ -146,8 +146,8 @@ export interface FileRouteTypes { | '/blog/$blogId/$slug' | '/blog/$blogId/edit' | '/posts/$postId/deep' - | '/blog/$slug' - | '/posts/$postId' + | '/blog/$slug/' + | '/posts/$postId/' | '/blog/$blogId/$slug/bar' fileRoutesByTo: FileRoutesByTo to: @@ -242,14 +242,14 @@ declare module '@tanstack/react-router' { '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' - fullPath: '/posts/$postId' + fullPath: '/posts/$postId/' preLoaderRoute: typeof PostsPostIdIndexRouteImport parentRoute: typeof PostsRouteRoute } '/blog/$slug/': { id: '/blog/$slug/' path: '/$slug' - fullPath: '/blog/$slug' + fullPath: '/blog/$slug/' preLoaderRoute: typeof BlogSlugIndexRouteImport parentRoute: typeof BlogRouteRoute } diff --git a/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts index 6501b213ecc..c38345a64bf 100644 --- a/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested-layouts/routeTree.snapshot.ts @@ -278,14 +278,14 @@ declare module '@tanstack/react-router' { '/_layout-a2': { id: '/_layout-a2' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutA2RouteImport parentRoute: typeof rootRouteImport } '/_layout-a1': { id: '/_layout-a1' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof LayoutA1RouteImport parentRoute: typeof rootRouteImport } diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.snapshot.ts index b7094626b90..ce5d831534c 100644 --- a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routeTree.snapshot.ts @@ -11,6 +11,7 @@ import type { CreateFileRoute, FileRoutesByPath } from '@tanstack/react-router' import { Route as rootRouteImport } from './routes/__root' +import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts/route' import { Route as BlogRouteRouteImport } from './routes/blog/route' import { Route as IndexRouteImport } from './routes/index' @@ -18,6 +19,7 @@ import { Route as PostsIndexRouteImport } from './routes/posts/index' import { Route as BlogIndexRouteImport } from './routes/blog/index' import { Route as BlogStatsRouteImport } from './routes/blog_/stats' import { Route as BlogSlugRouteImport } from './routes/blog/$slug' +import { Route as PathlessLayoutSettingsRouteImport } from './routes/_pathlessLayout/settings' import { Route as BlogBlogIdRouteRouteImport } from './routes/blog_/$blogId/route' import { Route as PostsPostIdIndexRouteImport } from './routes/posts/$postId/index' import { Route as PostsPostIdDeepRouteImport } from './routes/posts/$postId/deep' @@ -25,6 +27,10 @@ import { Route as BlogBlogIdEditRouteImport } from './routes/blog_/$blogId_/edit import { Route as BlogBlogIdSlugRouteRouteImport } from './routes/blog_/$blogId/$slug/route' import { Route as BlogBlogIdSlugBarRouteImport } from './routes/blog_/$blogId/$slug_/bar' +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', @@ -60,6 +66,11 @@ const BlogSlugRoute = BlogSlugRouteImport.update({ path: '/$slug', getParentRoute: () => BlogRouteRoute, } as any) +const PathlessLayoutSettingsRoute = PathlessLayoutSettingsRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => PathlessLayoutRoute, +} as any) const BlogBlogIdRouteRoute = BlogBlogIdRouteRouteImport.update({ id: '/blog_/$blogId', path: '/blog/$blogId', @@ -96,6 +107,7 @@ export interface FileRoutesByFullPath { '/blog': typeof BlogRouteRouteWithChildren '/posts': typeof PostsRouteRouteWithChildren '/blog/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog/stats': typeof BlogStatsRoute '/blog/': typeof BlogIndexRoute @@ -103,12 +115,13 @@ export interface FileRoutesByFullPath { '/blog/$blogId/$slug': typeof BlogBlogIdSlugRouteRoute '/blog/$blogId/edit': typeof BlogBlogIdEditRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute - '/posts/$postId': typeof PostsPostIdIndexRoute + '/posts/$postId/': typeof PostsPostIdIndexRoute '/blog/$blogId/$slug/bar': typeof BlogBlogIdSlugBarRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/blog/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog/stats': typeof BlogStatsRoute '/blog': typeof BlogIndexRoute @@ -124,7 +137,9 @@ export interface FileRoutesById { '/': typeof IndexRoute '/blog': typeof BlogRouteRouteWithChildren '/posts': typeof PostsRouteRouteWithChildren + '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren '/blog_/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/_pathlessLayout/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog_/stats': typeof BlogStatsRoute '/blog/': typeof BlogIndexRoute @@ -142,6 +157,7 @@ export interface FileRouteTypes { | '/blog' | '/posts' | '/blog/$blogId' + | '/settings' | '/blog/$slug' | '/blog/stats' | '/blog/' @@ -149,12 +165,13 @@ export interface FileRouteTypes { | '/blog/$blogId/$slug' | '/blog/$blogId/edit' | '/posts/$postId/deep' - | '/posts/$postId' + | '/posts/$postId/' | '/blog/$blogId/$slug/bar' fileRoutesByTo: FileRoutesByTo to: | '/' | '/blog/$blogId' + | '/settings' | '/blog/$slug' | '/blog/stats' | '/blog' @@ -169,7 +186,9 @@ export interface FileRouteTypes { | '/' | '/blog' | '/posts' + | '/_pathlessLayout' | '/blog_/$blogId' + | '/_pathlessLayout/settings' | '/blog/$slug' | '/blog_/stats' | '/blog/' @@ -185,6 +204,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlogRouteRoute: typeof BlogRouteRouteWithChildren PostsRouteRoute: typeof PostsRouteRouteWithChildren + PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren BlogBlogIdRouteRoute: typeof BlogBlogIdRouteRouteWithChildren BlogStatsRoute: typeof BlogStatsRoute BlogBlogIdEditRoute: typeof BlogBlogIdEditRoute @@ -192,6 +212,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -241,6 +268,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogSlugRouteImport parentRoute: typeof BlogRouteRoute } + '/_pathlessLayout/settings': { + id: '/_pathlessLayout/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof PathlessLayoutSettingsRouteImport + parentRoute: typeof PathlessLayoutRoute + } '/blog_/$blogId': { id: '/blog_/$blogId' path: '/blog/$blogId' @@ -251,7 +285,7 @@ declare module '@tanstack/react-router' { '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' - fullPath: '/posts/$postId' + fullPath: '/posts/$postId/' preLoaderRoute: typeof PostsPostIdIndexRouteImport parentRoute: typeof PostsRouteRoute } @@ -313,6 +347,15 @@ declare module './routes/posts/route' { FileRoutesByPath['/posts']['fullPath'] > } +declare module './routes/_pathlessLayout' { + const createFileRoute: CreateFileRoute< + '/_pathlessLayout', + FileRoutesByPath['/_pathlessLayout']['parentRoute'], + FileRoutesByPath['/_pathlessLayout']['id'], + FileRoutesByPath['/_pathlessLayout']['path'], + FileRoutesByPath['/_pathlessLayout']['fullPath'] + > +} declare module './routes/blog_/$blogId/route' { const createFileRoute: CreateFileRoute< '/blog_/$blogId', @@ -322,6 +365,15 @@ declare module './routes/blog_/$blogId/route' { FileRoutesByPath['/blog_/$blogId']['fullPath'] > } +declare module './routes/_pathlessLayout/settings' { + const createFileRoute: CreateFileRoute< + '/_pathlessLayout/settings', + FileRoutesByPath['/_pathlessLayout/settings']['parentRoute'], + FileRoutesByPath['/_pathlessLayout/settings']['id'], + FileRoutesByPath['/_pathlessLayout/settings']['path'], + FileRoutesByPath['/_pathlessLayout/settings']['fullPath'] + > +} declare module './routes/blog/$slug' { const createFileRoute: CreateFileRoute< '/blog/$slug', @@ -434,6 +486,18 @@ const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) +interface PathlessLayoutRouteChildren { + PathlessLayoutSettingsRoute: typeof PathlessLayoutSettingsRoute +} + +const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { + PathlessLayoutSettingsRoute: PathlessLayoutSettingsRoute, +} + +const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( + PathlessLayoutRouteChildren, +) + interface BlogBlogIdRouteRouteChildren { BlogBlogIdSlugRouteRoute: typeof BlogBlogIdSlugRouteRoute BlogBlogIdSlugBarRoute: typeof BlogBlogIdSlugBarRoute @@ -452,6 +516,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlogRouteRoute: BlogRouteRouteWithChildren, PostsRouteRoute: PostsRouteRouteWithChildren, + PathlessLayoutRoute: PathlessLayoutRouteWithChildren, BlogBlogIdRouteRoute: BlogBlogIdRouteRouteWithChildren, BlogStatsRoute: BlogStatsRoute, BlogBlogIdEditRoute: BlogBlogIdEditRoute, diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout.tsx b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout.tsx new file mode 100644 index 00000000000..ae38370b806 --- /dev/null +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout.tsx @@ -0,0 +1,4 @@ +import { Outlet } from '@tanstack/react-router' +export const Route = createFileRoute({ + component: () => , +}) diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout/settings.tsx b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout/settings.tsx new file mode 100644 index 00000000000..b2a1f1a950d --- /dev/null +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/routes/_pathlessLayout/settings.tsx @@ -0,0 +1,3 @@ +export const Route = createFileRoute({ + component: () =>
Settings
, +}) diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.test-d.ts b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.test-d.ts index adfb3ae535e..47dcc0f5b1e 100644 --- a/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.test-d.ts +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-false/tests.test-d.ts @@ -10,9 +10,10 @@ import { useRouteContext, useSearch, } from '@tanstack/react-router' +import type { FileRoutesByPath, MakeRouteMatch } from '@tanstack/react-router' import { expectTypeOf, test } from 'vitest' import { routeTree } from './routeTree.gen' -import type { MakeRouteMatch } from '@tanstack/react-router' +import type { FileRouteTypes } from './routeTree.gen' const defaultRouter = createRouter({ routeTree, @@ -36,6 +37,29 @@ const preserveTrailingSlashRouter = createRouter({ }) test('when navigating to the root', () => { + // Issue #4892: Pathless layout routes should have fullPath: '/' not '' + expectTypeOf().toEqualTypeOf<'/'>() + expectTypeOf< + FileRoutesByPath['/posts/']['fullPath'] + >().toEqualTypeOf<'/posts/'>() + // Issue #6403: Index routes should have trailing slash in fullPath to match runtime + expectTypeOf< + FileRoutesByPath['/posts/$postId/']['fullPath'] + >().toEqualTypeOf<'/posts/$postId/'>() + expectTypeOf< + FileRoutesByPath['/blog/']['fullPath'] + >().toEqualTypeOf<'/blog/'>() + // Verify empty string is not in fullPaths union + expectTypeOf<''>().not.toMatchTypeOf() + // Verify pathless layout's fullPath is '/' (not '') + expectTypeOf< + FileRoutesByPath['/_pathlessLayout']['fullPath'] + >().toEqualTypeOf<'/'>() + // Child of pathless layout should have correct fullPath + expectTypeOf< + FileRoutesByPath['/_pathlessLayout/settings']['fullPath'] + >().toEqualTypeOf<'/settings'>() + expectTypeOf(Link) .parameter(0) .toHaveProperty('to') @@ -51,6 +75,7 @@ test('when navigating to the root', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | undefined >() @@ -69,6 +94,7 @@ test('when navigating to the root', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' | undefined >() @@ -87,6 +113,7 @@ test('when navigating to the root', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | undefined >() @@ -115,6 +142,8 @@ test('when navigating to the root', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' + | '/settings' | undefined >() @@ -134,7 +163,8 @@ test('when navigating to the root', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' - | '/posts/$postId' + | '/posts/$postId/' + | '/settings' | undefined >() @@ -181,6 +211,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' >() expectTypeOf( @@ -202,6 +233,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' >() expectTypeOf(Link) @@ -219,6 +251,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | '.' | '..' >() @@ -254,6 +287,8 @@ test('when navigating a index route with search and params', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings' + | '/settings/' >() expectTypeOf(Link) @@ -261,7 +296,7 @@ test('when navigating a index route with search and params', () => { .toHaveProperty('from') .toEqualTypeOf< | '/' - | '/posts/$postId' + | '/posts/$postId/' | '/blog' | '/posts' | '/blog/$blogId' @@ -273,6 +308,7 @@ test('when navigating a index route with search and params', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' + | '/settings' | undefined >() @@ -366,12 +402,12 @@ test('when navigating a index route with search and params', () => { }) test('when navigating from a index route with search and params', () => { - expectTypeOf(Link) + expectTypeOf(Link) .parameter(0) .toHaveProperty('from') .toEqualTypeOf< | '/' - | '/posts/$postId' + | '/posts/$postId/' | '/blog' | '/posts' | '/blog/$blogId' @@ -383,10 +419,11 @@ test('when navigating from a index route with search and params', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' + | '/settings' | undefined >() - expectTypeOf(Link) + expectTypeOf(Link) .parameter(0) .toHaveProperty('search') .parameter(0) @@ -413,6 +450,7 @@ test('when using useNavigate', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' >() }) @@ -432,6 +470,7 @@ test('when using redirect', () => { | '/blog/$slug' | '/blog/stats' | '/posts/$postId/deep' + | '/settings' | undefined >() }) @@ -444,7 +483,6 @@ test('when using useSearch from a route with no search', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -452,9 +490,12 @@ test('when using useSearch from a route with no search', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf(useSearch).returns.toEqualTypeOf<{}>() @@ -468,7 +509,6 @@ test('when using useSearch from a route with search', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -476,9 +516,12 @@ test('when using useSearch from a route with search', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -494,17 +537,19 @@ test('when using useLoaderData from a route with loaderData', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' + | '/blog/$slug' | '/blog_/$blogId' | '/blog_/$blogId_/edit' | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' - | '/blog/$slug' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -520,7 +565,6 @@ test('when using useLoaderDeps from a route with loaderDeps', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -528,9 +572,12 @@ test('when using useLoaderDeps from a route with loaderDeps', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -546,7 +593,6 @@ test('when using useMatch from a route', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -554,9 +600,12 @@ test('when using useMatch from a route', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -574,7 +623,6 @@ test('when using useParams from a route', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -582,9 +630,12 @@ test('when using useParams from a route', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -600,7 +651,6 @@ test('when using useRouteContext from a route', () => { | '__root__' | '/' | '/blog' - | '/blog/' | '/posts' | '/blog/$slug' | '/blog_/$blogId' @@ -608,9 +658,12 @@ test('when using useRouteContext from a route', () => { | '/blog_/$blogId/$slug' | '/blog_/$blogId/$slug_/bar' | '/blog_/stats' + | '/blog/' | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.snapshot.ts b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.snapshot.ts index 3cb8036f7c3..9a87dd69538 100644 --- a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routeTree.snapshot.ts @@ -9,6 +9,7 @@ // 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 PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as PostsRouteRouteImport } from './routes/posts/route' import { Route as BlogRouteRouteImport } from './routes/blog/route' import { Route as IndexRouteImport } from './routes/index' @@ -16,6 +17,7 @@ import { Route as PostsIndexRouteImport } from './routes/posts/index' import { Route as BlogIndexRouteImport } from './routes/blog/index' import { Route as BlogStatsRouteImport } from './routes/blog_/stats' import { Route as BlogSlugRouteImport } from './routes/blog/$slug' +import { Route as PathlessLayoutSettingsRouteImport } from './routes/_pathlessLayout/settings' import { Route as BlogBlogIdRouteRouteImport } from './routes/blog_/$blogId/route' import { Route as PostsPostIdIndexRouteImport } from './routes/posts/$postId/index' import { Route as PostsPostIdDeepRouteImport } from './routes/posts/$postId/deep' @@ -23,6 +25,10 @@ import { Route as BlogBlogIdEditRouteImport } from './routes/blog_/$blogId_/edit import { Route as BlogBlogIdSlugRouteRouteImport } from './routes/blog_/$blogId/$slug/route' import { Route as BlogBlogIdSlugBarRouteImport } from './routes/blog_/$blogId/$slug_/bar' +const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ + id: '/_pathlessLayout', + getParentRoute: () => rootRouteImport, +} as any) const PostsRouteRoute = PostsRouteRouteImport.update({ id: '/posts', path: '/posts', @@ -58,6 +64,11 @@ const BlogSlugRoute = BlogSlugRouteImport.update({ path: '/$slug', getParentRoute: () => BlogRouteRoute, } as any) +const PathlessLayoutSettingsRoute = PathlessLayoutSettingsRouteImport.update({ + id: '/settings', + path: '/settings', + getParentRoute: () => PathlessLayoutRoute, +} as any) const BlogBlogIdRouteRoute = BlogBlogIdRouteRouteImport.update({ id: '/blog_/$blogId', path: '/blog/$blogId', @@ -94,6 +105,7 @@ export interface FileRoutesByFullPath { '/blog': typeof BlogRouteRouteWithChildren '/posts': typeof PostsRouteRouteWithChildren '/blog/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog/stats': typeof BlogStatsRoute '/blog/': typeof BlogIndexRoute @@ -101,12 +113,13 @@ export interface FileRoutesByFullPath { '/blog/$blogId/$slug': typeof BlogBlogIdSlugRouteRoute '/blog/$blogId/edit': typeof BlogBlogIdEditRoute '/posts/$postId/deep': typeof PostsPostIdDeepRoute - '/posts/$postId': typeof PostsPostIdIndexRoute + '/posts/$postId/': typeof PostsPostIdIndexRoute '/blog/$blogId/$slug/bar': typeof BlogBlogIdSlugBarRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/blog/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog/stats': typeof BlogStatsRoute '/blog': typeof BlogIndexRoute @@ -122,7 +135,9 @@ export interface FileRoutesById { '/': typeof IndexRoute '/blog': typeof BlogRouteRouteWithChildren '/posts': typeof PostsRouteRouteWithChildren + '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren '/blog_/$blogId': typeof BlogBlogIdRouteRouteWithChildren + '/_pathlessLayout/settings': typeof PathlessLayoutSettingsRoute '/blog/$slug': typeof BlogSlugRoute '/blog_/stats': typeof BlogStatsRoute '/blog/': typeof BlogIndexRoute @@ -140,6 +155,7 @@ export interface FileRouteTypes { | '/blog' | '/posts' | '/blog/$blogId' + | '/settings' | '/blog/$slug' | '/blog/stats' | '/blog/' @@ -147,12 +163,13 @@ export interface FileRouteTypes { | '/blog/$blogId/$slug' | '/blog/$blogId/edit' | '/posts/$postId/deep' - | '/posts/$postId' + | '/posts/$postId/' | '/blog/$blogId/$slug/bar' fileRoutesByTo: FileRoutesByTo to: | '/' | '/blog/$blogId' + | '/settings' | '/blog/$slug' | '/blog/stats' | '/blog' @@ -167,7 +184,9 @@ export interface FileRouteTypes { | '/' | '/blog' | '/posts' + | '/_pathlessLayout' | '/blog_/$blogId' + | '/_pathlessLayout/settings' | '/blog/$slug' | '/blog_/stats' | '/blog/' @@ -183,6 +202,7 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute BlogRouteRoute: typeof BlogRouteRouteWithChildren PostsRouteRoute: typeof PostsRouteRouteWithChildren + PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren BlogBlogIdRouteRoute: typeof BlogBlogIdRouteRouteWithChildren BlogStatsRoute: typeof BlogStatsRoute BlogBlogIdEditRoute: typeof BlogBlogIdEditRoute @@ -190,6 +210,13 @@ export interface RootRouteChildren { declare module '@tanstack/react-router' { interface FileRoutesByPath { + '/_pathlessLayout': { + id: '/_pathlessLayout' + path: '' + fullPath: '/' + preLoaderRoute: typeof PathlessLayoutRouteImport + parentRoute: typeof rootRouteImport + } '/posts': { id: '/posts' path: '/posts' @@ -239,6 +266,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof BlogSlugRouteImport parentRoute: typeof BlogRouteRoute } + '/_pathlessLayout/settings': { + id: '/_pathlessLayout/settings' + path: '/settings' + fullPath: '/settings' + preLoaderRoute: typeof PathlessLayoutSettingsRouteImport + parentRoute: typeof PathlessLayoutRoute + } '/blog_/$blogId': { id: '/blog_/$blogId' path: '/blog/$blogId' @@ -249,7 +283,7 @@ declare module '@tanstack/react-router' { '/posts/$postId/': { id: '/posts/$postId/' path: '/$postId' - fullPath: '/posts/$postId' + fullPath: '/posts/$postId/' preLoaderRoute: typeof PostsPostIdIndexRouteImport parentRoute: typeof PostsRouteRoute } @@ -314,6 +348,18 @@ const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren( PostsRouteRouteChildren, ) +interface PathlessLayoutRouteChildren { + PathlessLayoutSettingsRoute: typeof PathlessLayoutSettingsRoute +} + +const PathlessLayoutRouteChildren: PathlessLayoutRouteChildren = { + PathlessLayoutSettingsRoute: PathlessLayoutSettingsRoute, +} + +const PathlessLayoutRouteWithChildren = PathlessLayoutRoute._addFileChildren( + PathlessLayoutRouteChildren, +) + interface BlogBlogIdRouteRouteChildren { BlogBlogIdSlugRouteRoute: typeof BlogBlogIdSlugRouteRoute BlogBlogIdSlugBarRoute: typeof BlogBlogIdSlugBarRoute @@ -332,6 +378,7 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, BlogRouteRoute: BlogRouteRouteWithChildren, PostsRouteRoute: PostsRouteRouteWithChildren, + PathlessLayoutRoute: PathlessLayoutRouteWithChildren, BlogBlogIdRouteRoute: BlogBlogIdRouteRouteWithChildren, BlogStatsRoute: BlogStatsRoute, BlogBlogIdEditRoute: BlogBlogIdEditRoute, diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout.tsx b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout.tsx new file mode 100644 index 00000000000..714f9def3e6 --- /dev/null +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout.tsx @@ -0,0 +1,4 @@ +import { createFileRoute, Outlet } from '@tanstack/react-router' +export const Route = createFileRoute('/_pathlessLayout')({ + component: () => , +}) diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout/settings.tsx b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout/settings.tsx new file mode 100644 index 00000000000..b687c8c8c9b --- /dev/null +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/routes/_pathlessLayout/settings.tsx @@ -0,0 +1,4 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/_pathlessLayout/settings')({ + component: () =>
Settings
, +}) diff --git a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.test-d.ts b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.test-d.ts index 2e991843d59..47dcc0f5b1e 100644 --- a/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.test-d.ts +++ b/packages/router-generator/tests/generator/nested-verboseFileRoutes-true/tests.test-d.ts @@ -10,9 +10,10 @@ import { useRouteContext, useSearch, } from '@tanstack/react-router' +import type { FileRoutesByPath, MakeRouteMatch } from '@tanstack/react-router' import { expectTypeOf, test } from 'vitest' import { routeTree } from './routeTree.gen' -import type { MakeRouteMatch } from '@tanstack/react-router' +import type { FileRouteTypes } from './routeTree.gen' const defaultRouter = createRouter({ routeTree, @@ -36,6 +37,29 @@ const preserveTrailingSlashRouter = createRouter({ }) test('when navigating to the root', () => { + // Issue #4892: Pathless layout routes should have fullPath: '/' not '' + expectTypeOf().toEqualTypeOf<'/'>() + expectTypeOf< + FileRoutesByPath['/posts/']['fullPath'] + >().toEqualTypeOf<'/posts/'>() + // Issue #6403: Index routes should have trailing slash in fullPath to match runtime + expectTypeOf< + FileRoutesByPath['/posts/$postId/']['fullPath'] + >().toEqualTypeOf<'/posts/$postId/'>() + expectTypeOf< + FileRoutesByPath['/blog/']['fullPath'] + >().toEqualTypeOf<'/blog/'>() + // Verify empty string is not in fullPaths union + expectTypeOf<''>().not.toMatchTypeOf() + // Verify pathless layout's fullPath is '/' (not '') + expectTypeOf< + FileRoutesByPath['/_pathlessLayout']['fullPath'] + >().toEqualTypeOf<'/'>() + // Child of pathless layout should have correct fullPath + expectTypeOf< + FileRoutesByPath['/_pathlessLayout/settings']['fullPath'] + >().toEqualTypeOf<'/settings'>() + expectTypeOf(Link) .parameter(0) .toHaveProperty('to') @@ -51,6 +75,7 @@ test('when navigating to the root', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | undefined >() @@ -69,6 +94,7 @@ test('when navigating to the root', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' | undefined >() @@ -87,6 +113,7 @@ test('when navigating to the root', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | undefined >() @@ -115,6 +142,8 @@ test('when navigating to the root', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' + | '/settings' | undefined >() @@ -134,7 +163,8 @@ test('when navigating to the root', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' - | '/posts/$postId' + | '/posts/$postId/' + | '/settings' | undefined >() @@ -181,6 +211,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' >() expectTypeOf( @@ -202,6 +233,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings/' >() expectTypeOf(Link) @@ -219,6 +251,7 @@ test('when navigating a index route with search and params', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' | '.' | '..' >() @@ -254,6 +287,8 @@ test('when navigating a index route with search and params', () => { | '/blog/stats/' | '/posts/$postId/deep/' | '/posts/$postId/' + | '/settings' + | '/settings/' >() expectTypeOf(Link) @@ -261,7 +296,7 @@ test('when navigating a index route with search and params', () => { .toHaveProperty('from') .toEqualTypeOf< | '/' - | '/posts/$postId' + | '/posts/$postId/' | '/blog' | '/posts' | '/blog/$blogId' @@ -273,6 +308,7 @@ test('when navigating a index route with search and params', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' + | '/settings' | undefined >() @@ -366,12 +402,12 @@ test('when navigating a index route with search and params', () => { }) test('when navigating from a index route with search and params', () => { - expectTypeOf(Link) + expectTypeOf(Link) .parameter(0) .toHaveProperty('from') .toEqualTypeOf< | '/' - | '/posts/$postId' + | '/posts/$postId/' | '/blog' | '/posts' | '/blog/$blogId' @@ -383,10 +419,11 @@ test('when navigating from a index route with search and params', () => { | '/blog/' | '/posts/' | '/posts/$postId/deep' + | '/settings' | undefined >() - expectTypeOf(Link) + expectTypeOf(Link) .parameter(0) .toHaveProperty('search') .parameter(0) @@ -413,6 +450,7 @@ test('when using useNavigate', () => { | '/blog/stats' | '/posts/$postId/deep' | '/posts/$postId' + | '/settings' >() }) @@ -432,6 +470,7 @@ test('when using redirect', () => { | '/blog/$slug' | '/blog/stats' | '/posts/$postId/deep' + | '/settings' | undefined >() }) @@ -455,6 +494,8 @@ test('when using useSearch from a route with no search', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf(useSearch).returns.toEqualTypeOf<{}>() @@ -479,6 +520,8 @@ test('when using useSearch from a route with search', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -505,6 +548,8 @@ test('when using useLoaderData from a route with loaderData', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -531,6 +576,8 @@ test('when using useLoaderDeps from a route with loaderDeps', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -557,6 +604,8 @@ test('when using useMatch from a route', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -585,6 +634,8 @@ test('when using useParams from a route', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( @@ -611,6 +662,8 @@ test('when using useRouteContext from a route', () => { | '/posts/' | '/posts/$postId/deep' | '/posts/$postId/' + | '/_pathlessLayout' + | '/_pathlessLayout/settings' >() expectTypeOf( diff --git a/packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts b/packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts index e4f872ce3a9..b2ff9737650 100644 --- a/packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.snapshot.ts @@ -24,7 +24,7 @@ const NestedChildRoute = NestedChildRouteImport.update({ }as any) export interface FileRoutesByFullPath { -'/': typeof IndexRoute,'/nested/child': typeof NestedChildRoute,'/nested': typeof NestedIndexRoute +'/': typeof IndexRoute,'/nested/child': typeof NestedChildRoute,'/nested/': typeof NestedIndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute,'/nested/child': typeof NestedChildRoute,'/nested': typeof NestedIndexRoute @@ -35,7 +35,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath -fullPaths: '/'|'/nested/child'|'/nested' +fullPaths: '/'|'/nested/child'|'/nested/' fileRoutesByTo: FileRoutesByTo to: '/'|'/nested/child'|'/nested' id: '__root__'|'/'|'/nested/child'|'/nested/' @@ -57,7 +57,7 @@ declare module '@tanstack/react-router' { '/nested/': { id: '/nested/' path: '/nested' - fullPath: '/nested' + fullPath: '/nested/' preLoaderRoute: typeof NestedIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts b/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts index 754b730b7ae..76af9354014 100644 --- a/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/numbers-in-path/routeTree.snapshot.ts @@ -45,8 +45,8 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/03': typeof R03Route '/about': typeof AboutRoute - '/01-example': typeof R01ExampleIndexRoute - '/02': typeof R02IndexRoute + '/01-example/': typeof R01ExampleIndexRoute + '/02/': typeof R02IndexRoute } export interface FileRoutesByTo { '/': typeof IndexRoute @@ -65,7 +65,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/03' | '/about' | '/01-example' | '/02' + fullPaths: '/' | '/03' | '/about' | '/01-example/' | '/02/' fileRoutesByTo: FileRoutesByTo to: '/' | '/03' | '/about' | '/01-example' | '/02' id: '__root__' | '/' | '/03' | '/about' | '/01-example/' | '/02/' @@ -105,14 +105,14 @@ declare module '@tanstack/react-router' { '/02/': { id: '/02/' path: '/02' - fullPath: '/02' + fullPath: '/02/' preLoaderRoute: typeof R02IndexRouteImport parentRoute: typeof rootRouteImport } '/01-example/': { id: '/01-example/' path: '/01-example' - fullPath: '/01-example' + fullPath: '/01-example/' preLoaderRoute: typeof R01ExampleIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts b/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts index c937f95ba87..1a8ad18a103 100644 --- a/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/path-above-route-in-group/routeTree.snapshot.ts @@ -32,7 +32,7 @@ const ABcDEIndexRoute = ABcDEIndexRouteImport.update({ export interface FileRoutesByFullPath { '/a/$b': typeof ABcRouteRouteWithChildren '/a/$b/': typeof ABcIndexRoute - '/a/$b/d/e': typeof ABcDEIndexRoute + '/a/$b/d/e/': typeof ABcDEIndexRoute } export interface FileRoutesByTo { '/a/$b': typeof ABcIndexRoute @@ -46,7 +46,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/a/$b' | '/a/$b/' | '/a/$b/d/e' + fullPaths: '/a/$b' | '/a/$b/' | '/a/$b/d/e/' fileRoutesByTo: FileRoutesByTo to: '/a/$b' | '/a/$b/d/e' id: '__root__' | '/a/$b/(c)' | '/a/$b/(c)/' | '/a/$b/(c)/d/e/' @@ -75,7 +75,7 @@ declare module '@tanstack/react-router' { '/a/$b/(c)/d/e/': { id: '/a/$b/(c)/d/e/' path: '/d/e' - fullPath: '/a/$b/d/e' + fullPath: '/a/$b/d/e/' preLoaderRoute: typeof ABcDEIndexRouteImport parentRoute: typeof ABcRouteRoute } diff --git a/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts index ab31f63e997..3d3c2de9d5f 100644 --- a/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-config-file-default-export/routeTree.snapshot.ts @@ -78,15 +78,15 @@ const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof indexRoute - '/$lang': typeof pagesRoute + '/$lang/': typeof pagesRoute '/dashboard': typeof dbDashboardRouteWithChildren '/dashboard/': typeof dbDashboardIndexRoute '/dashboard/invoices': typeof dbDashboardInvoicesRouteWithChildren - '/hello': typeof HelloIndexRoute + '/hello/': typeof HelloIndexRoute '/dashboard/invoices/': typeof dbInvoicesIndexRoute '/dashboard/invoices/$id': typeof dbInvoiceDetailRoute '/hello/foo/$id': typeof HelloFooIdRoute - '/hello/foo': typeof HelloFooIndexRoute + '/hello/foo/': typeof HelloFooIndexRoute } export interface FileRoutesByTo { '/': typeof indexRoute @@ -116,15 +116,15 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' - | '/$lang' + | '/$lang/' | '/dashboard' | '/dashboard/' | '/dashboard/invoices' - | '/hello' + | '/hello/' | '/dashboard/invoices/' | '/dashboard/invoices/$id' | '/hello/foo/$id' - | '/hello/foo' + | '/hello/foo/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -161,7 +161,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof layoutRouteImport parentRoute: typeof rootRouteImport } @@ -182,14 +182,14 @@ declare module '@tanstack/react-router' { '/$lang/': { id: '/$lang/' path: '/$lang' - fullPath: '/$lang' + fullPath: '/$lang/' preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } '/_layout/hello/': { id: '/_layout/hello/' path: '/hello' - fullPath: '/hello' + fullPath: '/hello/' preLoaderRoute: typeof HelloIndexRouteImport parentRoute: typeof layoutRoute } @@ -210,7 +210,7 @@ declare module '@tanstack/react-router' { '/_layout/hello/foo/': { id: '/_layout/hello/foo/' path: '/hello/foo' - fullPath: '/hello/foo' + fullPath: '/hello/foo/' preLoaderRoute: typeof HelloFooIndexRouteImport parentRoute: typeof layoutRoute } diff --git a/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts index ab31f63e997..3d3c2de9d5f 100644 --- a/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-config-file-named-export/routeTree.snapshot.ts @@ -78,15 +78,15 @@ const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof indexRoute - '/$lang': typeof pagesRoute + '/$lang/': typeof pagesRoute '/dashboard': typeof dbDashboardRouteWithChildren '/dashboard/': typeof dbDashboardIndexRoute '/dashboard/invoices': typeof dbDashboardInvoicesRouteWithChildren - '/hello': typeof HelloIndexRoute + '/hello/': typeof HelloIndexRoute '/dashboard/invoices/': typeof dbInvoicesIndexRoute '/dashboard/invoices/$id': typeof dbInvoiceDetailRoute '/hello/foo/$id': typeof HelloFooIdRoute - '/hello/foo': typeof HelloFooIndexRoute + '/hello/foo/': typeof HelloFooIndexRoute } export interface FileRoutesByTo { '/': typeof indexRoute @@ -116,15 +116,15 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' - | '/$lang' + | '/$lang/' | '/dashboard' | '/dashboard/' | '/dashboard/invoices' - | '/hello' + | '/hello/' | '/dashboard/invoices/' | '/dashboard/invoices/$id' | '/hello/foo/$id' - | '/hello/foo' + | '/hello/foo/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -161,7 +161,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof layoutRouteImport parentRoute: typeof rootRouteImport } @@ -182,14 +182,14 @@ declare module '@tanstack/react-router' { '/$lang/': { id: '/$lang/' path: '/$lang' - fullPath: '/$lang' + fullPath: '/$lang/' preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } '/_layout/hello/': { id: '/_layout/hello/' path: '/hello' - fullPath: '/hello' + fullPath: '/hello/' preLoaderRoute: typeof HelloIndexRouteImport parentRoute: typeof layoutRoute } @@ -210,7 +210,7 @@ declare module '@tanstack/react-router' { '/_layout/hello/foo/': { id: '/_layout/hello/foo/' path: '/hello/foo' - fullPath: '/hello/foo' + fullPath: '/hello/foo/' preLoaderRoute: typeof HelloFooIndexRouteImport parentRoute: typeof layoutRoute } diff --git a/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts index 53ba374b1ac..28aebc0aad0 100644 --- a/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-inside-with-escaped-underscore/routeTree.snapshot.ts @@ -37,7 +37,7 @@ const nestedHomeRoute = nestedHomeRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof IndexRoute - '/nested': typeof nestedHomeRoute + '/nested/': typeof nestedHomeRoute '/nested/_auth': typeof nestedAuthRoute '/nested/_callback': typeof nestedCallbackRoute } @@ -56,7 +56,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/nested' | '/nested/_auth' | '/nested/_callback' + fullPaths: '/' | '/nested/' | '/nested/_auth' | '/nested/_callback' fileRoutesByTo: FileRoutesByTo to: '/' | '/nested' | '/nested/_auth' | '/nested/_callback' id: '__root__' | '/' | '/nested/' | '/nested/_auth' | '/nested/_callback' @@ -95,7 +95,7 @@ declare module '@tanstack/react-router' { '/nested/': { id: '/nested/' path: '/nested' - fullPath: '/nested' + fullPath: '/nested/' preLoaderRoute: typeof nestedHomeRouteImport parentRoute: typeof rootRouteImport } diff --git a/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts index 5726099cc75..8eed55a7fef 100644 --- a/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual-with-escaped-underscore/routeTree.snapshot.ts @@ -40,7 +40,7 @@ export interface FileRoutesByFullPath { '/': typeof indexRoute '/api/_auth': typeof ApiChar91_Char93authDotrouteRoute '/api/_hello': typeof ApiChar91_Char93helloRoute - '/api': typeof ApiIndexRoute + '/api/': typeof ApiIndexRoute } export interface FileRoutesByTo { '/': typeof indexRoute @@ -57,7 +57,7 @@ export interface FileRoutesById { } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/api/_auth' | '/api/_hello' | '/api' + fullPaths: '/' | '/api/_auth' | '/api/_hello' | '/api/' fileRoutesByTo: FileRoutesByTo to: '/' | '/api/_auth' | '/api/_hello' | '/api' id: '__root__' | '/' | '/api/_auth' | '/api/_hello' | '/api/' @@ -82,7 +82,7 @@ declare module '@tanstack/react-router' { '/api/': { id: '/api/' path: '/api' - fullPath: '/api' + fullPath: '/api/' preLoaderRoute: typeof ApiIndexRouteImport parentRoute: typeof rootRouteImport } diff --git a/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts b/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts index ab31f63e997..3d3c2de9d5f 100644 --- a/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts +++ b/packages/router-generator/tests/generator/virtual/routeTree.snapshot.ts @@ -78,15 +78,15 @@ const dbInvoicesIndexRoute = dbInvoicesIndexRouteImport.update({ export interface FileRoutesByFullPath { '/': typeof indexRoute - '/$lang': typeof pagesRoute + '/$lang/': typeof pagesRoute '/dashboard': typeof dbDashboardRouteWithChildren '/dashboard/': typeof dbDashboardIndexRoute '/dashboard/invoices': typeof dbDashboardInvoicesRouteWithChildren - '/hello': typeof HelloIndexRoute + '/hello/': typeof HelloIndexRoute '/dashboard/invoices/': typeof dbInvoicesIndexRoute '/dashboard/invoices/$id': typeof dbInvoiceDetailRoute '/hello/foo/$id': typeof HelloFooIdRoute - '/hello/foo': typeof HelloFooIndexRoute + '/hello/foo/': typeof HelloFooIndexRoute } export interface FileRoutesByTo { '/': typeof indexRoute @@ -116,15 +116,15 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' - | '/$lang' + | '/$lang/' | '/dashboard' | '/dashboard/' | '/dashboard/invoices' - | '/hello' + | '/hello/' | '/dashboard/invoices/' | '/dashboard/invoices/$id' | '/hello/foo/$id' - | '/hello/foo' + | '/hello/foo/' fileRoutesByTo: FileRoutesByTo to: | '/' @@ -161,7 +161,7 @@ declare module '@tanstack/react-router' { '/_layout': { id: '/_layout' path: '' - fullPath: '' + fullPath: '/' preLoaderRoute: typeof layoutRouteImport parentRoute: typeof rootRouteImport } @@ -182,14 +182,14 @@ declare module '@tanstack/react-router' { '/$lang/': { id: '/$lang/' path: '/$lang' - fullPath: '/$lang' + fullPath: '/$lang/' preLoaderRoute: typeof pagesRouteImport parentRoute: typeof rootRouteImport } '/_layout/hello/': { id: '/_layout/hello/' path: '/hello' - fullPath: '/hello' + fullPath: '/hello/' preLoaderRoute: typeof HelloIndexRouteImport parentRoute: typeof layoutRoute } @@ -210,7 +210,7 @@ declare module '@tanstack/react-router' { '/_layout/hello/foo/': { id: '/_layout/hello/foo/' path: '/hello/foo' - fullPath: '/hello/foo' + fullPath: '/hello/foo/' preLoaderRoute: typeof HelloFooIndexRouteImport parentRoute: typeof layoutRoute } diff --git a/packages/router-generator/tests/utils.test.ts b/packages/router-generator/tests/utils.test.ts index 8d1d1be3723..0d9f2478bf7 100644 --- a/packages/router-generator/tests/utils.test.ts +++ b/packages/router-generator/tests/utils.test.ts @@ -5,6 +5,7 @@ import { determineInitialRoutePath, hasEscapedLeadingUnderscore, hasEscapedTrailingUnderscore, + inferFullPath, isSegmentPathless, mergeImportDeclarations, multiSortBy, @@ -24,6 +25,20 @@ describe('cleanPath', () => { }) }) +describe('inferFullPath', () => { + it('returns "/" for pathless layouts under root', () => { + const node = { + routePath: '/_layout-a1', + originalRoutePath: '/_layout-a1', + cleanedPath: '', + _fsRouteType: 'pathless_layout', + } as unknown as RouteNode + + // This avoids inferred fullPath "" which breaks match.fullPath unions + expect(inferFullPath(node)).toBe('/') + }) +}) + describe('determineInitialRoutePath', () => { it('removes dots and adds slashes', () => { expect(determineInitialRoutePath('test.test')).toStrictEqual({