diff --git a/docs/router/framework/react/guide/path-params.md b/docs/router/framework/react/guide/path-params.md index 85cecc7ee61..75bcae601e9 100644 --- a/docs/router/framework/react/guide/path-params.md +++ b/docs/router/framework/react/guide/path-params.md @@ -235,7 +235,12 @@ Optional parameters create flexible URL patterns: - `/posts/{-$category}` matches both `/posts` and `/posts/tech` - `/posts/{-$category}/{-$slug}` matches `/posts`, `/posts/tech`, and `/posts/tech/hello-world` +- `/posts/{-$category}/{-$slug}/list` matches `/posts/list`, `/posts/tech/list`, and `/posts/tech/hello-world/list` - `/users/$id/{-$tab}` matches `/users/123` and `/users/123/settings` +- `/users/{-$category}/$id/{-$tab}` matches `/users/123`, `/users/admin/123` and `/users/admin/123/settings` +- `/users/{-$category}/$id/$slug/{-$tab}` matches `/users/123/slug`, `/users/admin/123/slug` and `/users/admin/123/slug/settings` +- `/users/{-$category}/$id/{-$tab}/$slug` matches `/users/123/slug`, `/users/admin/123/slug` and `/users/admin/123/settings/slug` +- `/users/{-$category}/$id/edit/{-$tab}` matches `/users/123/edit`, `/users/admin/123/edit`, `/users/123/edit/settings` and `/users/admin/123/edit/settings` When an optional parameter is not present in the URL, its value will be `undefined` in your route handlers and components. diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts deleted file mode 100644 index 2daaf9a821e..00000000000 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ /dev/null @@ -1,1697 +0,0 @@ -/* eslint-disable */ - -// @ts-nocheck - -// noinspection JSUnusedGlobalSymbols - -// This file was automatically generated by TanStack Router. -// You should NOT make any changes in this file as it will be overwritten. -// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. - -import { createFileRoute } from '@tanstack/react-router' - -import { Route as rootRouteImport } from './routes/__root' -import { Route as Char45824Char54620Char48124Char44397RouteImport } from './routes/대한민국' -import { Route as RemountDepsRouteImport } from './routes/remountDeps' -import { Route as PostsRouteImport } from './routes/posts' -import { Route as NotRemountDepsRouteImport } from './routes/notRemountDeps' -import { Route as EditingBRouteImport } from './routes/editing-b' -import { Route as EditingARouteImport } from './routes/editing-a' -import { Route as ComponentTypesTestRouteImport } from './routes/component-types-test' -import { Route as AnchorRouteImport } from './routes/anchor' -import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' -import { Route as NonNestedRouteRouteImport } from './routes/non-nested/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' -import { Route as RedirectIndexRouteImport } from './routes/redirect/index' -import { Route as PostsIndexRouteImport } from './routes/posts.index' -import { Route as ParamsPsIndexRouteImport } from './routes/params-ps/index' -import { Route as StructuralSharingEnabledRouteImport } from './routes/structural-sharing.$enabled' -import { Route as SearchParamsDefaultRouteImport } from './routes/search-params/default' -import { Route as RedirectTargetRouteImport } from './routes/redirect/$target' -import { Route as PostsPostIdRouteImport } from './routes/posts.$postId' -import { Route as NonNestedBazRouteImport } from './routes/non-nested/baz' -import { Route as LayoutLayout2RouteImport } from './routes/_layout/_layout-2' -import { Route as groupLazyinsideRouteImport } from './routes/(group)/lazyinside' -import { Route as groupInsideRouteImport } from './routes/(group)/inside' -import { Route as groupLayoutRouteImport } from './routes/(group)/_layout' -import { Route as anotherGroupOnlyrouteinsideRouteImport } from './routes/(another-group)/onlyrouteinside' -import { Route as RelativeUseNavigateRouteRouteImport } from './routes/relative/useNavigate/route' -import { Route as RelativeLinkRouteRouteImport } from './routes/relative/link/route' -import { Route as ParamsPsNonNestedRouteRouteImport } from './routes/params-ps/non-nested/route' -import { Route as RedirectTargetIndexRouteImport } from './routes/redirect/$target/index' -import { Route as ParamsPsWildcardIndexRouteImport } from './routes/params-ps/wildcard/index' -import { Route as ParamsPsNamedIndexRouteImport } from './routes/params-ps/named/index' -import { Route as RelativeUseNavigateRelativeUseNavigateBRouteImport } from './routes/relative/useNavigate/relative-useNavigate-b' -import { Route as RelativeUseNavigateRelativeUseNavigateARouteImport } from './routes/relative/useNavigate/relative-useNavigate-a' -import { Route as RelativeLinkRelativeLinkBRouteImport } from './routes/relative/link/relative-link-b' -import { Route as RelativeLinkRelativeLinkARouteImport } from './routes/relative/link/relative-link-a' -import { Route as RedirectPreloadThirdRouteImport } from './routes/redirect/preload/third' -import { Route as RedirectPreloadSecondRouteImport } from './routes/redirect/preload/second' -import { Route as RedirectPreloadFirstRouteImport } from './routes/redirect/preload/first' -import { Route as RedirectTargetViaLoaderRouteImport } from './routes/redirect/$target/via-loader' -import { Route as RedirectTargetViaBeforeLoadRouteImport } from './routes/redirect/$target/via-beforeLoad' -import { Route as PostsPostIdEditRouteImport } from './routes/posts_.$postId.edit' -import { Route as ParamsSingleValueRouteImport } from './routes/params.single.$value' -import { Route as ParamsPsWildcardChar123Char125suffixRouteImport } from './routes/params-ps/wildcard/{$}suffix' -import { Route as ParamsPsWildcardPrefixChar123Char125RouteImport } from './routes/params-ps/wildcard/prefix{$}' -import { Route as ParamsPsWildcardSplatRouteImport } from './routes/params-ps/wildcard/$' -import { Route as ParamsPsNamedChar123fooChar125suffixRouteImport } from './routes/params-ps/named/{$foo}suffix' -import { Route as ParamsPsNamedPrefixChar123fooChar125RouteImport } from './routes/params-ps/named/prefix{$foo}' -import { Route as NonNestedBazBazidRouteImport } from './routes/non-nested/baz.$bazid' -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' -import { Route as groupLayoutInsidelayoutRouteImport } from './routes/(group)/_layout.insidelayout' -import { Route as ParamsPsNonNestedFooRouteRouteImport } from './routes/params-ps/non-nested/$foo_/route' -import { Route as ParamsPsNamedFooRouteRouteImport } from './routes/params-ps/named/$foo/route' -import { Route as RelativeUseNavigateWithSearchIndexRouteImport } from './routes/relative/useNavigate/with-search/index' -import { Route as RelativeUseNavigatePathIndexRouteImport } from './routes/relative/useNavigate/path/index' -import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/relative/useNavigate/nested/index' -import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' -import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' -import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' -import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' -import { Route as NonNestedBazBazidEditRouteImport } from './routes/non-nested/baz_.$bazid.edit' -import { Route as ParamsPsNamedFooBarRouteRouteImport } from './routes/params-ps/named/$foo/$bar.route' -import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' -import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' -import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' -import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' -import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' - -const groupRouteImport = createFileRoute('/(group)')() - -const groupRoute = groupRouteImport.update({ - id: '/(group)', - getParentRoute: () => rootRouteImport, -} as any) -const Char45824Char54620Char48124Char44397Route = - Char45824Char54620Char48124Char44397RouteImport.update({ - id: '/대한민국', - path: '/대한민국', - getParentRoute: () => rootRouteImport, - } as any) -const RemountDepsRoute = RemountDepsRouteImport.update({ - id: '/remountDeps', - path: '/remountDeps', - getParentRoute: () => rootRouteImport, -} as any) -const PostsRoute = PostsRouteImport.update({ - id: '/posts', - path: '/posts', - getParentRoute: () => rootRouteImport, -} as any) -const NotRemountDepsRoute = NotRemountDepsRouteImport.update({ - id: '/notRemountDeps', - path: '/notRemountDeps', - getParentRoute: () => rootRouteImport, -} as any) -const EditingBRoute = EditingBRouteImport.update({ - id: '/editing-b', - path: '/editing-b', - getParentRoute: () => rootRouteImport, -} as any) -const EditingARoute = EditingARouteImport.update({ - id: '/editing-a', - path: '/editing-a', - getParentRoute: () => rootRouteImport, -} as any) -const ComponentTypesTestRoute = ComponentTypesTestRouteImport.update({ - id: '/component-types-test', - path: '/component-types-test', - getParentRoute: () => rootRouteImport, -} as any) -const AnchorRoute = AnchorRouteImport.update({ - id: '/anchor', - path: '/anchor', - getParentRoute: () => rootRouteImport, -} as any) -const LayoutRoute = LayoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => rootRouteImport, -} as any) -const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ - id: '/search-params', - path: '/search-params', - getParentRoute: () => rootRouteImport, -} as any) -const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ - id: '/non-nested', - path: '/non-nested', - getParentRoute: () => rootRouteImport, -} as any) -const IndexRoute = IndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => rootRouteImport, -} as any) -const SearchParamsIndexRoute = SearchParamsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RelativeIndexRoute = RelativeIndexRouteImport.update({ - id: '/relative/', - path: '/relative/', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectIndexRoute = RedirectIndexRouteImport.update({ - id: '/redirect/', - path: '/redirect/', - getParentRoute: () => rootRouteImport, -} as any) -const PostsIndexRoute = PostsIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => PostsRoute, -} as any) -const ParamsPsIndexRoute = ParamsPsIndexRouteImport.update({ - id: '/params-ps/', - path: '/params-ps/', - getParentRoute: () => rootRouteImport, -} as any) -const StructuralSharingEnabledRoute = - StructuralSharingEnabledRouteImport.update({ - id: '/structural-sharing/$enabled', - path: '/structural-sharing/$enabled', - getParentRoute: () => rootRouteImport, - } as any) -const SearchParamsDefaultRoute = SearchParamsDefaultRouteImport.update({ - id: '/default', - path: '/default', - getParentRoute: () => SearchParamsRouteRoute, -} as any) -const RedirectTargetRoute = RedirectTargetRouteImport.update({ - id: '/redirect/$target', - path: '/redirect/$target', - getParentRoute: () => rootRouteImport, -} as any) -const PostsPostIdRoute = PostsPostIdRouteImport.update({ - id: '/$postId', - path: '/$postId', - getParentRoute: () => PostsRoute, -} as any) -const NonNestedBazRoute = NonNestedBazRouteImport.update({ - id: '/baz', - path: '/baz', - getParentRoute: () => NonNestedRouteRoute, -} as any) -const LayoutLayout2Route = LayoutLayout2RouteImport.update({ - id: '/_layout-2', - getParentRoute: () => LayoutRoute, -} as any) -const groupLazyinsideRoute = groupLazyinsideRouteImport - .update({ - id: '/lazyinside', - path: '/lazyinside', - getParentRoute: () => groupRoute, - } as any) - .lazy(() => import('./routes/(group)/lazyinside.lazy').then((d) => d.Route)) -const groupInsideRoute = groupInsideRouteImport.update({ - id: '/inside', - path: '/inside', - getParentRoute: () => groupRoute, -} as any) -const groupLayoutRoute = groupLayoutRouteImport.update({ - id: '/_layout', - getParentRoute: () => groupRoute, -} as any) -const anotherGroupOnlyrouteinsideRoute = - anotherGroupOnlyrouteinsideRouteImport.update({ - id: '/(another-group)/onlyrouteinside', - path: '/onlyrouteinside', - getParentRoute: () => rootRouteImport, - } as any) -const RelativeUseNavigateRouteRoute = - RelativeUseNavigateRouteRouteImport.update({ - id: '/relative/useNavigate', - path: '/relative/useNavigate', - getParentRoute: () => rootRouteImport, - } as any) -const RelativeLinkRouteRoute = RelativeLinkRouteRouteImport.update({ - id: '/relative/link', - path: '/relative/link', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsPsNonNestedRouteRoute = ParamsPsNonNestedRouteRouteImport.update({ - id: '/params-ps/non-nested', - path: '/params-ps/non-nested', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectTargetIndexRoute = RedirectTargetIndexRouteImport.update({ - id: '/', - path: '/', - getParentRoute: () => RedirectTargetRoute, -} as any) -const ParamsPsWildcardIndexRoute = ParamsPsWildcardIndexRouteImport.update({ - id: '/params-ps/wildcard/', - path: '/params-ps/wildcard/', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsPsNamedIndexRoute = ParamsPsNamedIndexRouteImport.update({ - id: '/params-ps/named/', - path: '/params-ps/named/', - getParentRoute: () => rootRouteImport, -} as any) -const RelativeUseNavigateRelativeUseNavigateBRoute = - RelativeUseNavigateRelativeUseNavigateBRouteImport.update({ - id: '/relative-useNavigate-b', - path: '/relative-useNavigate-b', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigateRelativeUseNavigateARoute = - RelativeUseNavigateRelativeUseNavigateARouteImport.update({ - id: '/relative-useNavigate-a', - path: '/relative-useNavigate-a', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeLinkRelativeLinkBRoute = - RelativeLinkRelativeLinkBRouteImport.update({ - id: '/relative-link-b', - path: '/relative-link-b', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkRelativeLinkARoute = - RelativeLinkRelativeLinkARouteImport.update({ - id: '/relative-link-a', - path: '/relative-link-a', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RedirectPreloadThirdRoute = RedirectPreloadThirdRouteImport.update({ - id: '/redirect/preload/third', - path: '/redirect/preload/third', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectPreloadSecondRoute = RedirectPreloadSecondRouteImport.update({ - id: '/redirect/preload/second', - path: '/redirect/preload/second', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectPreloadFirstRoute = RedirectPreloadFirstRouteImport.update({ - id: '/redirect/preload/first', - path: '/redirect/preload/first', - getParentRoute: () => rootRouteImport, -} as any) -const RedirectTargetViaLoaderRoute = RedirectTargetViaLoaderRouteImport.update({ - id: '/via-loader', - path: '/via-loader', - getParentRoute: () => RedirectTargetRoute, -} as any) -const RedirectTargetViaBeforeLoadRoute = - RedirectTargetViaBeforeLoadRouteImport.update({ - id: '/via-beforeLoad', - path: '/via-beforeLoad', - getParentRoute: () => RedirectTargetRoute, - } as any) -const PostsPostIdEditRoute = PostsPostIdEditRouteImport.update({ - id: '/posts_/$postId/edit', - path: '/posts/$postId/edit', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsSingleValueRoute = ParamsSingleValueRouteImport.update({ - id: '/params/single/$value', - path: '/params/single/$value', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsPsWildcardChar123Char125suffixRoute = - ParamsPsWildcardChar123Char125suffixRouteImport.update({ - id: '/params-ps/wildcard/{$}suffix', - path: '/params-ps/wildcard/{$}suffix', - getParentRoute: () => rootRouteImport, - } as any) -const ParamsPsWildcardPrefixChar123Char125Route = - ParamsPsWildcardPrefixChar123Char125RouteImport.update({ - id: '/params-ps/wildcard/prefix{$}', - path: '/params-ps/wildcard/prefix{$}', - getParentRoute: () => rootRouteImport, - } as any) -const ParamsPsWildcardSplatRoute = ParamsPsWildcardSplatRouteImport.update({ - id: '/params-ps/wildcard/$', - path: '/params-ps/wildcard/$', - getParentRoute: () => rootRouteImport, -} as any) -const ParamsPsNamedChar123fooChar125suffixRoute = - ParamsPsNamedChar123fooChar125suffixRouteImport.update({ - id: '/params-ps/named/{$foo}suffix', - path: '/params-ps/named/{$foo}suffix', - getParentRoute: () => rootRouteImport, - } as any) -const ParamsPsNamedPrefixChar123fooChar125Route = - ParamsPsNamedPrefixChar123fooChar125RouteImport.update({ - id: '/params-ps/named/prefix{$foo}', - path: '/params-ps/named/prefix{$foo}', - getParentRoute: () => rootRouteImport, - } as any) -const NonNestedBazBazidRoute = NonNestedBazBazidRouteImport.update({ - id: '/$bazid', - path: '/$bazid', - getParentRoute: () => NonNestedBazRoute, -} as any) -const LayoutLayout2LayoutBRoute = LayoutLayout2LayoutBRouteImport.update({ - id: '/layout-b', - path: '/layout-b', - getParentRoute: () => LayoutLayout2Route, -} as any) -const LayoutLayout2LayoutARoute = LayoutLayout2LayoutARouteImport.update({ - id: '/layout-a', - path: '/layout-a', - getParentRoute: () => LayoutLayout2Route, -} as any) -const groupSubfolderInsideRoute = groupSubfolderInsideRouteImport.update({ - id: '/subfolder/inside', - path: '/subfolder/inside', - getParentRoute: () => groupRoute, -} as any) -const groupLayoutInsidelayoutRoute = groupLayoutInsidelayoutRouteImport.update({ - id: '/insidelayout', - path: '/insidelayout', - getParentRoute: () => groupLayoutRoute, -} as any) -const ParamsPsNonNestedFooRouteRoute = - ParamsPsNonNestedFooRouteRouteImport.update({ - id: '/$foo_', - path: '/$foo', - getParentRoute: () => ParamsPsNonNestedRouteRoute, - } as any) -const ParamsPsNamedFooRouteRoute = ParamsPsNamedFooRouteRouteImport.update({ - id: '/params-ps/named/$foo', - path: '/params-ps/named/$foo', - getParentRoute: () => rootRouteImport, -} as any) -const RelativeUseNavigateWithSearchIndexRoute = - RelativeUseNavigateWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigatePathIndexRoute = - RelativeUseNavigatePathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigateNestedIndexRoute = - RelativeUseNavigateNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeLinkWithSearchIndexRoute = - RelativeLinkWithSearchIndexRouteImport.update({ - id: '/with-search/', - path: '/with-search/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkPathIndexRoute = RelativeLinkPathIndexRouteImport.update({ - id: '/path/', - path: '/path/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) -const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ - id: '/nested/', - path: '/nested/', - getParentRoute: () => RelativeLinkRouteRoute, -} as any) -const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ - id: '/$bar', - path: '/$bar', - getParentRoute: () => ParamsPsNonNestedFooRouteRoute, -} as any) -const NonNestedBazBazidEditRoute = NonNestedBazBazidEditRouteImport.update({ - id: '/baz_/$bazid/edit', - path: '/baz/$bazid/edit', - getParentRoute: () => NonNestedRouteRoute, -} as any) -const ParamsPsNamedFooBarRouteRoute = - ParamsPsNamedFooBarRouteRouteImport.update({ - id: '/$bar', - path: '/$bar', - getParentRoute: () => ParamsPsNamedFooRouteRoute, - } as any) -const RelativeUseNavigatePathPathIndexRoute = - RelativeUseNavigatePathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeUseNavigateNestedDeepIndexRoute = - RelativeUseNavigateNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', - getParentRoute: () => RelativeUseNavigateRouteRoute, - } as any) -const RelativeLinkPathPathIndexRoute = - RelativeLinkPathPathIndexRouteImport.update({ - id: '/path/$path/', - path: '/path/$path/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const RelativeLinkNestedDeepIndexRoute = - RelativeLinkNestedDeepIndexRouteImport.update({ - id: '/nested/deep/', - path: '/nested/deep/', - getParentRoute: () => RelativeLinkRouteRoute, - } as any) -const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ - id: '/$baz', - path: '/$baz', - getParentRoute: () => ParamsPsNamedFooBarRouteRoute, -} as any) - -export interface FileRoutesByFullPath { - '/': typeof groupLayoutRouteWithChildren - '/non-nested': typeof NonNestedRouteRouteWithChildren - '/search-params': typeof SearchParamsRouteRouteWithChildren - '/anchor': typeof AnchorRoute - '/component-types-test': typeof ComponentTypesTestRoute - '/editing-a': typeof EditingARoute - '/editing-b': typeof EditingBRoute - '/notRemountDeps': typeof NotRemountDepsRoute - '/posts': typeof PostsRouteWithChildren - '/remountDeps': typeof RemountDepsRoute - '/대한민국': typeof Char45824Char54620Char48124Char44397Route - '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren - '/relative/link': typeof RelativeLinkRouteRouteWithChildren - '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren - '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute - '/inside': typeof groupInsideRoute - '/lazyinside': typeof groupLazyinsideRoute - '/non-nested/baz': typeof NonNestedBazRouteWithChildren - '/posts/$postId': typeof PostsPostIdRoute - '/redirect/$target': typeof RedirectTargetRouteWithChildren - '/search-params/default': typeof SearchParamsDefaultRoute - '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute - '/params-ps': typeof ParamsPsIndexRoute - '/posts/': typeof PostsIndexRoute - '/redirect': typeof RedirectIndexRoute - '/relative': typeof RelativeIndexRoute - '/search-params/': typeof SearchParamsIndexRoute - '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren - '/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren - '/insidelayout': typeof groupLayoutInsidelayoutRoute - '/subfolder/inside': typeof groupSubfolderInsideRoute - '/layout-a': typeof LayoutLayout2LayoutARoute - '/layout-b': typeof LayoutLayout2LayoutBRoute - '/non-nested/baz/$bazid': typeof NonNestedBazBazidRoute - '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route - '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute - '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute - '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route - '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute - '/params/single/$value': typeof ParamsSingleValueRoute - '/posts/$postId/edit': typeof PostsPostIdEditRoute - '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute - '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute - '/redirect/preload/first': typeof RedirectPreloadFirstRoute - '/redirect/preload/second': typeof RedirectPreloadSecondRoute - '/redirect/preload/third': typeof RedirectPreloadThirdRoute - '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute - '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute - '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute - '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute - '/params-ps/named': typeof ParamsPsNamedIndexRoute - '/params-ps/wildcard': typeof ParamsPsWildcardIndexRoute - '/redirect/$target/': typeof RedirectTargetIndexRoute - '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren - '/non-nested/baz/$bazid/edit': typeof NonNestedBazBazidEditRoute - '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute - '/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 - '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute - '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute - '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute - '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute - '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute -} -export interface FileRoutesByTo { - '/': typeof groupLayoutRouteWithChildren - '/non-nested': typeof NonNestedRouteRouteWithChildren - '/anchor': typeof AnchorRoute - '/component-types-test': typeof ComponentTypesTestRoute - '/editing-a': typeof EditingARoute - '/editing-b': typeof EditingBRoute - '/notRemountDeps': typeof NotRemountDepsRoute - '/remountDeps': typeof RemountDepsRoute - '/대한민국': typeof Char45824Char54620Char48124Char44397Route - '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren - '/relative/link': typeof RelativeLinkRouteRouteWithChildren - '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren - '/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute - '/inside': typeof groupInsideRoute - '/lazyinside': typeof groupLazyinsideRoute - '/non-nested/baz': typeof NonNestedBazRouteWithChildren - '/posts/$postId': typeof PostsPostIdRoute - '/search-params/default': typeof SearchParamsDefaultRoute - '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute - '/params-ps': typeof ParamsPsIndexRoute - '/posts': typeof PostsIndexRoute - '/redirect': typeof RedirectIndexRoute - '/relative': typeof RelativeIndexRoute - '/search-params': typeof SearchParamsIndexRoute - '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren - '/params-ps/non-nested/$foo': typeof ParamsPsNonNestedFooRouteRouteWithChildren - '/insidelayout': typeof groupLayoutInsidelayoutRoute - '/subfolder/inside': typeof groupSubfolderInsideRoute - '/layout-a': typeof LayoutLayout2LayoutARoute - '/layout-b': typeof LayoutLayout2LayoutBRoute - '/non-nested/baz/$bazid': typeof NonNestedBazBazidRoute - '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route - '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute - '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute - '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route - '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute - '/params/single/$value': typeof ParamsSingleValueRoute - '/posts/$postId/edit': typeof PostsPostIdEditRoute - '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute - '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute - '/redirect/preload/first': typeof RedirectPreloadFirstRoute - '/redirect/preload/second': typeof RedirectPreloadSecondRoute - '/redirect/preload/third': typeof RedirectPreloadThirdRoute - '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute - '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute - '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute - '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute - '/params-ps/named': typeof ParamsPsNamedIndexRoute - '/params-ps/wildcard': typeof ParamsPsWildcardIndexRoute - '/redirect/$target': typeof RedirectTargetIndexRoute - '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren - '/non-nested/baz/$bazid/edit': typeof NonNestedBazBazidEditRoute - '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute - '/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 - '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute - '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute - '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute - '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute - '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute -} -export interface FileRoutesById { - __root__: typeof rootRouteImport - '/': typeof IndexRoute - '/non-nested': typeof NonNestedRouteRouteWithChildren - '/search-params': typeof SearchParamsRouteRouteWithChildren - '/_layout': typeof LayoutRouteWithChildren - '/anchor': typeof AnchorRoute - '/component-types-test': typeof ComponentTypesTestRoute - '/editing-a': typeof EditingARoute - '/editing-b': typeof EditingBRoute - '/notRemountDeps': typeof NotRemountDepsRoute - '/posts': typeof PostsRouteWithChildren - '/remountDeps': typeof RemountDepsRoute - '/대한민국': typeof Char45824Char54620Char48124Char44397Route - '/params-ps/non-nested': typeof ParamsPsNonNestedRouteRouteWithChildren - '/relative/link': typeof RelativeLinkRouteRouteWithChildren - '/relative/useNavigate': typeof RelativeUseNavigateRouteRouteWithChildren - '/(another-group)/onlyrouteinside': typeof anotherGroupOnlyrouteinsideRoute - '/(group)': typeof groupRouteWithChildren - '/(group)/_layout': typeof groupLayoutRouteWithChildren - '/(group)/inside': typeof groupInsideRoute - '/(group)/lazyinside': typeof groupLazyinsideRoute - '/_layout/_layout-2': typeof LayoutLayout2RouteWithChildren - '/non-nested/baz': typeof NonNestedBazRouteWithChildren - '/posts/$postId': typeof PostsPostIdRoute - '/redirect/$target': typeof RedirectTargetRouteWithChildren - '/search-params/default': typeof SearchParamsDefaultRoute - '/structural-sharing/$enabled': typeof StructuralSharingEnabledRoute - '/params-ps/': typeof ParamsPsIndexRoute - '/posts/': typeof PostsIndexRoute - '/redirect/': typeof RedirectIndexRoute - '/relative/': typeof RelativeIndexRoute - '/search-params/': typeof SearchParamsIndexRoute - '/params-ps/named/$foo': typeof ParamsPsNamedFooRouteRouteWithChildren - '/params-ps/non-nested/$foo_': typeof ParamsPsNonNestedFooRouteRouteWithChildren - '/(group)/_layout/insidelayout': typeof groupLayoutInsidelayoutRoute - '/(group)/subfolder/inside': typeof groupSubfolderInsideRoute - '/_layout/_layout-2/layout-a': typeof LayoutLayout2LayoutARoute - '/_layout/_layout-2/layout-b': typeof LayoutLayout2LayoutBRoute - '/non-nested/baz/$bazid': typeof NonNestedBazBazidRoute - '/params-ps/named/prefix{$foo}': typeof ParamsPsNamedPrefixChar123fooChar125Route - '/params-ps/named/{$foo}suffix': typeof ParamsPsNamedChar123fooChar125suffixRoute - '/params-ps/wildcard/$': typeof ParamsPsWildcardSplatRoute - '/params-ps/wildcard/prefix{$}': typeof ParamsPsWildcardPrefixChar123Char125Route - '/params-ps/wildcard/{$}suffix': typeof ParamsPsWildcardChar123Char125suffixRoute - '/params/single/$value': typeof ParamsSingleValueRoute - '/posts_/$postId/edit': typeof PostsPostIdEditRoute - '/redirect/$target/via-beforeLoad': typeof RedirectTargetViaBeforeLoadRoute - '/redirect/$target/via-loader': typeof RedirectTargetViaLoaderRoute - '/redirect/preload/first': typeof RedirectPreloadFirstRoute - '/redirect/preload/second': typeof RedirectPreloadSecondRoute - '/redirect/preload/third': typeof RedirectPreloadThirdRoute - '/relative/link/relative-link-a': typeof RelativeLinkRelativeLinkARoute - '/relative/link/relative-link-b': typeof RelativeLinkRelativeLinkBRoute - '/relative/useNavigate/relative-useNavigate-a': typeof RelativeUseNavigateRelativeUseNavigateARoute - '/relative/useNavigate/relative-useNavigate-b': typeof RelativeUseNavigateRelativeUseNavigateBRoute - '/params-ps/named/': typeof ParamsPsNamedIndexRoute - '/params-ps/wildcard/': typeof ParamsPsWildcardIndexRoute - '/redirect/$target/': typeof RedirectTargetIndexRoute - '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren - '/non-nested/baz_/$bazid/edit': typeof NonNestedBazBazidEditRoute - '/params-ps/non-nested/$foo_/$bar': typeof ParamsPsNonNestedFooBarRoute - '/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 - '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute - '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute - '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute - '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute - '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute -} -export interface FileRouteTypes { - fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: - | '/' - | '/non-nested' - | '/search-params' - | '/anchor' - | '/component-types-test' - | '/editing-a' - | '/editing-b' - | '/notRemountDeps' - | '/posts' - | '/remountDeps' - | '/대한민국' - | '/params-ps/non-nested' - | '/relative/link' - | '/relative/useNavigate' - | '/onlyrouteinside' - | '/inside' - | '/lazyinside' - | '/non-nested/baz' - | '/posts/$postId' - | '/redirect/$target' - | '/search-params/default' - | '/structural-sharing/$enabled' - | '/params-ps' - | '/posts/' - | '/redirect' - | '/relative' - | '/search-params/' - | '/params-ps/named/$foo' - | '/params-ps/non-nested/$foo' - | '/insidelayout' - | '/subfolder/inside' - | '/layout-a' - | '/layout-b' - | '/non-nested/baz/$bazid' - | '/params-ps/named/prefix{$foo}' - | '/params-ps/named/{$foo}suffix' - | '/params-ps/wildcard/$' - | '/params-ps/wildcard/prefix{$}' - | '/params-ps/wildcard/{$}suffix' - | '/params/single/$value' - | '/posts/$postId/edit' - | '/redirect/$target/via-beforeLoad' - | '/redirect/$target/via-loader' - | '/redirect/preload/first' - | '/redirect/preload/second' - | '/redirect/preload/third' - | '/relative/link/relative-link-a' - | '/relative/link/relative-link-b' - | '/relative/useNavigate/relative-useNavigate-a' - | '/relative/useNavigate/relative-useNavigate-b' - | '/params-ps/named' - | '/params-ps/wildcard' - | '/redirect/$target/' - | '/params-ps/named/$foo/$bar' - | '/non-nested/baz/$bazid/edit' - | '/params-ps/non-nested/$foo/$bar' - | '/relative/link/nested' - | '/relative/link/path' - | '/relative/link/with-search' - | '/relative/useNavigate/nested' - | '/relative/useNavigate/path' - | '/relative/useNavigate/with-search' - | '/params-ps/named/$foo/$bar/$baz' - | '/relative/link/nested/deep' - | '/relative/link/path/$path' - | '/relative/useNavigate/nested/deep' - | '/relative/useNavigate/path/$path' - fileRoutesByTo: FileRoutesByTo - to: - | '/' - | '/non-nested' - | '/anchor' - | '/component-types-test' - | '/editing-a' - | '/editing-b' - | '/notRemountDeps' - | '/remountDeps' - | '/대한민국' - | '/params-ps/non-nested' - | '/relative/link' - | '/relative/useNavigate' - | '/onlyrouteinside' - | '/inside' - | '/lazyinside' - | '/non-nested/baz' - | '/posts/$postId' - | '/search-params/default' - | '/structural-sharing/$enabled' - | '/params-ps' - | '/posts' - | '/redirect' - | '/relative' - | '/search-params' - | '/params-ps/named/$foo' - | '/params-ps/non-nested/$foo' - | '/insidelayout' - | '/subfolder/inside' - | '/layout-a' - | '/layout-b' - | '/non-nested/baz/$bazid' - | '/params-ps/named/prefix{$foo}' - | '/params-ps/named/{$foo}suffix' - | '/params-ps/wildcard/$' - | '/params-ps/wildcard/prefix{$}' - | '/params-ps/wildcard/{$}suffix' - | '/params/single/$value' - | '/posts/$postId/edit' - | '/redirect/$target/via-beforeLoad' - | '/redirect/$target/via-loader' - | '/redirect/preload/first' - | '/redirect/preload/second' - | '/redirect/preload/third' - | '/relative/link/relative-link-a' - | '/relative/link/relative-link-b' - | '/relative/useNavigate/relative-useNavigate-a' - | '/relative/useNavigate/relative-useNavigate-b' - | '/params-ps/named' - | '/params-ps/wildcard' - | '/redirect/$target' - | '/params-ps/named/$foo/$bar' - | '/non-nested/baz/$bazid/edit' - | '/params-ps/non-nested/$foo/$bar' - | '/relative/link/nested' - | '/relative/link/path' - | '/relative/link/with-search' - | '/relative/useNavigate/nested' - | '/relative/useNavigate/path' - | '/relative/useNavigate/with-search' - | '/params-ps/named/$foo/$bar/$baz' - | '/relative/link/nested/deep' - | '/relative/link/path/$path' - | '/relative/useNavigate/nested/deep' - | '/relative/useNavigate/path/$path' - id: - | '__root__' - | '/' - | '/non-nested' - | '/search-params' - | '/_layout' - | '/anchor' - | '/component-types-test' - | '/editing-a' - | '/editing-b' - | '/notRemountDeps' - | '/posts' - | '/remountDeps' - | '/대한민국' - | '/params-ps/non-nested' - | '/relative/link' - | '/relative/useNavigate' - | '/(another-group)/onlyrouteinside' - | '/(group)' - | '/(group)/_layout' - | '/(group)/inside' - | '/(group)/lazyinside' - | '/_layout/_layout-2' - | '/non-nested/baz' - | '/posts/$postId' - | '/redirect/$target' - | '/search-params/default' - | '/structural-sharing/$enabled' - | '/params-ps/' - | '/posts/' - | '/redirect/' - | '/relative/' - | '/search-params/' - | '/params-ps/named/$foo' - | '/params-ps/non-nested/$foo_' - | '/(group)/_layout/insidelayout' - | '/(group)/subfolder/inside' - | '/_layout/_layout-2/layout-a' - | '/_layout/_layout-2/layout-b' - | '/non-nested/baz/$bazid' - | '/params-ps/named/prefix{$foo}' - | '/params-ps/named/{$foo}suffix' - | '/params-ps/wildcard/$' - | '/params-ps/wildcard/prefix{$}' - | '/params-ps/wildcard/{$}suffix' - | '/params/single/$value' - | '/posts_/$postId/edit' - | '/redirect/$target/via-beforeLoad' - | '/redirect/$target/via-loader' - | '/redirect/preload/first' - | '/redirect/preload/second' - | '/redirect/preload/third' - | '/relative/link/relative-link-a' - | '/relative/link/relative-link-b' - | '/relative/useNavigate/relative-useNavigate-a' - | '/relative/useNavigate/relative-useNavigate-b' - | '/params-ps/named/' - | '/params-ps/wildcard/' - | '/redirect/$target/' - | '/params-ps/named/$foo/$bar' - | '/non-nested/baz_/$bazid/edit' - | '/params-ps/non-nested/$foo_/$bar' - | '/relative/link/nested/' - | '/relative/link/path/' - | '/relative/link/with-search/' - | '/relative/useNavigate/nested/' - | '/relative/useNavigate/path/' - | '/relative/useNavigate/with-search/' - | '/params-ps/named/$foo/$bar/$baz' - | '/relative/link/nested/deep/' - | '/relative/link/path/$path/' - | '/relative/useNavigate/nested/deep/' - | '/relative/useNavigate/path/$path/' - fileRoutesById: FileRoutesById -} -export interface RootRouteChildren { - IndexRoute: typeof IndexRoute - NonNestedRouteRoute: typeof NonNestedRouteRouteWithChildren - SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren - LayoutRoute: typeof LayoutRouteWithChildren - AnchorRoute: typeof AnchorRoute - ComponentTypesTestRoute: typeof ComponentTypesTestRoute - EditingARoute: typeof EditingARoute - EditingBRoute: typeof EditingBRoute - NotRemountDepsRoute: typeof NotRemountDepsRoute - PostsRoute: typeof PostsRouteWithChildren - RemountDepsRoute: typeof RemountDepsRoute - Char45824Char54620Char48124Char44397Route: typeof Char45824Char54620Char48124Char44397Route - ParamsPsNonNestedRouteRoute: typeof ParamsPsNonNestedRouteRouteWithChildren - RelativeLinkRouteRoute: typeof RelativeLinkRouteRouteWithChildren - RelativeUseNavigateRouteRoute: typeof RelativeUseNavigateRouteRouteWithChildren - anotherGroupOnlyrouteinsideRoute: typeof anotherGroupOnlyrouteinsideRoute - groupRoute: typeof groupRouteWithChildren - RedirectTargetRoute: typeof RedirectTargetRouteWithChildren - StructuralSharingEnabledRoute: typeof StructuralSharingEnabledRoute - ParamsPsIndexRoute: typeof ParamsPsIndexRoute - RedirectIndexRoute: typeof RedirectIndexRoute - RelativeIndexRoute: typeof RelativeIndexRoute - ParamsPsNamedFooRouteRoute: typeof ParamsPsNamedFooRouteRouteWithChildren - ParamsPsNamedPrefixChar123fooChar125Route: typeof ParamsPsNamedPrefixChar123fooChar125Route - ParamsPsNamedChar123fooChar125suffixRoute: typeof ParamsPsNamedChar123fooChar125suffixRoute - ParamsPsWildcardSplatRoute: typeof ParamsPsWildcardSplatRoute - ParamsPsWildcardPrefixChar123Char125Route: typeof ParamsPsWildcardPrefixChar123Char125Route - ParamsPsWildcardChar123Char125suffixRoute: typeof ParamsPsWildcardChar123Char125suffixRoute - ParamsSingleValueRoute: typeof ParamsSingleValueRoute - PostsPostIdEditRoute: typeof PostsPostIdEditRoute - RedirectPreloadFirstRoute: typeof RedirectPreloadFirstRoute - RedirectPreloadSecondRoute: typeof RedirectPreloadSecondRoute - RedirectPreloadThirdRoute: typeof RedirectPreloadThirdRoute - ParamsPsNamedIndexRoute: typeof ParamsPsNamedIndexRoute - ParamsPsWildcardIndexRoute: typeof ParamsPsWildcardIndexRoute -} - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/(group)': { - id: '/(group)' - path: '/' - fullPath: '/' - preLoaderRoute: typeof groupRouteImport - parentRoute: typeof rootRouteImport - } - '/대한민국': { - id: '/대한민국' - path: '/대한민국' - fullPath: '/대한민국' - preLoaderRoute: typeof Char45824Char54620Char48124Char44397RouteImport - parentRoute: typeof rootRouteImport - } - '/remountDeps': { - id: '/remountDeps' - path: '/remountDeps' - fullPath: '/remountDeps' - preLoaderRoute: typeof RemountDepsRouteImport - parentRoute: typeof rootRouteImport - } - '/posts': { - id: '/posts' - path: '/posts' - fullPath: '/posts' - preLoaderRoute: typeof PostsRouteImport - parentRoute: typeof rootRouteImport - } - '/notRemountDeps': { - id: '/notRemountDeps' - path: '/notRemountDeps' - fullPath: '/notRemountDeps' - preLoaderRoute: typeof NotRemountDepsRouteImport - parentRoute: typeof rootRouteImport - } - '/editing-b': { - id: '/editing-b' - path: '/editing-b' - fullPath: '/editing-b' - preLoaderRoute: typeof EditingBRouteImport - parentRoute: typeof rootRouteImport - } - '/editing-a': { - id: '/editing-a' - path: '/editing-a' - fullPath: '/editing-a' - preLoaderRoute: typeof EditingARouteImport - parentRoute: typeof rootRouteImport - } - '/component-types-test': { - id: '/component-types-test' - path: '/component-types-test' - fullPath: '/component-types-test' - preLoaderRoute: typeof ComponentTypesTestRouteImport - parentRoute: typeof rootRouteImport - } - '/anchor': { - id: '/anchor' - path: '/anchor' - fullPath: '/anchor' - preLoaderRoute: typeof AnchorRouteImport - parentRoute: typeof rootRouteImport - } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof LayoutRouteImport - parentRoute: typeof rootRouteImport - } - '/search-params': { - id: '/search-params' - path: '/search-params' - fullPath: '/search-params' - preLoaderRoute: typeof SearchParamsRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/non-nested': { - id: '/non-nested' - path: '/non-nested' - fullPath: '/non-nested' - preLoaderRoute: typeof NonNestedRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/': { - id: '/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof IndexRouteImport - parentRoute: typeof rootRouteImport - } - '/search-params/': { - id: '/search-params/' - path: '/' - fullPath: '/search-params/' - preLoaderRoute: typeof SearchParamsIndexRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/relative/': { - id: '/relative/' - path: '/relative' - fullPath: '/relative' - preLoaderRoute: typeof RelativeIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/redirect/': { - id: '/redirect/' - path: '/redirect' - fullPath: '/redirect' - preLoaderRoute: typeof RedirectIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/posts/': { - id: '/posts/' - path: '/' - fullPath: '/posts/' - preLoaderRoute: typeof PostsIndexRouteImport - parentRoute: typeof PostsRoute - } - '/params-ps/': { - id: '/params-ps/' - path: '/params-ps' - fullPath: '/params-ps' - preLoaderRoute: typeof ParamsPsIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/structural-sharing/$enabled': { - id: '/structural-sharing/$enabled' - path: '/structural-sharing/$enabled' - fullPath: '/structural-sharing/$enabled' - preLoaderRoute: typeof StructuralSharingEnabledRouteImport - parentRoute: typeof rootRouteImport - } - '/search-params/default': { - id: '/search-params/default' - path: '/default' - fullPath: '/search-params/default' - preLoaderRoute: typeof SearchParamsDefaultRouteImport - parentRoute: typeof SearchParamsRouteRoute - } - '/redirect/$target': { - id: '/redirect/$target' - path: '/redirect/$target' - fullPath: '/redirect/$target' - preLoaderRoute: typeof RedirectTargetRouteImport - parentRoute: typeof rootRouteImport - } - '/posts/$postId': { - id: '/posts/$postId' - path: '/$postId' - fullPath: '/posts/$postId' - preLoaderRoute: typeof PostsPostIdRouteImport - parentRoute: typeof PostsRoute - } - '/non-nested/baz': { - id: '/non-nested/baz' - path: '/baz' - fullPath: '/non-nested/baz' - preLoaderRoute: typeof NonNestedBazRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/_layout/_layout-2': { - id: '/_layout/_layout-2' - path: '' - fullPath: '' - preLoaderRoute: typeof LayoutLayout2RouteImport - parentRoute: typeof LayoutRoute - } - '/(group)/lazyinside': { - id: '/(group)/lazyinside' - path: '/lazyinside' - fullPath: '/lazyinside' - preLoaderRoute: typeof groupLazyinsideRouteImport - parentRoute: typeof groupRoute - } - '/(group)/inside': { - id: '/(group)/inside' - path: '/inside' - fullPath: '/inside' - preLoaderRoute: typeof groupInsideRouteImport - parentRoute: typeof groupRoute - } - '/(group)/_layout': { - id: '/(group)/_layout' - path: '/' - fullPath: '/' - preLoaderRoute: typeof groupLayoutRouteImport - parentRoute: typeof groupRoute - } - '/(another-group)/onlyrouteinside': { - id: '/(another-group)/onlyrouteinside' - path: '/onlyrouteinside' - fullPath: '/onlyrouteinside' - preLoaderRoute: typeof anotherGroupOnlyrouteinsideRouteImport - parentRoute: typeof rootRouteImport - } - '/relative/useNavigate': { - id: '/relative/useNavigate' - path: '/relative/useNavigate' - fullPath: '/relative/useNavigate' - preLoaderRoute: typeof RelativeUseNavigateRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/relative/link': { - id: '/relative/link' - path: '/relative/link' - fullPath: '/relative/link' - preLoaderRoute: typeof RelativeLinkRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/non-nested': { - id: '/params-ps/non-nested' - path: '/params-ps/non-nested' - fullPath: '/params-ps/non-nested' - preLoaderRoute: typeof ParamsPsNonNestedRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/redirect/$target/': { - id: '/redirect/$target/' - path: '/' - fullPath: '/redirect/$target/' - preLoaderRoute: typeof RedirectTargetIndexRouteImport - parentRoute: typeof RedirectTargetRoute - } - '/params-ps/wildcard/': { - id: '/params-ps/wildcard/' - path: '/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' - preLoaderRoute: typeof ParamsPsNamedIndexRouteImport - parentRoute: typeof rootRouteImport - } - '/relative/useNavigate/relative-useNavigate-b': { - id: '/relative/useNavigate/relative-useNavigate-b' - path: '/relative-useNavigate-b' - fullPath: '/relative/useNavigate/relative-useNavigate-b' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateBRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/useNavigate/relative-useNavigate-a': { - id: '/relative/useNavigate/relative-useNavigate-a' - path: '/relative-useNavigate-a' - fullPath: '/relative/useNavigate/relative-useNavigate-a' - preLoaderRoute: typeof RelativeUseNavigateRelativeUseNavigateARouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/link/relative-link-b': { - id: '/relative/link/relative-link-b' - path: '/relative-link-b' - fullPath: '/relative/link/relative-link-b' - preLoaderRoute: typeof RelativeLinkRelativeLinkBRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/relative-link-a': { - id: '/relative/link/relative-link-a' - path: '/relative-link-a' - fullPath: '/relative/link/relative-link-a' - preLoaderRoute: typeof RelativeLinkRelativeLinkARouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/redirect/preload/third': { - id: '/redirect/preload/third' - path: '/redirect/preload/third' - fullPath: '/redirect/preload/third' - preLoaderRoute: typeof RedirectPreloadThirdRouteImport - parentRoute: typeof rootRouteImport - } - '/redirect/preload/second': { - id: '/redirect/preload/second' - path: '/redirect/preload/second' - fullPath: '/redirect/preload/second' - preLoaderRoute: typeof RedirectPreloadSecondRouteImport - parentRoute: typeof rootRouteImport - } - '/redirect/preload/first': { - id: '/redirect/preload/first' - path: '/redirect/preload/first' - fullPath: '/redirect/preload/first' - preLoaderRoute: typeof RedirectPreloadFirstRouteImport - parentRoute: typeof rootRouteImport - } - '/redirect/$target/via-loader': { - id: '/redirect/$target/via-loader' - path: '/via-loader' - fullPath: '/redirect/$target/via-loader' - preLoaderRoute: typeof RedirectTargetViaLoaderRouteImport - parentRoute: typeof RedirectTargetRoute - } - '/redirect/$target/via-beforeLoad': { - id: '/redirect/$target/via-beforeLoad' - path: '/via-beforeLoad' - fullPath: '/redirect/$target/via-beforeLoad' - preLoaderRoute: typeof RedirectTargetViaBeforeLoadRouteImport - parentRoute: typeof RedirectTargetRoute - } - '/posts_/$postId/edit': { - id: '/posts_/$postId/edit' - path: '/posts/$postId/edit' - fullPath: '/posts/$postId/edit' - preLoaderRoute: typeof PostsPostIdEditRouteImport - parentRoute: typeof rootRouteImport - } - '/params/single/$value': { - id: '/params/single/$value' - path: '/params/single/$value' - fullPath: '/params/single/$value' - preLoaderRoute: typeof ParamsSingleValueRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/wildcard/{$}suffix': { - id: '/params-ps/wildcard/{$}suffix' - path: '/params-ps/wildcard/{$}suffix' - fullPath: '/params-ps/wildcard/{$}suffix' - preLoaderRoute: typeof ParamsPsWildcardChar123Char125suffixRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/wildcard/prefix{$}': { - id: '/params-ps/wildcard/prefix{$}' - path: '/params-ps/wildcard/prefix{$}' - fullPath: '/params-ps/wildcard/prefix{$}' - preLoaderRoute: typeof ParamsPsWildcardPrefixChar123Char125RouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/wildcard/$': { - id: '/params-ps/wildcard/$' - path: '/params-ps/wildcard/$' - fullPath: '/params-ps/wildcard/$' - preLoaderRoute: typeof ParamsPsWildcardSplatRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/named/{$foo}suffix': { - id: '/params-ps/named/{$foo}suffix' - path: '/params-ps/named/{$foo}suffix' - fullPath: '/params-ps/named/{$foo}suffix' - preLoaderRoute: typeof ParamsPsNamedChar123fooChar125suffixRouteImport - parentRoute: typeof rootRouteImport - } - '/params-ps/named/prefix{$foo}': { - id: '/params-ps/named/prefix{$foo}' - path: '/params-ps/named/prefix{$foo}' - fullPath: '/params-ps/named/prefix{$foo}' - preLoaderRoute: typeof ParamsPsNamedPrefixChar123fooChar125RouteImport - parentRoute: typeof rootRouteImport - } - '/non-nested/baz/$bazid': { - id: '/non-nested/baz/$bazid' - path: '/$bazid' - fullPath: '/non-nested/baz/$bazid' - preLoaderRoute: typeof NonNestedBazBazidRouteImport - parentRoute: typeof NonNestedBazRoute - } - '/_layout/_layout-2/layout-b': { - id: '/_layout/_layout-2/layout-b' - path: '/layout-b' - fullPath: '/layout-b' - preLoaderRoute: typeof LayoutLayout2LayoutBRouteImport - parentRoute: typeof LayoutLayout2Route - } - '/_layout/_layout-2/layout-a': { - id: '/_layout/_layout-2/layout-a' - path: '/layout-a' - fullPath: '/layout-a' - preLoaderRoute: typeof LayoutLayout2LayoutARouteImport - parentRoute: typeof LayoutLayout2Route - } - '/(group)/subfolder/inside': { - id: '/(group)/subfolder/inside' - path: '/subfolder/inside' - fullPath: '/subfolder/inside' - preLoaderRoute: typeof groupSubfolderInsideRouteImport - parentRoute: typeof groupRoute - } - '/(group)/_layout/insidelayout': { - id: '/(group)/_layout/insidelayout' - path: '/insidelayout' - fullPath: '/insidelayout' - preLoaderRoute: typeof groupLayoutInsidelayoutRouteImport - parentRoute: typeof groupLayoutRoute - } - '/params-ps/non-nested/$foo_': { - id: '/params-ps/non-nested/$foo_' - path: '/$foo' - fullPath: '/params-ps/non-nested/$foo' - preLoaderRoute: typeof ParamsPsNonNestedFooRouteRouteImport - parentRoute: typeof ParamsPsNonNestedRouteRoute - } - '/params-ps/named/$foo': { - id: '/params-ps/named/$foo' - path: '/params-ps/named/$foo' - fullPath: '/params-ps/named/$foo' - preLoaderRoute: typeof ParamsPsNamedFooRouteRouteImport - parentRoute: typeof rootRouteImport - } - '/relative/useNavigate/with-search/': { - id: '/relative/useNavigate/with-search/' - path: '/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' - preLoaderRoute: typeof RelativeUseNavigatePathIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/useNavigate/nested/': { - id: '/relative/useNavigate/nested/' - path: '/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' - preLoaderRoute: typeof RelativeLinkWithSearchIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/path/': { - id: '/relative/link/path/' - path: '/path' - fullPath: '/relative/link/path' - preLoaderRoute: typeof RelativeLinkPathIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/relative/link/nested/': { - id: '/relative/link/nested/' - path: '/nested' - fullPath: '/relative/link/nested' - preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/params-ps/non-nested/$foo_/$bar': { - id: '/params-ps/non-nested/$foo_/$bar' - path: '/$bar' - fullPath: '/params-ps/non-nested/$foo/$bar' - preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport - parentRoute: typeof ParamsPsNonNestedFooRouteRoute - } - '/non-nested/baz_/$bazid/edit': { - id: '/non-nested/baz_/$bazid/edit' - path: '/baz/$bazid/edit' - fullPath: '/non-nested/baz/$bazid/edit' - preLoaderRoute: typeof NonNestedBazBazidEditRouteImport - parentRoute: typeof NonNestedRouteRoute - } - '/params-ps/named/$foo/$bar': { - id: '/params-ps/named/$foo/$bar' - path: '/$bar' - fullPath: '/params-ps/named/$foo/$bar' - preLoaderRoute: typeof ParamsPsNamedFooBarRouteRouteImport - parentRoute: typeof ParamsPsNamedFooRouteRoute - } - '/relative/useNavigate/path/$path/': { - id: '/relative/useNavigate/path/$path/' - path: '/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' - preLoaderRoute: typeof RelativeUseNavigateNestedDeepIndexRouteImport - parentRoute: typeof RelativeUseNavigateRouteRoute - } - '/relative/link/path/$path/': { - id: '/relative/link/path/$path/' - path: '/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' - preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport - parentRoute: typeof RelativeLinkRouteRoute - } - '/params-ps/named/$foo/$bar/$baz': { - id: '/params-ps/named/$foo/$bar/$baz' - path: '/$baz' - fullPath: '/params-ps/named/$foo/$bar/$baz' - preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport - parentRoute: typeof ParamsPsNamedFooBarRouteRoute - } - } -} - -interface NonNestedBazRouteChildren { - NonNestedBazBazidRoute: typeof NonNestedBazBazidRoute -} - -const NonNestedBazRouteChildren: NonNestedBazRouteChildren = { - NonNestedBazBazidRoute: NonNestedBazBazidRoute, -} - -const NonNestedBazRouteWithChildren = NonNestedBazRoute._addFileChildren( - NonNestedBazRouteChildren, -) - -interface NonNestedRouteRouteChildren { - NonNestedBazRoute: typeof NonNestedBazRouteWithChildren - NonNestedBazBazidEditRoute: typeof NonNestedBazBazidEditRoute -} - -const NonNestedRouteRouteChildren: NonNestedRouteRouteChildren = { - NonNestedBazRoute: NonNestedBazRouteWithChildren, - NonNestedBazBazidEditRoute: NonNestedBazBazidEditRoute, -} - -const NonNestedRouteRouteWithChildren = NonNestedRouteRoute._addFileChildren( - NonNestedRouteRouteChildren, -) - -interface SearchParamsRouteRouteChildren { - SearchParamsDefaultRoute: typeof SearchParamsDefaultRoute - SearchParamsIndexRoute: typeof SearchParamsIndexRoute -} - -const SearchParamsRouteRouteChildren: SearchParamsRouteRouteChildren = { - SearchParamsDefaultRoute: SearchParamsDefaultRoute, - SearchParamsIndexRoute: SearchParamsIndexRoute, -} - -const SearchParamsRouteRouteWithChildren = - SearchParamsRouteRoute._addFileChildren(SearchParamsRouteRouteChildren) - -interface LayoutLayout2RouteChildren { - LayoutLayout2LayoutARoute: typeof LayoutLayout2LayoutARoute - LayoutLayout2LayoutBRoute: typeof LayoutLayout2LayoutBRoute -} - -const LayoutLayout2RouteChildren: LayoutLayout2RouteChildren = { - LayoutLayout2LayoutARoute: LayoutLayout2LayoutARoute, - LayoutLayout2LayoutBRoute: LayoutLayout2LayoutBRoute, -} - -const LayoutLayout2RouteWithChildren = LayoutLayout2Route._addFileChildren( - LayoutLayout2RouteChildren, -) - -interface LayoutRouteChildren { - LayoutLayout2Route: typeof LayoutLayout2RouteWithChildren -} - -const LayoutRouteChildren: LayoutRouteChildren = { - LayoutLayout2Route: LayoutLayout2RouteWithChildren, -} - -const LayoutRouteWithChildren = - LayoutRoute._addFileChildren(LayoutRouteChildren) - -interface PostsRouteChildren { - PostsPostIdRoute: typeof PostsPostIdRoute - PostsIndexRoute: typeof PostsIndexRoute -} - -const PostsRouteChildren: PostsRouteChildren = { - PostsPostIdRoute: PostsPostIdRoute, - PostsIndexRoute: PostsIndexRoute, -} - -const PostsRouteWithChildren = PostsRoute._addFileChildren(PostsRouteChildren) - -interface ParamsPsNonNestedFooRouteRouteChildren { - ParamsPsNonNestedFooBarRoute: typeof ParamsPsNonNestedFooBarRoute -} - -const ParamsPsNonNestedFooRouteRouteChildren: ParamsPsNonNestedFooRouteRouteChildren = - { - ParamsPsNonNestedFooBarRoute: ParamsPsNonNestedFooBarRoute, - } - -const ParamsPsNonNestedFooRouteRouteWithChildren = - ParamsPsNonNestedFooRouteRoute._addFileChildren( - ParamsPsNonNestedFooRouteRouteChildren, - ) - -interface ParamsPsNonNestedRouteRouteChildren { - ParamsPsNonNestedFooRouteRoute: typeof ParamsPsNonNestedFooRouteRouteWithChildren -} - -const ParamsPsNonNestedRouteRouteChildren: ParamsPsNonNestedRouteRouteChildren = - { - ParamsPsNonNestedFooRouteRoute: ParamsPsNonNestedFooRouteRouteWithChildren, - } - -const ParamsPsNonNestedRouteRouteWithChildren = - ParamsPsNonNestedRouteRoute._addFileChildren( - ParamsPsNonNestedRouteRouteChildren, - ) - -interface RelativeLinkRouteRouteChildren { - RelativeLinkRelativeLinkARoute: typeof RelativeLinkRelativeLinkARoute - RelativeLinkRelativeLinkBRoute: typeof RelativeLinkRelativeLinkBRoute - RelativeLinkNestedIndexRoute: typeof RelativeLinkNestedIndexRoute - RelativeLinkPathIndexRoute: typeof RelativeLinkPathIndexRoute - RelativeLinkWithSearchIndexRoute: typeof RelativeLinkWithSearchIndexRoute - RelativeLinkNestedDeepIndexRoute: typeof RelativeLinkNestedDeepIndexRoute - RelativeLinkPathPathIndexRoute: typeof RelativeLinkPathPathIndexRoute -} - -const RelativeLinkRouteRouteChildren: RelativeLinkRouteRouteChildren = { - RelativeLinkRelativeLinkARoute: RelativeLinkRelativeLinkARoute, - RelativeLinkRelativeLinkBRoute: RelativeLinkRelativeLinkBRoute, - RelativeLinkNestedIndexRoute: RelativeLinkNestedIndexRoute, - RelativeLinkPathIndexRoute: RelativeLinkPathIndexRoute, - RelativeLinkWithSearchIndexRoute: RelativeLinkWithSearchIndexRoute, - RelativeLinkNestedDeepIndexRoute: RelativeLinkNestedDeepIndexRoute, - RelativeLinkPathPathIndexRoute: RelativeLinkPathPathIndexRoute, -} - -const RelativeLinkRouteRouteWithChildren = - RelativeLinkRouteRoute._addFileChildren(RelativeLinkRouteRouteChildren) - -interface RelativeUseNavigateRouteRouteChildren { - RelativeUseNavigateRelativeUseNavigateARoute: typeof RelativeUseNavigateRelativeUseNavigateARoute - RelativeUseNavigateRelativeUseNavigateBRoute: typeof RelativeUseNavigateRelativeUseNavigateBRoute - RelativeUseNavigateNestedIndexRoute: typeof RelativeUseNavigateNestedIndexRoute - RelativeUseNavigatePathIndexRoute: typeof RelativeUseNavigatePathIndexRoute - RelativeUseNavigateWithSearchIndexRoute: typeof RelativeUseNavigateWithSearchIndexRoute - RelativeUseNavigateNestedDeepIndexRoute: typeof RelativeUseNavigateNestedDeepIndexRoute - RelativeUseNavigatePathPathIndexRoute: typeof RelativeUseNavigatePathPathIndexRoute -} - -const RelativeUseNavigateRouteRouteChildren: RelativeUseNavigateRouteRouteChildren = - { - RelativeUseNavigateRelativeUseNavigateARoute: - RelativeUseNavigateRelativeUseNavigateARoute, - RelativeUseNavigateRelativeUseNavigateBRoute: - RelativeUseNavigateRelativeUseNavigateBRoute, - RelativeUseNavigateNestedIndexRoute: RelativeUseNavigateNestedIndexRoute, - RelativeUseNavigatePathIndexRoute: RelativeUseNavigatePathIndexRoute, - RelativeUseNavigateWithSearchIndexRoute: - RelativeUseNavigateWithSearchIndexRoute, - RelativeUseNavigateNestedDeepIndexRoute: - RelativeUseNavigateNestedDeepIndexRoute, - RelativeUseNavigatePathPathIndexRoute: - RelativeUseNavigatePathPathIndexRoute, - } - -const RelativeUseNavigateRouteRouteWithChildren = - RelativeUseNavigateRouteRoute._addFileChildren( - RelativeUseNavigateRouteRouteChildren, - ) - -interface groupLayoutRouteChildren { - groupLayoutInsidelayoutRoute: typeof groupLayoutInsidelayoutRoute -} - -const groupLayoutRouteChildren: groupLayoutRouteChildren = { - groupLayoutInsidelayoutRoute: groupLayoutInsidelayoutRoute, -} - -const groupLayoutRouteWithChildren = groupLayoutRoute._addFileChildren( - groupLayoutRouteChildren, -) - -interface groupRouteChildren { - groupLayoutRoute: typeof groupLayoutRouteWithChildren - groupInsideRoute: typeof groupInsideRoute - groupLazyinsideRoute: typeof groupLazyinsideRoute - groupSubfolderInsideRoute: typeof groupSubfolderInsideRoute -} - -const groupRouteChildren: groupRouteChildren = { - groupLayoutRoute: groupLayoutRouteWithChildren, - groupInsideRoute: groupInsideRoute, - groupLazyinsideRoute: groupLazyinsideRoute, - groupSubfolderInsideRoute: groupSubfolderInsideRoute, -} - -const groupRouteWithChildren = groupRoute._addFileChildren(groupRouteChildren) - -interface RedirectTargetRouteChildren { - RedirectTargetViaBeforeLoadRoute: typeof RedirectTargetViaBeforeLoadRoute - RedirectTargetViaLoaderRoute: typeof RedirectTargetViaLoaderRoute - RedirectTargetIndexRoute: typeof RedirectTargetIndexRoute -} - -const RedirectTargetRouteChildren: RedirectTargetRouteChildren = { - RedirectTargetViaBeforeLoadRoute: RedirectTargetViaBeforeLoadRoute, - RedirectTargetViaLoaderRoute: RedirectTargetViaLoaderRoute, - RedirectTargetIndexRoute: RedirectTargetIndexRoute, -} - -const RedirectTargetRouteWithChildren = RedirectTargetRoute._addFileChildren( - RedirectTargetRouteChildren, -) - -interface ParamsPsNamedFooBarRouteRouteChildren { - ParamsPsNamedFooBarBazRoute: typeof ParamsPsNamedFooBarBazRoute -} - -const ParamsPsNamedFooBarRouteRouteChildren: ParamsPsNamedFooBarRouteRouteChildren = - { - ParamsPsNamedFooBarBazRoute: ParamsPsNamedFooBarBazRoute, - } - -const ParamsPsNamedFooBarRouteRouteWithChildren = - ParamsPsNamedFooBarRouteRoute._addFileChildren( - ParamsPsNamedFooBarRouteRouteChildren, - ) - -interface ParamsPsNamedFooRouteRouteChildren { - ParamsPsNamedFooBarRouteRoute: typeof ParamsPsNamedFooBarRouteRouteWithChildren -} - -const ParamsPsNamedFooRouteRouteChildren: ParamsPsNamedFooRouteRouteChildren = { - ParamsPsNamedFooBarRouteRoute: ParamsPsNamedFooBarRouteRouteWithChildren, -} - -const ParamsPsNamedFooRouteRouteWithChildren = - ParamsPsNamedFooRouteRoute._addFileChildren( - ParamsPsNamedFooRouteRouteChildren, - ) - -const rootRouteChildren: RootRouteChildren = { - IndexRoute: IndexRoute, - NonNestedRouteRoute: NonNestedRouteRouteWithChildren, - SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren, - LayoutRoute: LayoutRouteWithChildren, - AnchorRoute: AnchorRoute, - ComponentTypesTestRoute: ComponentTypesTestRoute, - EditingARoute: EditingARoute, - EditingBRoute: EditingBRoute, - NotRemountDepsRoute: NotRemountDepsRoute, - PostsRoute: PostsRouteWithChildren, - RemountDepsRoute: RemountDepsRoute, - Char45824Char54620Char48124Char44397Route: - Char45824Char54620Char48124Char44397Route, - ParamsPsNonNestedRouteRoute: ParamsPsNonNestedRouteRouteWithChildren, - RelativeLinkRouteRoute: RelativeLinkRouteRouteWithChildren, - RelativeUseNavigateRouteRoute: RelativeUseNavigateRouteRouteWithChildren, - anotherGroupOnlyrouteinsideRoute: anotherGroupOnlyrouteinsideRoute, - groupRoute: groupRouteWithChildren, - RedirectTargetRoute: RedirectTargetRouteWithChildren, - StructuralSharingEnabledRoute: StructuralSharingEnabledRoute, - ParamsPsIndexRoute: ParamsPsIndexRoute, - RedirectIndexRoute: RedirectIndexRoute, - RelativeIndexRoute: RelativeIndexRoute, - ParamsPsNamedFooRouteRoute: ParamsPsNamedFooRouteRouteWithChildren, - ParamsPsNamedPrefixChar123fooChar125Route: - ParamsPsNamedPrefixChar123fooChar125Route, - ParamsPsNamedChar123fooChar125suffixRoute: - ParamsPsNamedChar123fooChar125suffixRoute, - ParamsPsWildcardSplatRoute: ParamsPsWildcardSplatRoute, - ParamsPsWildcardPrefixChar123Char125Route: - ParamsPsWildcardPrefixChar123Char125Route, - ParamsPsWildcardChar123Char125suffixRoute: - ParamsPsWildcardChar123Char125suffixRoute, - ParamsSingleValueRoute: ParamsSingleValueRoute, - PostsPostIdEditRoute: PostsPostIdEditRoute, - RedirectPreloadFirstRoute: RedirectPreloadFirstRoute, - RedirectPreloadSecondRoute: RedirectPreloadSecondRoute, - RedirectPreloadThirdRoute: RedirectPreloadThirdRoute, - ParamsPsNamedIndexRoute: ParamsPsNamedIndexRoute, - ParamsPsWildcardIndexRoute: ParamsPsWildcardIndexRoute, -} -export const routeTree = rootRouteImport - ._addFileChildren(rootRouteChildren) - ._addFileTypes() diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx new file mode 100644 index 00000000000..619bcffd844 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( + <> +
+ Hello "/optional-params/consecutive/-$id/-$slug/$category/info"! +
+
+ params:{' '} + + {JSON.stringify(params)} + +
+ + ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx new file mode 100644 index 00000000000..f815366a0c3 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx @@ -0,0 +1,298 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+

optional path params

+ +
+ +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx new file mode 100644 index 00000000000..892e0f9b7fd --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx @@ -0,0 +1,16 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params/simple/{-$id}/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + + return ( +
+ Hello "/optional-params/simple/-$id/"! + {JSON.stringify(params)} +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx new file mode 100644 index 00000000000..dbd23271375 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params/simple/{-$id}/path')({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/simple/-$id/path"! + {JSON.stringify(params)} +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/single/path.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/single/path.tsx new file mode 100644 index 00000000000..ffb74c5909e --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/single/path.tsx @@ -0,0 +1,13 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params/single/path')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+ Hello "/optional-params/single/path"! +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/single/{-$id}.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/single/{-$id}.tsx new file mode 100644 index 00000000000..a4b98cf46fe --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/single/{-$id}.tsx @@ -0,0 +1,17 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params/single/{-$id}')({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( + <> +
+ Hello "/optional-params/single/-$id"! +
+ {JSON.stringify(params)} + + ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx new file mode 100644 index 00000000000..2f430ec248e --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx @@ -0,0 +1,17 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/optional-params/withIndex/{-$id}/$category/', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/withIndex/-$id/$category/"! + {JSON.stringify(params)} +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx new file mode 100644 index 00000000000..7002333956b --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx @@ -0,0 +1,17 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/optional-params/withIndex/{-$id}/$category/path/', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/withIndex/-$id/$category/path"! + {JSON.stringify(params)} +
+ ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx new file mode 100644 index 00000000000..6b32b244a06 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx @@ -0,0 +1,26 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + + return ( + <> +
+ Hello + "/optional-params/withRequiredInBetween/-$id/$category/path/$slug"! +
+
+ params:{' '} + + {JSON.stringify(params)} + +
+ + ) +} diff --git a/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx new file mode 100644 index 00000000000..d276a2cc0e1 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute( + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( + <> +
+ Hello "/optional-params/-$id/$category/-$slug/info"! +
+
+ params:{' '} + + {JSON.stringify(params)} + +
+ + ) +} diff --git a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts new file mode 100644 index 00000000000..a56db5e99cd --- /dev/null +++ b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts @@ -0,0 +1,477 @@ +import { expect, test } from '@playwright/test' + +test.beforeEach(async ({ page }) => { + await page.goto('/') +}) + +test.describe('ensure paths with optional params are resolved correctly', () => { + test('singular optional param usage', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const singleIndexLink = page.getByTestId('l-to-single-index') + const singleIdIndexLink = page.getByTestId('l-to-single-id-index') + const singlePathLink = page.getByTestId('l-to-single-path') + + await expect(singleIndexLink).toHaveAttribute( + 'href', + '/optional-params/single', + ) + await expect(singleIdIndexLink).toHaveAttribute( + 'href', + '/optional-params/single/id', + ) + await expect(singlePathLink).toHaveAttribute( + 'href', + '/optional-params/single/path', + ) + + await singleIndexLink.click() + + await page.waitForURL('/optional-params/single') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/single') + await expect(page.getByTestId('single-id-heading')).toBeVisible() + expect(await page.getByTestId('single-id-params').innerText()).toEqual( + JSON.stringify({}), + ) + + await singleIdIndexLink.click() + await page.waitForURL('/optional-params/single/id') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/single/id') + await expect(page.getByTestId('single-id-heading')).toBeVisible() + expect(await page.getByTestId('single-id-params').innerText()).toEqual( + JSON.stringify({ id: 'id' }), + ) + + await singlePathLink.click() + await page.waitForURL('/optional-params/single/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/single/path') + await expect(page.getByTestId('single-path-heading')).toBeVisible() + }) + + test('simple optional param usage', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const simpleIndexLink = page.getByTestId('l-to-simple-index') + const simpleIdIndexLink = page.getByTestId('l-to-simple-id-index') + const simplePathLink = page.getByTestId('l-to-simple-path') + const simpleIdPathLink = page.getByTestId('l-to-simple-id-path') + + await expect(simpleIndexLink).toHaveAttribute( + 'href', + '/optional-params/simple', + ) + await expect(simpleIdIndexLink).toHaveAttribute( + 'href', + '/optional-params/simple/id', + ) + await expect(simplePathLink).toHaveAttribute( + 'href', + '/optional-params/simple/path', + ) + await expect(simpleIdPathLink).toHaveAttribute( + 'href', + '/optional-params/simple/id/path', + ) + + await simpleIndexLink.click() + await page.waitForURL('/optional-params/simple') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple') + await expect(page.getByTestId('simple-index-heading')).toBeVisible() + expect(await page.getByTestId('simple-index-params').innerText()).toEqual( + JSON.stringify({}), + ) + + await simpleIdIndexLink.click() + await page.waitForURL('/optional-params/simple/id') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/id') + await expect(page.getByTestId('simple-index-heading')).toBeVisible() + expect(await page.getByTestId('simple-index-params').innerText()).toEqual( + JSON.stringify({ id: 'id' }), + ) + + await simplePathLink.click() + await page.waitForURL('/optional-params/simple/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/path') + await expect(page.getByTestId('simple-path-heading')).toBeVisible() + expect(await page.getByTestId('simple-path-params').innerText()).toEqual( + JSON.stringify({}), + ) + + await simpleIdPathLink.click() + await page.waitForURL('/optional-params/simple/id/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/id/path') + await expect(page.getByTestId('simple-path-heading')).toBeVisible() + expect(await page.getByTestId('simple-path-params').innerText()).toEqual( + JSON.stringify({ id: 'id' }), + ) + }) + + test('with index pages, path params and optional params', async ({ + page, + }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withIndexCategoryLink = page.getByTestId( + 'l-to-withIndex-category-index', + ) + const withIndexIdCategoryLink = page.getByTestId( + 'l-to-withIndex-id-category-index', + ) + const withIndexCategoryPathLink = page.getByTestId( + 'l-to-withIndex-category-path', + ) + const withIndexIdCategoryPathLink = page.getByTestId( + 'l-to-withIndex-id-category-path', + ) + + await expect(withIndexCategoryLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/category', + ) + await expect(withIndexIdCategoryLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/id/category', + ) + await expect(withIndexCategoryPathLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/category/path', + ) + await expect(withIndexIdCategoryPathLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/id/category/path', + ) + + await withIndexCategoryLink.click() + await page.waitForURL('/optional-params/withIndex/category') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/category') + await expect(page.getByTestId('withIndex-index-heading')).toBeVisible() + expect( + await page.getByTestId('withIndex-index-params').innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withIndexIdCategoryLink.click() + await page.waitForURL('/optional-params/withIndex/id/category') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/id/category') + await expect(page.getByTestId('withIndex-index-heading')).toBeVisible() + expect( + await page.getByTestId('withIndex-index-params').innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withIndexCategoryPathLink.click() + await page.waitForURL('/optional-params/withIndex/category/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/category/path') + await expect(page.getByTestId('withIndex-path-heading')).toBeVisible() + expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( + JSON.stringify({ category: 'category' }), + ) + + await withIndexIdCategoryPathLink.click() + await page.waitForURL('/optional-params/withIndex/id/category/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/id/category/path') + await expect(page.getByTestId('withIndex-path-heading')).toBeVisible() + expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( + JSON.stringify({ id: 'id', category: 'category' }), + ) + }) + + test('with consecutive optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-consecutive-category-info', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-consecutive-id-category-info', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-consecutive-slug-category-info', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-consecutive-id-slug-category-info', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/category/info', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/id/category/info', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/slug/category/info', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/id/slug/category/info', + ) + + await withNoOptionalsLink.click() + await page.waitForURL('/optional-params/consecutive/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeVisible() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL('/optional-params/consecutive/id/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/id/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeVisible() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL('/optional-params/consecutive/slug/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/slug/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeVisible() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).not.toEqual(JSON.stringify({ slug: 'slug', category: 'category' })) + + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'slug', category: 'category' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL('/optional-params/consecutive/id/slug/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/consecutive/id/slug/category/info', + ) + await expect(page.getByTestId('consecutive-heading')).toBeVisible() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', slug: 'slug', category: 'category' })) + }) + + test('with required path between optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-withRequiredInBetween-category', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-withRequiredInBetween-id-category', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-withRequiredInBetween-category-slug', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-withRequiredInBetween-id-category-slug', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/category/path', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/id/category/path', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/category/path/slug', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + + await withNoOptionalsLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/category/path', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/category/path', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeVisible() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/id/category/path', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/id/category/path', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeVisible() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/category/path/slug', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/category/path/slug', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeVisible() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category', slug: 'slug' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeVisible() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category', slug: 'slug' })) + }) + + test('with required params between optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-withRequiredParam-category', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-withRequiredParam-id-category', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-withRequiredParam-category-slug', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-withRequiredParam-id-category-slug', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/category/info', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/id/category/info', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/category/slug/info', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/id/category/slug/info', + ) + + await withNoOptionalsLink.click() + await page.waitForURL('/optional-params/withRequiredParam/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/category/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL('/optional-params/withRequiredParam/id/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/id/category/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredParam/category/slug/info', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/category/slug/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).not.toEqual(JSON.stringify({ category: 'category', slug: 'slug' })) + + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'category', category: 'slug' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredParam/id/category/slug/info', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/id/category/slug/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category', slug: 'slug' })) + }) +}) 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 23092968648..51e8d259347 100644 --- a/e2e/solid-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/solid-router/basic-file-based/src/routeTree.gen.ts @@ -20,6 +20,7 @@ import { Route as ComponentTypesTestRouteImport } from './routes/component-types import { Route as AnchorRouteImport } from './routes/anchor' import { Route as LayoutRouteImport } from './routes/_layout' import { Route as SearchParamsRouteRouteImport } from './routes/search-params/route' +import { Route as OptionalParamsRouteRouteImport } from './routes/optional-params/route' import { Route as NonNestedRouteRouteImport } from './routes/non-nested/route' import { Route as IndexRouteImport } from './routes/index' import { Route as SearchParamsIndexRouteImport } from './routes/search-params/index' @@ -64,14 +65,21 @@ import { Route as RelativeUseNavigateNestedIndexRouteImport } from './routes/rel import { Route as RelativeLinkWithSearchIndexRouteImport } from './routes/relative/link/with-search/index' import { Route as RelativeLinkPathIndexRouteImport } from './routes/relative/link/path/index' import { Route as RelativeLinkNestedIndexRouteImport } from './routes/relative/link/nested/index' +import { Route as OptionalParamsSimpleChar123IdChar125IndexRouteImport } from './routes/optional-params/simple/{-$id}.index' import { Route as ParamsPsNonNestedFooBarRouteImport } from './routes/params-ps/non-nested/$foo_/$bar' +import { Route as OptionalParamsSimpleChar123IdChar125PathRouteImport } from './routes/optional-params/simple/{-$id}.path' import { Route as NonNestedBazBazidEditRouteImport } from './routes/non-nested/baz_.$bazid.edit' import { Route as ParamsPsNamedFooBarRouteRouteImport } from './routes/params-ps/named/$foo/$bar.route' import { Route as RelativeUseNavigatePathPathIndexRouteImport } from './routes/relative/useNavigate/path/$path/index' import { Route as RelativeUseNavigateNestedDeepIndexRouteImport } from './routes/relative/useNavigate/nested/deep/index' import { Route as RelativeLinkPathPathIndexRouteImport } from './routes/relative/link/path/$path/index' import { Route as RelativeLinkNestedDeepIndexRouteImport } from './routes/relative/link/nested/deep/index' +import { Route as OptionalParamsWithIndexChar123IdChar125CategoryIndexRouteImport } from './routes/optional-params/withIndex/{-$id}.$category.index' import { Route as ParamsPsNamedFooBarBazRouteImport } from './routes/params-ps/named/$foo/$bar.$baz' +import { Route as OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRouteImport } from './routes/optional-params/withIndex/{-$id}.$category.path.index' +import { Route as OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRouteImport } from './routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info' +import { Route as OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125RouteImport } from './routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}' +import { Route as OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRouteImport } from './routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info' const groupRouteImport = createFileRoute('/(group)')() @@ -123,6 +131,11 @@ const SearchParamsRouteRoute = SearchParamsRouteRouteImport.update({ path: '/search-params', getParentRoute: () => rootRouteImport, } as any) +const OptionalParamsRouteRoute = OptionalParamsRouteRouteImport.update({ + id: '/optional-params', + path: '/optional-params', + getParentRoute: () => rootRouteImport, +} as any) const NonNestedRouteRoute = NonNestedRouteRouteImport.update({ id: '/non-nested', path: '/non-nested', @@ -355,11 +368,23 @@ const RelativeLinkNestedIndexRoute = RelativeLinkNestedIndexRouteImport.update({ path: '/nested/', getParentRoute: () => RelativeLinkRouteRoute, } as any) +const OptionalParamsSimpleChar123IdChar125IndexRoute = + OptionalParamsSimpleChar123IdChar125IndexRouteImport.update({ + id: '/simple/{-$id}/', + path: '/simple/{-$id}/', + getParentRoute: () => OptionalParamsRouteRoute, + } as any) const ParamsPsNonNestedFooBarRoute = ParamsPsNonNestedFooBarRouteImport.update({ id: '/$bar', path: '/$bar', getParentRoute: () => ParamsPsNonNestedFooRouteRoute, } as any) +const OptionalParamsSimpleChar123IdChar125PathRoute = + OptionalParamsSimpleChar123IdChar125PathRouteImport.update({ + id: '/simple/{-$id}/path', + path: '/simple/{-$id}/path', + getParentRoute: () => OptionalParamsRouteRoute, + } as any) const NonNestedBazBazidEditRoute = NonNestedBazBazidEditRouteImport.update({ id: '/baz_/$bazid/edit', path: '/baz/$bazid/edit', @@ -395,15 +420,52 @@ const RelativeLinkNestedDeepIndexRoute = path: '/nested/deep/', getParentRoute: () => RelativeLinkRouteRoute, } as any) +const OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute = + OptionalParamsWithIndexChar123IdChar125CategoryIndexRouteImport.update({ + id: '/withIndex/{-$id}/$category/', + path: '/withIndex/{-$id}/$category/', + getParentRoute: () => OptionalParamsRouteRoute, + } as any) const ParamsPsNamedFooBarBazRoute = ParamsPsNamedFooBarBazRouteImport.update({ id: '/$baz', path: '/$baz', getParentRoute: () => ParamsPsNamedFooBarRouteRoute, } as any) +const OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute = + OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRouteImport.update({ + id: '/withIndex/{-$id}/$category/path/', + path: '/withIndex/{-$id}/$category/path/', + getParentRoute: () => OptionalParamsRouteRoute, + } as any) +const OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute = + OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRouteImport.update( + { + id: '/withRequiredParam/{-$id}/$category/{-$slug}/info', + path: '/withRequiredParam/{-$id}/$category/{-$slug}/info', + getParentRoute: () => OptionalParamsRouteRoute, + } as any, + ) +const OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route = + OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125RouteImport.update( + { + id: '/withRequiredInBetween/{-$id}/$category/path/{-$slug}', + path: '/withRequiredInBetween/{-$id}/$category/path/{-$slug}', + getParentRoute: () => OptionalParamsRouteRoute, + } as any, + ) +const OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute = + OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRouteImport.update( + { + id: '/consecutive/{-$id}/{-$slug}/$category/info', + path: '/consecutive/{-$id}/{-$slug}/$category/info', + getParentRoute: () => OptionalParamsRouteRoute, + } as any, + ) export interface FileRoutesByFullPath { '/': typeof groupLayoutRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren + '/optional-params': typeof OptionalParamsRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren '/anchor': typeof AnchorRoute '/component-types-test': typeof ComponentTypesTestRoute @@ -448,7 +510,9 @@ export interface FileRoutesByFullPath { '/redirect/$target/': typeof RedirectTargetIndexRoute '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/baz/$bazid/edit': typeof NonNestedBazBazidEditRoute + '/optional-params/simple/{-$id}/path': typeof OptionalParamsSimpleChar123IdChar125PathRoute '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute + '/optional-params/simple/{-$id}': typeof OptionalParamsSimpleChar123IdChar125IndexRoute '/relative/link/nested': typeof RelativeLinkNestedIndexRoute '/relative/link/path': typeof RelativeLinkPathIndexRoute '/relative/link/with-search': typeof RelativeLinkWithSearchIndexRoute @@ -456,14 +520,20 @@ export interface FileRoutesByFullPath { '/relative/useNavigate/path': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search': typeof RelativeUseNavigateWithSearchIndexRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute + '/optional-params/withIndex/{-$id}/$category': typeof OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info': typeof OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}': typeof OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info': typeof OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute + '/optional-params/withIndex/{-$id}/$category/path': typeof OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute } export interface FileRoutesByTo { '/': typeof groupLayoutRouteWithChildren '/non-nested': typeof NonNestedRouteRouteWithChildren + '/optional-params': typeof OptionalParamsRouteRouteWithChildren '/anchor': typeof AnchorRoute '/component-types-test': typeof ComponentTypesTestRoute '/editing-a': typeof EditingARoute @@ -505,7 +575,9 @@ export interface FileRoutesByTo { '/redirect/$target': typeof RedirectTargetIndexRoute '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/baz/$bazid/edit': typeof NonNestedBazBazidEditRoute + '/optional-params/simple/{-$id}/path': typeof OptionalParamsSimpleChar123IdChar125PathRoute '/params-ps/non-nested/$foo/$bar': typeof ParamsPsNonNestedFooBarRoute + '/optional-params/simple/{-$id}': typeof OptionalParamsSimpleChar123IdChar125IndexRoute '/relative/link/nested': typeof RelativeLinkNestedIndexRoute '/relative/link/path': typeof RelativeLinkPathIndexRoute '/relative/link/with-search': typeof RelativeLinkWithSearchIndexRoute @@ -513,15 +585,21 @@ export interface FileRoutesByTo { '/relative/useNavigate/path': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search': typeof RelativeUseNavigateWithSearchIndexRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute + '/optional-params/withIndex/{-$id}/$category': typeof OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute '/relative/link/nested/deep': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path': typeof RelativeUseNavigatePathPathIndexRoute + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info': typeof OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}': typeof OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info': typeof OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute + '/optional-params/withIndex/{-$id}/$category/path': typeof OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/non-nested': typeof NonNestedRouteRouteWithChildren + '/optional-params': typeof OptionalParamsRouteRouteWithChildren '/search-params': typeof SearchParamsRouteRouteWithChildren '/_layout': typeof LayoutRouteWithChildren '/anchor': typeof AnchorRoute @@ -570,7 +648,9 @@ export interface FileRoutesById { '/redirect/$target/': typeof RedirectTargetIndexRoute '/params-ps/named/$foo/$bar': typeof ParamsPsNamedFooBarRouteRouteWithChildren '/non-nested/baz_/$bazid/edit': typeof NonNestedBazBazidEditRoute + '/optional-params/simple/{-$id}/path': typeof OptionalParamsSimpleChar123IdChar125PathRoute '/params-ps/non-nested/$foo_/$bar': typeof ParamsPsNonNestedFooBarRoute + '/optional-params/simple/{-$id}/': typeof OptionalParamsSimpleChar123IdChar125IndexRoute '/relative/link/nested/': typeof RelativeLinkNestedIndexRoute '/relative/link/path/': typeof RelativeLinkPathIndexRoute '/relative/link/with-search/': typeof RelativeLinkWithSearchIndexRoute @@ -578,16 +658,22 @@ export interface FileRoutesById { '/relative/useNavigate/path/': typeof RelativeUseNavigatePathIndexRoute '/relative/useNavigate/with-search/': typeof RelativeUseNavigateWithSearchIndexRoute '/params-ps/named/$foo/$bar/$baz': typeof ParamsPsNamedFooBarBazRoute + '/optional-params/withIndex/{-$id}/$category/': typeof OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute '/relative/link/nested/deep/': typeof RelativeLinkNestedDeepIndexRoute '/relative/link/path/$path/': typeof RelativeLinkPathPathIndexRoute '/relative/useNavigate/nested/deep/': typeof RelativeUseNavigateNestedDeepIndexRoute '/relative/useNavigate/path/$path/': typeof RelativeUseNavigatePathPathIndexRoute + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info': typeof OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}': typeof OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info': typeof OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute + '/optional-params/withIndex/{-$id}/$category/path/': typeof OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' | '/non-nested' + | '/optional-params' | '/search-params' | '/anchor' | '/component-types-test' @@ -632,7 +718,9 @@ export interface FileRouteTypes { | '/redirect/$target/' | '/params-ps/named/$foo/$bar' | '/non-nested/baz/$bazid/edit' + | '/optional-params/simple/{-$id}/path' | '/params-ps/non-nested/$foo/$bar' + | '/optional-params/simple/{-$id}' | '/relative/link/nested' | '/relative/link/path' | '/relative/link/with-search' @@ -640,14 +728,20 @@ export interface FileRouteTypes { | '/relative/useNavigate/path' | '/relative/useNavigate/with-search' | '/params-ps/named/$foo/$bar/$baz' + | '/optional-params/withIndex/{-$id}/$category' | '/relative/link/nested/deep' | '/relative/link/path/$path' | '/relative/useNavigate/nested/deep' | '/relative/useNavigate/path/$path' + | '/optional-params/consecutive/{-$id}/{-$slug}/$category/info' + | '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + | '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info' + | '/optional-params/withIndex/{-$id}/$category/path' fileRoutesByTo: FileRoutesByTo to: | '/' | '/non-nested' + | '/optional-params' | '/anchor' | '/component-types-test' | '/editing-a' @@ -689,7 +783,9 @@ export interface FileRouteTypes { | '/redirect/$target' | '/params-ps/named/$foo/$bar' | '/non-nested/baz/$bazid/edit' + | '/optional-params/simple/{-$id}/path' | '/params-ps/non-nested/$foo/$bar' + | '/optional-params/simple/{-$id}' | '/relative/link/nested' | '/relative/link/path' | '/relative/link/with-search' @@ -697,14 +793,20 @@ export interface FileRouteTypes { | '/relative/useNavigate/path' | '/relative/useNavigate/with-search' | '/params-ps/named/$foo/$bar/$baz' + | '/optional-params/withIndex/{-$id}/$category' | '/relative/link/nested/deep' | '/relative/link/path/$path' | '/relative/useNavigate/nested/deep' | '/relative/useNavigate/path/$path' + | '/optional-params/consecutive/{-$id}/{-$slug}/$category/info' + | '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + | '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info' + | '/optional-params/withIndex/{-$id}/$category/path' id: | '__root__' | '/' | '/non-nested' + | '/optional-params' | '/search-params' | '/_layout' | '/anchor' @@ -753,7 +855,9 @@ export interface FileRouteTypes { | '/redirect/$target/' | '/params-ps/named/$foo/$bar' | '/non-nested/baz_/$bazid/edit' + | '/optional-params/simple/{-$id}/path' | '/params-ps/non-nested/$foo_/$bar' + | '/optional-params/simple/{-$id}/' | '/relative/link/nested/' | '/relative/link/path/' | '/relative/link/with-search/' @@ -761,15 +865,21 @@ export interface FileRouteTypes { | '/relative/useNavigate/path/' | '/relative/useNavigate/with-search/' | '/params-ps/named/$foo/$bar/$baz' + | '/optional-params/withIndex/{-$id}/$category/' | '/relative/link/nested/deep/' | '/relative/link/path/$path/' | '/relative/useNavigate/nested/deep/' | '/relative/useNavigate/path/$path/' + | '/optional-params/consecutive/{-$id}/{-$slug}/$category/info' + | '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + | '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info' + | '/optional-params/withIndex/{-$id}/$category/path/' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute NonNestedRouteRoute: typeof NonNestedRouteRouteWithChildren + OptionalParamsRouteRoute: typeof OptionalParamsRouteRouteWithChildren SearchParamsRouteRoute: typeof SearchParamsRouteRouteWithChildren LayoutRoute: typeof LayoutRouteWithChildren AnchorRoute: typeof AnchorRoute @@ -868,6 +978,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof SearchParamsRouteRouteImport parentRoute: typeof rootRouteImport } + '/optional-params': { + id: '/optional-params' + path: '/optional-params' + fullPath: '/optional-params' + preLoaderRoute: typeof OptionalParamsRouteRouteImport + parentRoute: typeof rootRouteImport + } '/non-nested': { id: '/non-nested' path: '/non-nested' @@ -1176,6 +1293,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RelativeLinkNestedIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } + '/optional-params/simple/{-$id}/': { + id: '/optional-params/simple/{-$id}/' + path: '/simple/{-$id}' + fullPath: '/optional-params/simple/{-$id}' + preLoaderRoute: typeof OptionalParamsSimpleChar123IdChar125IndexRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } '/params-ps/non-nested/$foo_/$bar': { id: '/params-ps/non-nested/$foo_/$bar' path: '/$bar' @@ -1183,6 +1307,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsPsNonNestedFooBarRouteImport parentRoute: typeof ParamsPsNonNestedFooRouteRoute } + '/optional-params/simple/{-$id}/path': { + id: '/optional-params/simple/{-$id}/path' + path: '/simple/{-$id}/path' + fullPath: '/optional-params/simple/{-$id}/path' + preLoaderRoute: typeof OptionalParamsSimpleChar123IdChar125PathRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } '/non-nested/baz_/$bazid/edit': { id: '/non-nested/baz_/$bazid/edit' path: '/baz/$bazid/edit' @@ -1225,6 +1356,13 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof RelativeLinkNestedDeepIndexRouteImport parentRoute: typeof RelativeLinkRouteRoute } + '/optional-params/withIndex/{-$id}/$category/': { + id: '/optional-params/withIndex/{-$id}/$category/' + path: '/withIndex/{-$id}/$category' + fullPath: '/optional-params/withIndex/{-$id}/$category' + preLoaderRoute: typeof OptionalParamsWithIndexChar123IdChar125CategoryIndexRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } '/params-ps/named/$foo/$bar/$baz': { id: '/params-ps/named/$foo/$bar/$baz' path: '/$baz' @@ -1232,6 +1370,34 @@ declare module '@tanstack/solid-router' { preLoaderRoute: typeof ParamsPsNamedFooBarBazRouteImport parentRoute: typeof ParamsPsNamedFooBarRouteRoute } + '/optional-params/withIndex/{-$id}/$category/path/': { + id: '/optional-params/withIndex/{-$id}/$category/path/' + path: '/withIndex/{-$id}/$category/path' + fullPath: '/optional-params/withIndex/{-$id}/$category/path' + preLoaderRoute: typeof OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info': { + id: '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info' + path: '/withRequiredParam/{-$id}/$category/{-$slug}/info' + fullPath: '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info' + preLoaderRoute: typeof OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}': { + id: '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + path: '/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + fullPath: '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}' + preLoaderRoute: typeof OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125RouteImport + parentRoute: typeof OptionalParamsRouteRoute + } + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info': { + id: '/optional-params/consecutive/{-$id}/{-$slug}/$category/info' + path: '/consecutive/{-$id}/{-$slug}/$category/info' + fullPath: '/optional-params/consecutive/{-$id}/{-$slug}/$category/info' + preLoaderRoute: typeof OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRouteImport + parentRoute: typeof OptionalParamsRouteRoute + } } } @@ -1261,6 +1427,36 @@ const NonNestedRouteRouteWithChildren = NonNestedRouteRoute._addFileChildren( NonNestedRouteRouteChildren, ) +interface OptionalParamsRouteRouteChildren { + OptionalParamsSimpleChar123IdChar125PathRoute: typeof OptionalParamsSimpleChar123IdChar125PathRoute + OptionalParamsSimpleChar123IdChar125IndexRoute: typeof OptionalParamsSimpleChar123IdChar125IndexRoute + OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute: typeof OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute + OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute: typeof OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute + OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route: typeof OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route + OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute: typeof OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute + OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute: typeof OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute +} + +const OptionalParamsRouteRouteChildren: OptionalParamsRouteRouteChildren = { + OptionalParamsSimpleChar123IdChar125PathRoute: + OptionalParamsSimpleChar123IdChar125PathRoute, + OptionalParamsSimpleChar123IdChar125IndexRoute: + OptionalParamsSimpleChar123IdChar125IndexRoute, + OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute: + OptionalParamsWithIndexChar123IdChar125CategoryIndexRoute, + OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute: + OptionalParamsConsecutiveChar123IdChar125Char123SlugChar125CategoryInfoRoute, + OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route: + OptionalParamsWithRequiredInBetweenChar123IdChar125CategoryPathChar123SlugChar125Route, + OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute: + OptionalParamsWithRequiredParamChar123IdChar125CategoryChar123SlugChar125InfoRoute, + OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute: + OptionalParamsWithIndexChar123IdChar125CategoryPathIndexRoute, +} + +const OptionalParamsRouteRouteWithChildren = + OptionalParamsRouteRoute._addFileChildren(OptionalParamsRouteRouteChildren) + interface SearchParamsRouteRouteChildren { SearchParamsDefaultRoute: typeof SearchParamsDefaultRoute SearchParamsIndexRoute: typeof SearchParamsIndexRoute @@ -1467,6 +1663,7 @@ const ParamsPsNamedFooRouteRouteWithChildren = const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, NonNestedRouteRoute: NonNestedRouteRouteWithChildren, + OptionalParamsRouteRoute: OptionalParamsRouteRouteWithChildren, SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren, LayoutRoute: LayoutRouteWithChildren, AnchorRoute: AnchorRoute, diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx new file mode 100644 index 00000000000..9d0e099480d --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute( + '/optional-params/consecutive/{-$id}/{-$slug}/$category/info', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( + <> +
+ Hello "/optional-params/consecutive/-$id/-$slug/$category/info"! +
+
+ params:{' '} + + {JSON.stringify(params())} + +
+ + ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/route.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/route.tsx new file mode 100644 index 00000000000..671a1658fd7 --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/route.tsx @@ -0,0 +1,262 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/optional-params')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+

optional path params

+ +
+ +
+ ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx new file mode 100644 index 00000000000..c36487edd8d --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx @@ -0,0 +1,16 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/optional-params/simple/{-$id}/')({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + + return ( +
+ Hello "/optional-params/simple/-$id/"! + {JSON.stringify(params())} +
+ ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx new file mode 100644 index 00000000000..56c9bb07385 --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx @@ -0,0 +1,15 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/optional-params/simple/{-$id}/path')({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/simple/-$id/path"! + {JSON.stringify(params())} +
+ ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx new file mode 100644 index 00000000000..891c1c50b63 --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute( + '/optional-params/withIndex/{-$id}/$category/', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/withIndex/-$id/$category/"! + + {JSON.stringify(params())} + +
+ ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx new file mode 100644 index 00000000000..856a6984a2b --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx @@ -0,0 +1,19 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute( + '/optional-params/withIndex/{-$id}/$category/path/', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( +
+ Hello "/optional-params/withIndex/-$id/$category/path"! + + {JSON.stringify(params())} + +
+ ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx new file mode 100644 index 00000000000..ccb4a831cdb --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx @@ -0,0 +1,26 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute( + '/optional-params/withRequiredInBetween/{-$id}/$category/path/{-$slug}', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + + return ( + <> +
+ Hello + "/optional-params/withRequiredInBetween/-$id/$category/path/$slug"! +
+
+ params:{' '} + + {JSON.stringify(params())} + +
+ + ) +} diff --git a/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx b/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx new file mode 100644 index 00000000000..9bca1710269 --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx @@ -0,0 +1,24 @@ +import { createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute( + '/optional-params/withRequiredParam/{-$id}/$category/{-$slug}/info', +)({ + component: RouteComponent, +}) + +function RouteComponent() { + const params = Route.useParams() + return ( + <> +
+ Hello "/optional-params/-$id/$category/-$slug/info"! +
+
+ params:{' '} + + {JSON.stringify(params())} + +
+ + ) +} diff --git a/e2e/solid-router/basic-file-based/tests/optionalParams.spec.ts b/e2e/solid-router/basic-file-based/tests/optionalParams.spec.ts new file mode 100644 index 00000000000..54a03637642 --- /dev/null +++ b/e2e/solid-router/basic-file-based/tests/optionalParams.spec.ts @@ -0,0 +1,430 @@ +import { expect, test } from '@playwright/test' + +test.beforeEach(async ({ page }) => { + await page.goto('/') +}) + +test.describe('ensure paths with optional params are resolved correctly', () => { + test('simple optional param usage', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const simpleIndexLink = page.getByTestId('l-to-simple-index') + const simpleIdIndexLink = page.getByTestId('l-to-simple-id-index') + const simplePathLink = page.getByTestId('l-to-simple-path') + const simpleIdPathLink = page.getByTestId('l-to-simple-id-path') + + await expect(simpleIndexLink).toHaveAttribute( + 'href', + '/optional-params/simple', + ) + await expect(simpleIdIndexLink).toHaveAttribute( + 'href', + '/optional-params/simple/id', + ) + await expect(simplePathLink).toHaveAttribute( + 'href', + '/optional-params/simple/path', + ) + await expect(simpleIdPathLink).toHaveAttribute( + 'href', + '/optional-params/simple/id/path', + ) + + await simpleIndexLink.click() + await page.waitForURL('/optional-params/simple') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple') + await expect(page.getByTestId('simple-index-heading')).toBeInViewport() + expect(await page.getByTestId('simple-index-params').innerText()).toEqual( + JSON.stringify({}), + ) + + await simpleIdIndexLink.click() + await page.waitForURL('/optional-params/simple/id') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/id') + await expect(page.getByTestId('simple-index-heading')).toBeInViewport() + expect(await page.getByTestId('simple-index-params').innerText()).toEqual( + JSON.stringify({ id: 'id' }), + ) + + await simplePathLink.click() + await page.waitForURL('/optional-params/simple/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/path') + await expect(page.getByTestId('simple-path-heading')).toBeInViewport() + expect(await page.getByTestId('simple-path-params').innerText()).toEqual( + JSON.stringify({}), + ) + + await simpleIdPathLink.click() + await page.waitForURL('/optional-params/simple/id/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/simple/id/path') + await expect(page.getByTestId('simple-path-heading')).toBeInViewport() + expect(await page.getByTestId('simple-path-params').innerText()).toEqual( + JSON.stringify({ id: 'id' }), + ) + }) + + test('with index pages, path params and optional params', async ({ + page, + }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withIndexCategoryLink = page.getByTestId( + 'l-to-withIndex-category-index', + ) + const withIndexIdCategoryLink = page.getByTestId( + 'l-to-withIndex-id-category-index', + ) + const withIndexCategoryPathLink = page.getByTestId( + 'l-to-withIndex-category-path', + ) + const withIndexIdCategoryPathLink = page.getByTestId( + 'l-to-withIndex-id-category-path', + ) + + await expect(withIndexCategoryLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/category', + ) + await expect(withIndexIdCategoryLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/id/category', + ) + await expect(withIndexCategoryPathLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/category/path', + ) + await expect(withIndexIdCategoryPathLink).toHaveAttribute( + 'href', + '/optional-params/withIndex/id/category/path', + ) + + await withIndexCategoryLink.click() + await page.waitForURL('/optional-params/withIndex/category') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/category') + await expect(page.getByTestId('withIndex-index-heading')).toBeInViewport() + expect( + await page.getByTestId('withIndex-index-params').innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withIndexIdCategoryLink.click() + await page.waitForURL('/optional-params/withIndex/id/category') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/id/category') + await expect(page.getByTestId('withIndex-index-heading')).toBeInViewport() + expect( + await page.getByTestId('withIndex-index-params').innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withIndexCategoryPathLink.click() + await page.waitForURL('/optional-params/withIndex/category/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/category/path') + await expect(page.getByTestId('withIndex-path-heading')).toBeInViewport() + expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( + JSON.stringify({ category: 'category' }), + ) + + await withIndexIdCategoryPathLink.click() + await page.waitForURL('/optional-params/withIndex/id/category/path') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/withIndex/id/category/path') + await expect(page.getByTestId('withIndex-path-heading')).toBeInViewport() + expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( + JSON.stringify({ id: 'id', category: 'category' }), + ) + }) + + test('with consecutive optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-consecutive-category-info', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-consecutive-id-category-info', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-consecutive-slug-category-info', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-consecutive-id-slug-category-info', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/category/info', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/id/category/info', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/slug/category/info', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/consecutive/id/slug/category/info', + ) + + await withNoOptionalsLink.click() + await page.waitForURL('/optional-params/consecutive/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeInViewport() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL('/optional-params/consecutive/id/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/id/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeInViewport() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL('/optional-params/consecutive/slug/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe('/optional-params/consecutive/slug/category/info') + await expect(page.getByTestId('consecutive-heading')).toBeInViewport() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).not.toEqual(JSON.stringify({ slug: 'slug', category: 'category' })) + + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'slug', category: 'category' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL('/optional-params/consecutive/id/slug/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/consecutive/id/slug/category/info', + ) + await expect(page.getByTestId('consecutive-heading')).toBeInViewport() + expect( + await page + .getByTestId('consecutive-id-slug-category-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', slug: 'slug', category: 'category' })) + }) + + test('with required path between optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-withRequiredInBetween-category', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-withRequiredInBetween-id-category', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-withRequiredInBetween-category-slug', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-withRequiredInBetween-id-category-slug', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/category/path', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/id/category/path', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/category/path/slug', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + + await withNoOptionalsLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/category/path', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/category/path', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeInViewport() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/id/category/path', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/id/category/path', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeInViewport() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/category/path/slug', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/category/path/slug', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeInViewport() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category', slug: 'slug' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredInBetween/id/category/path/slug', + ) + await expect( + page.getByTestId('withRequiredInBetween-heading'), + ).toBeInViewport() + expect( + await page + .getByTestId('withRequiredInBetween-id-category-path-slug-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category', slug: 'slug' })) + }) + + test('with required params between optional params', async ({ page }) => { + await page.goto('/optional-params') + let pagePathname = '' + + const withNoOptionalsLink = page.getByTestId( + 'l-to-withRequiredParam-category', + ) + const withOptionalIdLink = page.getByTestId( + 'l-to-withRequiredParam-id-category', + ) + const withOptionalSlugLink = page.getByTestId( + 'l-to-withRequiredParam-category-slug', + ) + const withOptionalIdSlugLink = page.getByTestId( + 'l-to-withRequiredParam-id-category-slug', + ) + + await expect(withNoOptionalsLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/category/info', + ) + await expect(withOptionalIdLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/id/category/info', + ) + await expect(withOptionalSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/category/slug/info', + ) + await expect(withOptionalIdSlugLink).toHaveAttribute( + 'href', + '/optional-params/withRequiredParam/id/category/slug/info', + ) + + await withNoOptionalsLink.click() + await page.waitForURL('/optional-params/withRequiredParam/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/category/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ category: 'category' })) + + await withOptionalIdLink.click() + await page.waitForURL('/optional-params/withRequiredParam/id/category/info') + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/id/category/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) + + await withOptionalSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredParam/category/slug/info', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/category/slug/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).not.toEqual(JSON.stringify({ category: 'category', slug: 'slug' })) + + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'category', category: 'slug' })) + + await withOptionalIdSlugLink.click() + await page.waitForURL( + '/optional-params/withRequiredParam/id/category/slug/info', + ) + pagePathname = new URL(page.url()).pathname + expect(pagePathname).toBe( + '/optional-params/withRequiredParam/id/category/slug/info', + ) + await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + expect( + await page + .getByTestId('withRequiredParam-id-category-slug-info-params') + .innerText(), + ).toEqual(JSON.stringify({ id: 'id', category: 'category', slug: 'slug' })) + }) +}) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 617685a10bf..6a47ec4e60c 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -611,6 +611,11 @@ function isMatch( ): boolean { let baseIndex = 0 let routeIndex = 0 + let processedOptionals = 0 + + const optionalCount = routeSegments.filter( + (seg) => seg.type === SEGMENT_TYPE_OPTIONAL_PARAM, + ).length while (baseIndex < baseSegments.length || routeIndex < routeSegments.length) { const baseSegment = baseSegments[baseIndex] @@ -754,12 +759,16 @@ function isMatch( // Optional parameters can be missing - don't fail the match if (!baseSegment) { // No base segment for optional param - skip this route segment + processedOptionals++ + routeIndex++ continue } if (baseSegment.value === '/') { // Skip slash segments for optional params + processedOptionals++ + routeIndex++ continue } @@ -796,33 +805,111 @@ function isMatch( // For optional params without prefix/suffix, we need to check if the current // base segment should match this optional param or a later route segment - // Look ahead to see if there's a later route segment that matches the current base segment let shouldMatchOptional = true + const remainingOptionals = optionalCount - processedOptionals - 1 > 0 + + // consider last route segment might be index route and any prior optionals that was not matched + const remainingRouteSegmentLength = + (routeSegments.slice(-1)[0]?.value === '/' + ? routeSegments.length - 1 + : routeSegments.length) - routeIndex + + const remainingRequiredRouteSegmentCount = + remainingRouteSegmentLength - (optionalCount - processedOptionals) + const remainingBaseSegmentCount = baseSegments.length - baseIndex + + // Look ahead to see if there's a later route segment that matches the current base segment for ( let lookAhead = routeIndex + 1; lookAhead < routeSegments.length; lookAhead++ ) { const futureRouteSegment = routeSegments[lookAhead] - if ( - futureRouteSegment?.type === SEGMENT_TYPE_PATHNAME && - futureRouteSegment.value === baseSegment.value - ) { - // The current base segment matches a future pathname segment, - // so we should skip this optional parameter - shouldMatchOptional = false + + // where the next segment is a required path name, we can break early. + // either the current base segment matches a future pathname segment, + // in which case we should skip this optional parameter, + // or the url is invalid + if (futureRouteSegment?.type === SEGMENT_TYPE_PATHNAME) { + if ( + caseSensitive + ? futureRouteSegment.value === baseSegment.value + : futureRouteSegment.value.toLowerCase() === + baseSegment.value.toLowerCase() + ) { + // so we should skip this optional parameter + shouldMatchOptional = false + } + break + } + + // where consecutive optional params are used, we can break early. + // preference is given to the first optional param + if (futureRouteSegment?.type === SEGMENT_TYPE_OPTIONAL_PARAM) { + if ( + remainingRequiredRouteSegmentCount >= remainingBaseSegmentCount + ) { + shouldMatchOptional = false + } break } - // If we encounter a required param or wildcard, stop looking ahead + // this if is not required for the rest of the logic, but it's useful to know what type of future segment we're looking at' if ( futureRouteSegment?.type === SEGMENT_TYPE_PARAM || futureRouteSegment?.type === SEGMENT_TYPE_WILDCARD ) { - if (baseSegments.length < routeSegments.length) { - shouldMatchOptional = false + const followingRouteSegment = routeSegments[lookAhead + 1] + + let isMatchedFurtherDown = false + + // since we know there are remaining optionals, we look to the segment following the next. + // if further segments are required paths, then we can possibly match further optionals based on the url pattern. to do this, we match the remaining paths + // if all that follows are wildcards/required params and/or optionals params we continue matching on a first-case basis, and optionals further down are unmatched. + if ( + remainingOptionals && + followingRouteSegment && + (followingRouteSegment.type === SEGMENT_TYPE_PATHNAME || + (followingRouteSegment.type === SEGMENT_TYPE_OPTIONAL_PARAM && + (followingRouteSegment.prefixSegment || + followingRouteSegment.suffixSegment))) + ) { + const remainingRouteSegments = routeSegments.slice( + lookAhead + 1, + ) + + const remainingRouteSegmentLength = + remainingRouteSegments.slice(-1)[0]?.value === '/' + ? remainingRouteSegments.length - 1 + : remainingRouteSegments.length + + const remainingBaseSegments = baseSegments.slice(baseIndex + 1) + + isMatchedFurtherDown = + remainingRouteSegmentLength === + remainingBaseSegments.length && + isMatch( + remainingBaseSegments, + remainingRouteSegments, + { ...params }, + fuzzy, + caseSensitive, + ) + } + + if ( + !remainingOptionals || + // remaining required segments equals remaining base segments + remainingRequiredRouteSegmentCount === + remainingBaseSegmentCount || + // matched by probe further down + isMatchedFurtherDown + ) { + if (remainingBaseSegmentCount < remainingRouteSegmentLength) { + shouldMatchOptional = false + } + break } - break } } @@ -838,6 +925,8 @@ function isMatch( baseIndex++ } + processedOptionals++ + routeIndex++ continue } diff --git a/packages/router-core/tests/match-by-path.test.ts b/packages/router-core/tests/match-by-path.test.ts index bb886cf7540..db3e3b32827 100644 --- a/packages/router-core/tests/match-by-path.test.ts +++ b/packages/router-core/tests/match-by-path.test.ts @@ -52,10 +52,15 @@ describe('default path matching', () => { ['/a/1', '/a/{-$id}', { id: '1' }], ['/a', '/a/{-$id}', {}], ['/a/1/b', '/a/{-$id}/b', { id: '1' }], + ['/a/1/b', '/a/{-$id}/b/', { id: '1' }], ['/a/b', '/a/{-$id}/b', {}], + ['/a/b', '/a/{-$id}/b/', {}], ['/a/1/b/2', '/a/{-$id}/b/{-$other}', { id: '1', other: '2' }], + ['/a/1/b/2', '/a/{-$id}/b/{-$other}/', { id: '1', other: '2' }], ['/a/b/2', '/a/{-$id}/b/{-$other}', { other: '2' }], + ['/a/b/2', '/a/{-$id}/b/{-$other}/', { other: '2' }], ['/a/1/b', '/a/{-$id}/b/{-$other}', { id: '1' }], + ['/a/1/b', '/a/{-$id}/b/{-$other}/', { id: '1' }], ['/a/b', '/a/{-$id}/b/{-$other}', {}], ['/a/1/b/2', '/a/{-$id}/b/{-$id}', { id: '2' }], ])('optional %s => %s', (from, to, result) => { @@ -74,6 +79,213 @@ describe('default path matching', () => { matchByPath('/', from, { to, caseSensitive: true, fuzzy: false }), ).toEqual(result) }) + + it.each([ + ['/a/1/b/2', '/a/{-$id}/b/$other', { id: '1', other: '2' }], + ['/a/1/b/2', '/a/{-$id}/b/$other/', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/a/{-$id}/b/$other/c', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/a/{-$id}/b/$other/c/', { id: '1', other: '2' }], + [ + '/a/1/b/2/c/3', + '/a/{-$id}/b/$other/c/$d', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/a/{-$id}/b/$other/c/$d/', + { id: '1', other: '2', d: '3' }, + ], + + ['/a/1/2/3/c', '/a/{-$id}/$other/{-$d}/c', { id: '1', other: '2', d: '3' }], + [ + '/a/1/2/3/c', + '/a/{-$id}/$other/{-$d}/c/', + { id: '1', other: '2', d: '3' }, + ], + ['/a/2', '/a/{-$id}/$other/{-$d}', { other: '2' }], + ['/a/2', '/a/{-$id}/$other/{-$d}/', { other: '2' }], + ['/a/2/c', '/a/{-$id}/$other/{-$d}/c', { other: '2' }], + ['/a/2/c', '/a/{-$id}/$other/{-$d}/c/', { other: '2' }], + ['/a/1/2', '/a/{-$id}/$other/{-$d}', { id: '1', other: '2' }], + ['/a/1/2', '/a/{-$id}/$other/{-$d}/', { id: '1', other: '2' }], + ['/a/2/e', '/a/{-$id}/$other/{-$d}/$e', { other: '2', e: 'e' }], + ['/a/2/e', '/a/{-$id}/$other/{-$d}/$e/', { other: '2', e: 'e' }], + ['/a/1/2/e', '/a/{-$id}/$other/{-$d}/$e', { id: '1', other: '2', e: 'e' }], + ['/a/1/2/e', '/a/{-$id}/$other/{-$d}/$e/', { id: '1', other: '2', e: 'e' }], + [ + '/a/1/2/d/e', + '/a/{-$id}/$other/{-$d}/$e', + { id: '1', other: '2', d: 'd', e: 'e' }, + ], + [ + '/a/1/2/d/e', + '/a/{-$id}/$other/{-$d}/$e/', + { id: '1', other: '2', d: 'd', e: 'e' }, + ], + ['/a/1/2/c', '/a/{-$id}/$other/{-$d}/c', { id: '1', other: '2' }], + ['/a/1/2/c', '/a/{-$id}/$other/{-$d}/c/', { id: '1', other: '2' }], + ['/a/1/2/c', '/a/{-$id}/$other/c/{-$d}', { id: '1', other: '2' }], + ['/a/1/2/c', '/a/{-$id}/$other/c/{-$d}/', { id: '1', other: '2' }], + ['/a/1/2/c/3', '/a/{-$id}/$other/c/{-$d}', { id: '1', other: '2', d: '3' }], + [ + '/a/1/2/c/3', + '/a/{-$id}/$other/c/{-$d}/', + { id: '1', other: '2', d: '3' }, + ], + ['/a/2/c/3', '/a/{-$id}/$other/c/{-$d}', { other: '2', d: '3' }], + ['/a/2/c/3', '/a/{-$id}/$other/c/{-$d}/', { other: '2', d: '3' }], + ['/a/2/c', '/a/{-$id}/$other/c/{-$d}', { other: '2' }], + ['/a/2/c', '/a/{-$id}/$other/c/{-$d}/', { other: '2' }], + ['/a/1/2/c', '/a/{-$id}/$other/$c/{-$d}', { id: '1', other: '2', c: 'c' }], + ['/a/1/2/c', '/a/{-$id}/$other/$c/{-$d}/', { id: '1', other: '2', c: 'c' }], + [ + '/a/1/2/c/3', + '/a/{-$id}/$other/$c/{-$d}', + { id: '1', other: '2', c: 'c', d: '3' }, + ], + [ + '/a/1/2/c/3', + '/a/{-$id}/$other/$c/{-$d}/', + { id: '1', other: '2', c: 'c', d: '3' }, + ], + [ + '/a/1/2/c/e', + '/a/{-$id}/$other/$c/e/{-$d}', + { id: '1', other: '2', c: 'c' }, + ], + [ + '/a/1/2/c/e', + '/a/{-$id}/$other/$c/e/{-$d}/', + { id: '1', other: '2', c: 'c' }, + ], + [ + '/a/1/2/c/e/3', + '/a/{-$id}/$other/$c/e/{-$d}', + { id: '1', other: '2', c: 'c', d: '3' }, + ], + [ + '/a/1/2/c/e/3', + '/a/{-$id}/$other/$c/e/{-$d}/', + { id: '1', other: '2', c: 'c', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/a/{-$id}/b/$other/c/{-$d}', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/a/{-$id}/b/$other/c/{-$d}/', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3/4', + '/a/{-$id}/b/$other/c/{-$d}/$e', + { id: '1', other: '2', d: '3', e: '4' }, + ], + [ + '/a/1/b/2/c/3/4', + '/a/{-$id}/b/$other/c/{-$d}/$e/', + { id: '1', other: '2', d: '3', e: '4' }, + ], + ['/a/b/2', '/a/{-$id}/b/$other', { other: '2' }], + ['/a/b/2', '/a/{-$id}/b/$other/', { other: '2' }], + ['/a/b/2/c', '/a/{-$id}/b/$other/c', { other: '2' }], + ['/a/b/2/c', '/a/{-$id}/b/$other/c/', { other: '2' }], + ['/a/b/2/c/3', '/a/{-$id}/b/$other/c/$d', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/a/{-$id}/b/$other/c/$d/', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/a/{-$id}/b/$other/c/{-$d}', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/a/{-$id}/b/$other/c/{-$d}/', { other: '2', d: '3' }], + [ + '/a/b/2/c/3/4', + '/a/{-$id}/b/$other/c/{-$d}/$e', + { other: '2', d: '3', e: '4' }, + ], + [ + '/a/b/2/c/3/4', + '/a/{-$id}/b/$other/c/{-$d}/$e/', + { other: '2', d: '3', e: '4' }, + ], + ['/a/1/b/2/c', '/a/{-$id}/b/$other/c/{-$d}', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/a/{-$id}/b/$other/c/{-$d}/', { id: '1', other: '2' }], + [ + '/a/1/b/2/c/4', + '/a/{-$id}/b/$other/c/{-$d}/$e', + { id: '1', other: '2', e: '4' }, + ], + [ + '/a/1/b/2/c/4', + '/a/{-$id}/b/$other/c/{-$d}/$e/', + { id: '1', other: '2', e: '4' }, + ], + ['/a/b/2/c', '/a/{-$id}/b/$other/c/{-$d}', { other: '2' }], + ['/a/b/2/c', '/a/{-$id}/b/$other/c/{-$d}/', { other: '2' }], + ['/a/b/2/c/4', '/a/{-$id}/b/$other/c/{-$d}/$e', { other: '2', e: '4' }], + ['/a/b/2/c/4', '/a/{-$id}/b/$other/c/{-$d}/$e/', { other: '2', e: '4' }], + [ + '/a/2/c/3', + '/a/{-$id}/$other/c{-$cid}/{-$d}', + { cid: '', other: '2', d: '3' }, + ], + [ + '/a/2/c4/3', + '/a/{-$id}/$other/c{-$cid}/{-$d}/', + { other: '2', cid: '4', d: '3' }, + ], + ])('complex optional usage %s => %s', (from, to, result) => { + expect( + matchByPath('/', from, { to, caseSensitive: true, fuzzy: false }), + ).toEqual(result) + }) + + it.each([ + ['/a/2', '/a/{-$id}/{-$other}', { id: '2' }], + ['/a/2', '/a/{-$id}/{-$other}/', { id: '2' }], + ['/a/2/b', '/a/{-$id}/{-$other}/b', { id: '2' }], + ['/a/2/b', '/a/{-$id}/{-$other}/b/', { id: '2' }], + ['/a/2/b/c', '/a/{-$id}/{-$other}/b/{-$d}/c', { id: '2' }], + ['/a/2/b/c', '/a/{-$id}/{-$other}/b/{-$d}/c/', { id: '2' }], + ['/a/2/b/3/c', '/a/{-$id}/{-$other}/b/{-$d}/c', { id: '2', d: '3' }], + ['/a/2/b/3/c', '/a/{-$id}/{-$other}/b/{-$d}/c/', { id: '2', d: '3' }], + [ + '/a/2/b/3/c/4', + '/a/{-$id}/{-$other}/b/{-$d}/c/$e', + { id: '2', d: '3', e: '4' }, + ], + [ + '/a/2/b/3/c/4', + '/a/{-$id}/{-$other}/b/{-$d}/c/$e/', + { id: '2', d: '3', e: '4' }, + ], + ['/a/b/c', '/a/{-$id}/{-$other}/$b/{-$d}/c', { b: 'b' }], + ['/a/b/c', '/a/{-$id}/{-$other}/$b/{-$d}/c/', { b: 'b' }], + ['/a/1/b/c', '/a/{-$id}/{-$other}/$b/{-$d}/c', { id: '1', b: 'b' }], + ['/a/1/b/c', '/a/{-$id}/{-$other}/$b/{-$d}/c/', { id: '1', b: 'b' }], + [ + '/a/1/other/b/c', + '/a/{-$id}/{-$other}/$b/{-$d}/c', + { id: '1', other: 'other', b: 'b' }, + ], + [ + '/a/1/other/b/c', + '/a/{-$id}/{-$other}/$b/{-$d}/c/', + { id: '1', other: 'other', b: 'b' }, + ], + [ + '/a/1/other/b/d/c', + '/a/{-$id}/{-$other}/$b/{-$d}/c', + { id: '1', other: 'other', b: 'b', d: 'd' }, + ], + [ + '/a/1/other/b/d/c', + '/a/{-$id}/{-$other}/$b/{-$d}/c/', + { id: '1', other: 'other', b: 'b', d: 'd' }, + ], + ])('consecutive optionals %s => %s', (from, to, result) => { + expect( + matchByPath('/', from, { to, caseSensitive: true, fuzzy: false }), + ).toEqual(result) + }) }) describe('case insensitive path matching', () => { @@ -110,11 +322,11 @@ describe('case insensitive path matching', () => { ['/a/1', '/A/{-$id}', { id: '1' }], ['/a', '/A/{-$id}', {}], ['/a/1/b', '/A/{-$id}/B', { id: '1' }], - // ['/a/b', '/A/{-$id}/B', {}], + ['/a/b', '/A/{-$id}/B', {}], ['/a/1/b/2', '/A/{-$id}/B/{-$other}', { id: '1', other: '2' }], - // ['/a/b/2', '/A/{-$id}/B/{-$other}', { other: '2' }], + ['/a/b/2', '/A/{-$id}/B/{-$other}', { other: '2' }], ['/a/1/b', '/A/{-$id}/B/{-$other}', { id: '1' }], - // ['/a/b', '/A/{-$id}/B/{-$other}', {}], + ['/a/b', '/A/{-$id}/B/{-$other}', {}], ['/a/1/b/2', '/A/{-$id}/B/{-$id}', { id: '2' }], ])('optional %s => %s', (from, to, result) => { expect( @@ -132,6 +344,81 @@ describe('case insensitive path matching', () => { matchByPath('/', from, { to, caseSensitive: false, fuzzy: false }), ).toEqual(result) }) + + it.each([ + ['/a/1/b/2', '/A/{-$id}/B/$other', { id: '1', other: '2' }], + ['/a/1/b/2', '/A/{-$id}/B/$other/', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/A/{-$id}/B/$other/C', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/A/{-$id}/B/$other/C/', { id: '1', other: '2' }], + [ + '/a/1/b/2/c/3', + '/A/{-$id}/B/$other/C/$d', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/A/{-$id}/B/$other/C/$d/', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/A/{-$id}/B/$other/C/{-$d}', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3', + '/A/{-$id}/B/$other/C/{-$d}/', + { id: '1', other: '2', d: '3' }, + ], + [ + '/a/1/b/2/c/3/4', + '/A/{-$id}/B/$other/C/{-$d}/$e', + { id: '1', other: '2', d: '3', e: '4' }, + ], + [ + '/a/1/b/2/c/3/4', + '/A/{-$id}/B/$other/C/{-$d}/$e/', + { id: '1', other: '2', d: '3', e: '4' }, + ], + ['/a/b/2', '/A/{-$id}/B/$other', { other: '2' }], + ['/a/b/2', '/A/{-$id}/B/$other/', { other: '2' }], + ['/a/b/2/c', '/A/{-$id}/B/$other/C', { other: '2' }], + ['/a/b/2/c', '/A/{-$id}/B/$other/C/', { other: '2' }], + ['/a/b/2/c/3', '/A/{-$id}/B/$other/C/$d', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/A/{-$id}/B/$other/C/$d/', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/A/{-$id}/B/$other/C/{-$d}', { other: '2', d: '3' }], + ['/a/b/2/c/3', '/A/{-$id}/B/$other/C/{-$d}/', { other: '2', d: '3' }], + [ + '/a/b/2/c/3/4', + '/A/{-$id}/B/$other/C/{-$d}/$e', + { other: '2', d: '3', e: '4' }, + ], + [ + '/a/b/2/c/3/4', + '/A/{-$id}/B/$other/C/{-$d}/$e/', + { other: '2', d: '3', e: '4' }, + ], + ['/a/1/b/2/c', '/A/{-$id}/B/$other/C/{-$d}', { id: '1', other: '2' }], + ['/a/1/b/2/c', '/A/{-$id}/B/$other/C/{-$d}/', { id: '1', other: '2' }], + [ + '/a/1/b/2/c/4', + '/A/{-$id}/B/$other/C/{-$d}/$e', + { id: '1', other: '2', e: '4' }, + ], + [ + '/a/1/b/2/c/4', + '/A/{-$id}/B/$other/C/{-$d}/$e/', + { id: '1', other: '2', e: '4' }, + ], + ['/a/b/2/c', '/A/{-$id}/B/$other/C/{-$d}', { other: '2' }], + ['/a/b/2/c', '/A/{-$id}/B/$other/C/{-$d}/', { other: '2' }], + ['/a/b/2/c/4', '/A/{-$id}/B/$other/C/{-$d}/$e', { other: '2', e: '4' }], + ['/a/b/2/c/4', '/A/{-$id}/B/$other/C/{-$d}/$e/', { other: '2', e: '4' }], + ])('complex optional usage %s => %s', (from, to, result) => { + expect( + matchByPath('/', from, { to, caseSensitive: false, fuzzy: false }), + ).toEqual(result) + }) }) describe('fuzzy path matching', () => { @@ -304,7 +591,7 @@ describe('non-nested paths', () => { ['/a/1/b', '/A_/{-$id}_/B_', { id: '1' }], ['/a/1/b/2', '/A_/{-$id}_/B_/{-$other}_', { id: '1', other: '2' }], ['/a/1/b', '/A_/{-$id}_/B_/{-$other}_', { id: '1' }], - ['/a/1/b/2', '/A_/{-$id}_/B/{-$id}_', { id: '2' }], + ['/a/1/b/2', '/A_/{-$id}_/B_/{-$id}_', { id: '2' }], ])('optional %s => %s', (from, to, result) => { expect( matchByPath('/', from, { to, caseSensitive: false, fuzzy: false }),