From 718e07d0387910f73717d5ac2a1d700b64242fda Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 00:24:40 +0200 Subject: [PATCH 01/15] add tests --- .../router-core/tests/match-by-path.test.ts | 269 +++++++++++++++++- 1 file changed, 266 insertions(+), 3 deletions(-) diff --git a/packages/router-core/tests/match-by-path.test.ts b/packages/router-core/tests/match-by-path.test.ts index bb886cf7540..fe09e0cce04 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,189 @@ 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' }, + ], + ])('consecutive optionals %s => %s', (from, to, result) => { + expect( + matchByPath('/', from, { to, caseSensitive: true, fuzzy: false }), + ).toEqual(result) + }) }) describe('case insensitive path matching', () => { @@ -110,11 +298,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 +320,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' }], + ])('optional preceding wildcard %s => %s', (from, to, result) => { + expect( + matchByPath('/', from, { to, caseSensitive: false, fuzzy: false }), + ).toEqual(result) + }) }) describe('fuzzy path matching', () => { From 148143bd9263d967195edce526de60610f35218c Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 00:25:26 +0200 Subject: [PATCH 02/15] update isMatch to test more variations for matching --- packages/router-core/src/path.ts | 102 +++++++++++++++++++++++++++---- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 617685a10bf..1d5ccd25037 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 + let skippedOptionals = 0 + const optionalCount = routeSegments.filter( + (seg) => seg.type === SEGMENT_TYPE_OPTIONAL_PARAM, + ).length while (baseIndex < baseSegments.length || routeIndex < routeSegments.length) { const baseSegment = baseSegments[baseIndex] @@ -796,33 +801,102 @@ 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 routeSegmentLength = + (routeSegments.slice(-1)[0]?.value === '/' + ? routeSegments.length - 1 + : routeSegments.length) - skippedOptionals + + // 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 and we should bail out + 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 } - // If we encounter a required param or wildcard, stop looking ahead + // 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) { + break + } + + // 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(lookAhead) + + isMatchedFurtherDown = + remainingRouteSegmentLength === + remainingBaseSegments.length && + isMatch( + remainingBaseSegments, + remainingRouteSegments, + params, + fuzzy, + caseSensitive, + ) + } + + if ( + !remainingOptionals || + // remaining length excluding remaining optionals and matched optionals + routeSegmentLength + skippedOptionals - optionalCount === + baseSegments.length || + // is matched further down + isMatchedFurtherDown + ) { + if (baseSegments.length < routeSegmentLength) { + shouldMatchOptional = false + } + break } - break } } @@ -836,8 +910,12 @@ function isMatch( if (matched) { params[routeSegment.value.substring(1)] = _paramValue baseIndex++ + } else { + skippedOptionals++ } + processedOptionals++ + routeIndex++ continue } From 677087708b1a0b04d6f674b77f3a90b03db98efb Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 00:25:46 +0200 Subject: [PATCH 03/15] update docs --- docs/router/framework/react/guide/path-params.md | 5 +++++ 1 file changed, 5 insertions(+) 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. From 435f899ec85682bc392caee78b73df1acbd83d3b Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 01:28:56 +0200 Subject: [PATCH 04/15] few more tweaks --- packages/router-core/src/path.ts | 10 ++++++-- .../router-core/tests/match-by-path.test.ts | 24 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 1d5ccd25037..8ac3443b43f 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -838,6 +838,12 @@ function isMatch( // 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 ( + routeSegmentLength - optionalCount + processedOptionals >= + baseSegments.length + ) { + shouldMatchOptional = false + } break } @@ -858,8 +864,8 @@ function isMatch( followingRouteSegment && (followingRouteSegment.type === SEGMENT_TYPE_PATHNAME || (followingRouteSegment.type === SEGMENT_TYPE_OPTIONAL_PARAM && - followingRouteSegment.prefixSegment) || - followingRouteSegment.suffixSegment) + (followingRouteSegment.prefixSegment || + followingRouteSegment.suffixSegment))) ) { const remainingRouteSegments = routeSegments.slice( lookAhead + 1, diff --git a/packages/router-core/tests/match-by-path.test.ts b/packages/router-core/tests/match-by-path.test.ts index fe09e0cce04..96ad2b00109 100644 --- a/packages/router-core/tests/match-by-path.test.ts +++ b/packages/router-core/tests/match-by-path.test.ts @@ -257,6 +257,30 @@ describe('default path matching', () => { '/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 }), From afd99a91586587f665d16753ce89dd0e97185ddb Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 03:09:47 +0200 Subject: [PATCH 05/15] add e2e tests --- .../basic-file-based/src/routeTree.gen.ts | 197 ++++++++ .../{-$id}.{-$slug}.$category.info.tsx | 24 + .../src/routes/optional-params/route.tsx | 243 ++++++++++ .../optional-params/simple/{-$id}.index.tsx | 16 + .../optional-params/simple/{-$id}.path.tsx | 15 + .../withIndex/{-$id}.$category.index.tsx | 17 + .../withIndex/{-$id}.$category.path.index.tsx | 17 + .../{-$id}.$category.path.{-$slug}.tsx | 26 ++ .../{-$id}.$category.{-$slug}.info.tsx | 24 + .../tests/optionalParams.spec.ts | 430 ++++++++++++++++++ .../basic-file-based/src/routeTree.gen.ts | 197 ++++++++ .../{-$id}.{-$slug}.$category.info.tsx | 24 + .../src/routes/optional-params/route.tsx | 243 ++++++++++ .../optional-params/simple/{-$id}.index.tsx | 16 + .../optional-params/simple/{-$id}.path.tsx | 15 + .../withIndex/{-$id}.$category.index.tsx | 19 + .../withIndex/{-$id}.$category.path.index.tsx | 19 + .../{-$id}.$category.path.{-$slug}.tsx | 26 ++ .../{-$id}.$category.{-$slug}.info.tsx | 24 + .../tests/optionalParams.spec.ts | 430 ++++++++++++++++++ 20 files changed, 2022 insertions(+) create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx create mode 100644 e2e/react-router/basic-file-based/tests/optionalParams.spec.ts create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/consecutive/{-$id}.{-$slug}.$category.info.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/route.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.index.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/simple/{-$id}.path.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.index.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/withIndex/{-$id}.$category.path.index.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredInBetween/{-$id}.$category.path.{-$slug}.tsx create mode 100644 e2e/solid-router/basic-file-based/src/routes/optional-params/withRequiredParam/{-$id}.$category.{-$slug}.info.tsx create mode 100644 e2e/solid-router/basic-file-based/tests/optionalParams.spec.ts diff --git a/e2e/react-router/basic-file-based/src/routeTree.gen.ts b/e2e/react-router/basic-file-based/src/routeTree.gen.ts index 2daaf9a821e..e181b9ea864 100644 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ b/e2e/react-router/basic-file-based/src/routeTree.gen.ts @@ -21,6 +21,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' @@ -73,14 +74,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)')() @@ -138,6 +146,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', @@ -415,11 +428,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', @@ -455,15 +480,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 @@ -517,7 +579,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 @@ -525,14 +589,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 @@ -583,7 +653,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 @@ -591,15 +663,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 @@ -657,7 +735,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 @@ -665,16 +745,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' @@ -728,7 +814,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' @@ -736,14 +824,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' @@ -794,7 +888,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' @@ -802,14 +898,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' @@ -867,7 +969,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/' @@ -875,15 +979,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 @@ -998,6 +1108,13 @@ declare module '@tanstack/react-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' @@ -1362,6 +1479,13 @@ declare module '@tanstack/react-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' @@ -1369,6 +1493,13 @@ declare module '@tanstack/react-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' @@ -1411,6 +1542,13 @@ declare module '@tanstack/react-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' @@ -1418,6 +1556,34 @@ declare module '@tanstack/react-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 + } } } @@ -1447,6 +1613,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 @@ -1653,6 +1849,7 @@ const ParamsPsNamedFooRouteRouteWithChildren = const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, NonNestedRouteRoute: NonNestedRouteRouteWithChildren, + OptionalParamsRouteRoute: OptionalParamsRouteRouteWithChildren, SearchParamsRouteRoute: SearchParamsRouteRouteWithChildren, LayoutRoute: LayoutRouteWithChildren, AnchorRoute: AnchorRoute, 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..a052eb8e5a6 --- /dev/null +++ b/e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx @@ -0,0 +1,243 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/optional-params')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+

optional path params

+
    +
    simple optional path param usage
    +
    +
  • + + /optionals/simple + {' '} +
  • +
  • + + /optionals/simple/id + {' '} +
  • +
  • + + /optionals/simple/path + {' '} +
  • +
  • + + /optionals/simple/id/path + +
  • +
    +
    +
    With path params and index page
    +
    +
  • + + /optionals/withIndex/category + {' '} +
  • +
  • + + /optionals/withIndex/id/category + {' '} +
  • +
  • + + /optionals/withIndex/category/path + {' '} +
  • +
  • + + /optionals/withIndex/id/category/path + {' '} +
  • +
    +
    +
    Consecutive path params
    +
    +
  • + + /optionals/consecutive/category/info + {' '} +
  • +
  • + + /optionals/consecutive/id1/category/info + {' '} +
  • +
  • + + /optionals/consecutive/slug/category/info + {' '} +
  • +
  • + + /optionals/consecutive/id1/slug/category/info + +
  • +
    +
    +
    Required path in between optional and path params
    +
    +
  • + + /optionals/withRequiredInBetween/category/path + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/id/category/path + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/category/path/slug + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/id/category/path/slug + +
  • +
    + +
    +
    Required Param in between optional path params
    +
    +
  • + + /optionals/withRequiredParam/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/category/slug/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/slug/info + +
  • +
    +
+
+ +
+ ) +} 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/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..b66f5f4d89b --- /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"! aaaa +
+
+ 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..54a03637642 --- /dev/null +++ b/e2e/react-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/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..246141c6b16 --- /dev/null +++ b/e2e/solid-router/basic-file-based/src/routes/optional-params/route.tsx @@ -0,0 +1,243 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/solid-router' + +export const Route = createFileRoute('/optional-params')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+

optional path params

+
    +
    simple optional path param usage
    +
    +
  • + + /optionals/simple + {' '} +
  • +
  • + + /optionals/simple/id + {' '} +
  • +
  • + + /optionals/simple/path + {' '} +
  • +
  • + + /optionals/simple/id/path + +
  • +
    +
    +
    With path params and index page
    +
    +
  • + + /optionals/withIndex/category + {' '} +
  • +
  • + + /optionals/withIndex/id/category + {' '} +
  • +
  • + + /optionals/withIndex/category/path + {' '} +
  • +
  • + + /optionals/withIndex/id/category/path + {' '} +
  • +
    +
    +
    Consecutive path params
    +
    +
  • + + /optionals/consecutive/category/info + {' '} +
  • +
  • + + /optionals/consecutive/id1/category/info + {' '} +
  • +
  • + + /optionals/consecutive/slug/category/info + {' '} +
  • +
  • + + /optionals/consecutive/id1/slug/category/info + +
  • +
    +
    +
    Required path in between optional and path params
    +
    +
  • + + /optionals/withRequiredInBetween/category/path + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/id/category/path + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/category/path/slug + {' '} +
  • +
  • + + /optionals/withRequiredInBetween/id/category/path/slug + +
  • +
    + +
    +
    Required Param in between optional path params
    +
    +
  • + + /optionals/withRequiredParam/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/category/slug/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/slug/info + +
  • +
    +
+
+ +
+ ) +} 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..a4446bacfc0 --- /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"! aaaa +
+
+ 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' })) + }) +}) From 1d760232184be77f3966c80c5832cd0ba5fec3d6 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 03:45:35 +0200 Subject: [PATCH 06/15] code rabbit suggestions --- .../src/routes/optional-params/route.tsx | 471 +++++++++--------- .../{-$id}.$category.{-$slug}.info.tsx | 2 +- .../src/routes/optional-params/route.tsx | 469 ++++++++--------- .../{-$id}.$category.{-$slug}.info.tsx | 2 +- packages/router-core/src/path.ts | 2 +- .../router-core/tests/match-by-path.test.ts | 2 +- 6 files changed, 492 insertions(+), 456 deletions(-) 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 index a052eb8e5a6..2246ed77386 100644 --- 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 @@ -9,232 +9,251 @@ function RouteComponent() {

optional path params

    -
    simple optional path param usage
    -
    -
  • - - /optionals/simple - {' '} -
  • -
  • - - /optionals/simple/id - {' '} -
  • -
  • - - /optionals/simple/path - {' '} -
  • -
  • - - /optionals/simple/id/path - -
  • -
    -
    -
    With path params and index page
    -
    -
  • - - /optionals/withIndex/category - {' '} -
  • -
  • - - /optionals/withIndex/id/category - {' '} -
  • -
  • - - /optionals/withIndex/category/path - {' '} -
  • -
  • - - /optionals/withIndex/id/category/path - {' '} -
  • -
    -
    -
    Consecutive path params
    -
    -
  • - - /optionals/consecutive/category/info - {' '} -
  • -
  • - - /optionals/consecutive/id1/category/info - {' '} -
  • -
  • - - /optionals/consecutive/slug/category/info - {' '} -
  • -
  • - - /optionals/consecutive/id1/slug/category/info - -
  • -
    -
    -
    Required path in between optional and path params
    -
    -
  • - - /optionals/withRequiredInBetween/category/path - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/id/category/path - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/category/path/slug - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/id/category/path/slug - -
  • -
    - -
    -
    Required Param in between optional path params
    -
    -
  • - - /optionals/withRequiredParam/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/category/slug/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/slug/info - -
  • -
    +
  • +
    simple optional path param usage
    +
    +
      +
    • + + /optionals/simple + {' '} +
    • +
    • + + /optionals/simple/id + {' '} +
    • +
    • + + /optionals/simple/path + {' '} +
    • +
    • + + /optionals/simple/id/path + +
    • +
    +
    +
    +
  • +
  • +
    With path params and index page
    +
    +
      +
    • + + /optionals/withIndex/category + {' '} +
    • +
    • + + /optionals/withIndex/id/category + {' '} +
    • +
    • + + /optionals/withIndex/category/path + {' '} +
    • +
    • + + /optionals/withIndex/id/category/path + {' '} +
    • +
    +
    +
    +
  • +
  • +
    Consecutive path params
    +
    +
      +
    • + + /optionals/consecutive/category/info + {' '} +
    • +
    • + + /optionals/consecutive/id1/category/info + {' '} +
    • +
    • + + /optionals/consecutive/slug/category/info + {' '} +
    • +
    • + + /optionals/consecutive/id1/slug/category/info + +
    • +
    +
    +
    +
  • +
  • +
    Required path in between optional and path params
    +
    +
      +
    • + + /optionals/withRequiredInBetween/category/path + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/id/category/path + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/category/path/slug + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/id/category/path/slug + +
    • +
    +
    +
    +
  • +
  • +
    Required Param in between optional path params
    +
    +
      +
    • + + /optionals/withRequiredParam/category/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/id1/category/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/category/slug/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/id1/category/slug/info + +
    • +
    +
    +

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 index b66f5f4d89b..d276a2cc0e1 100644 --- 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 @@ -11,7 +11,7 @@ function RouteComponent() { return ( <>
- Hello "/optional-params/-$id/$category/-$slug/info"! aaaa + Hello "/optional-params/-$id/$category/-$slug/info"!
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 index 246141c6b16..b08088d09e0 100644 --- 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 @@ -9,232 +9,249 @@ function RouteComponent() {

optional path params

    -
    simple optional path param usage
    -
    -
  • - - /optionals/simple - {' '} -
  • -
  • - - /optionals/simple/id - {' '} -
  • -
  • - - /optionals/simple/path - {' '} -
  • -
  • - - /optionals/simple/id/path - -
  • -
    -
    -
    With path params and index page
    -
    -
  • - - /optionals/withIndex/category - {' '} -
  • -
  • - - /optionals/withIndex/id/category - {' '} -
  • -
  • - - /optionals/withIndex/category/path - {' '} -
  • -
  • - - /optionals/withIndex/id/category/path - {' '} -
  • -
    -
    -
    Consecutive path params
    -
    -
  • - - /optionals/consecutive/category/info - {' '} -
  • -
  • - - /optionals/consecutive/id1/category/info - {' '} -
  • -
  • - - /optionals/consecutive/slug/category/info - {' '} -
  • -
  • - - /optionals/consecutive/id1/slug/category/info - -
  • -
    -
    -
    Required path in between optional and path params
    -
    -
  • - - /optionals/withRequiredInBetween/category/path - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/id/category/path - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/category/path/slug - {' '} -
  • -
  • - - /optionals/withRequiredInBetween/id/category/path/slug - -
  • -
    - -
    -
    Required Param in between optional path params
    -
    -
  • - - /optionals/withRequiredParam/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/category/slug/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/slug/info - -
  • -
    +
  • +
    simple optional path param usage
    +
    +
      +
    • + + /optionals/simple + {' '} +
    • +
    • + + /optionals/simple/id + {' '} +
    • +
    • + + /optionals/simple/path + {' '} +
    • +
    • + + /optionals/simple/id/path + +
    • +
    +
    +
    +
  • +
  • +
    With path params and index page
    +
    +
      +
    • + + /optionals/withIndex/category + {' '} +
    • +
    • + + /optionals/withIndex/id/category + {' '} +
    • +
    • + + /optionals/withIndex/category/path + {' '} +
    • +
    • + + /optionals/withIndex/id/category/path + {' '} +
    • +
    +
    +
    +
  • +
  • +
    Consecutive path params
    +
    +
      +
    • + + /optionals/consecutive/category/info + {' '} +
    • +
    • + + /optionals/consecutive/id1/category/info + {' '} +
    • +
    • + + /optionals/consecutive/slug/category/info + {' '} +
    • +
    • + + /optionals/consecutive/id1/slug/category/info + +
    • +
    +
    +
    +
  • +
  • +
    Required path in between optional and path params
    +
    +
      +
    • + + /optionals/withRequiredInBetween/category/path + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/id/category/path + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/category/path/slug + {' '} +
    • +
    • + + /optionals/withRequiredInBetween/id/category/path/slug + +
    • +
    +
    +
    +
  • +
  • +
    Required Param in between optional path params
    +
    +
  • + + /optionals/withRequiredParam/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/category/slug/info + {' '} +
  • +
  • + + /optionals/withRequiredParam/id1/category/slug/info + +
  • +
+
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 index a4446bacfc0..9bca1710269 100644 --- 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 @@ -11,7 +11,7 @@ function RouteComponent() { return ( <>
- Hello "/optional-params/-$id/$category/-$slug/info"! aaaa + Hello "/optional-params/-$id/$category/-$slug/info"!
params:{' '} diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 8ac3443b43f..f5dfe61072f 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -821,7 +821,7 @@ function isMatch( // 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 and we should bail out + // or the url is invalid, and we should return undefined if (futureRouteSegment?.type === SEGMENT_TYPE_PATHNAME) { if ( caseSensitive diff --git a/packages/router-core/tests/match-by-path.test.ts b/packages/router-core/tests/match-by-path.test.ts index 96ad2b00109..e6c0b86e993 100644 --- a/packages/router-core/tests/match-by-path.test.ts +++ b/packages/router-core/tests/match-by-path.test.ts @@ -591,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 }), From bae8f8bc964d676ab8417eb6aa52d62ccc3846f5 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 03:57:46 +0200 Subject: [PATCH 07/15] code rabbit suggestions --- packages/router-core/src/path.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index f5dfe61072f..40355c6fd69 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -759,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 } @@ -876,7 +880,7 @@ function isMatch( ? remainingRouteSegments.length - 1 : remainingRouteSegments.length - const remainingBaseSegments = baseSegments.slice(lookAhead) + const remainingBaseSegments = baseSegments.slice(baseIndex + 1) isMatchedFurtherDown = remainingRouteSegmentLength === @@ -884,7 +888,7 @@ function isMatch( isMatch( remainingBaseSegments, remainingRouteSegments, - params, + { ...params }, fuzzy, caseSensitive, ) From c4f784c0b1c4e8f75812b1261ef5fe81300adecf Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 04:15:28 +0200 Subject: [PATCH 08/15] fix test --- .../src/routes/optional-params/route.tsx | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) 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 index b08088d09e0..671a1658fd7 100644 --- 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 @@ -210,46 +210,48 @@ function RouteComponent() {
  • Required Param in between optional path params
    -
  • - - /optionals/withRequiredParam/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/category/slug/info - {' '} -
  • -
  • - - /optionals/withRequiredParam/id1/category/slug/info - -
  • +
      +
    • + + /optionals/withRequiredParam/category/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/id1/category/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/category/slug/info + {' '} +
    • +
    • + + /optionals/withRequiredParam/id1/category/slug/info + +
    • +
    From abd7e99330520be52c017280426c551a806c14aa Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 04:31:53 +0200 Subject: [PATCH 09/15] code rabbit suggestions --- packages/router-core/src/path.ts | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index 40355c6fd69..fddffc8af8e 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -809,10 +809,15 @@ function isMatch( 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 routeSegmentLength = (routeSegments.slice(-1)[0]?.value === '/' ? routeSegments.length - 1 - : routeSegments.length) - skippedOptionals + : routeSegments.length) - routeIndex // Look ahead to see if there's a later route segment that matches the current base segment for ( @@ -825,7 +830,7 @@ function isMatch( // 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, and we should return undefined + // or the url is invalid if (futureRouteSegment?.type === SEGMENT_TYPE_PATHNAME) { if ( caseSensitive @@ -843,8 +848,10 @@ function isMatch( // preference is given to the first optional param if (futureRouteSegment?.type === SEGMENT_TYPE_OPTIONAL_PARAM) { if ( - routeSegmentLength - optionalCount + processedOptionals >= - baseSegments.length + remainingRouteSegmentLength - + optionalCount + + processedOptionals >= + baseSegments.length - baseIndex ) { shouldMatchOptional = false } @@ -896,13 +903,17 @@ function isMatch( if ( !remainingOptionals || - // remaining length excluding remaining optionals and matched optionals - routeSegmentLength + skippedOptionals - optionalCount === - baseSegments.length || - // is matched further down + // remaining required segments equals remaining base segments + remainingRouteSegmentLength - + (optionalCount - processedOptionals) === + baseSegments.length - baseIndex || + // matched by probe further down isMatchedFurtherDown ) { - if (baseSegments.length < routeSegmentLength) { + if ( + baseSegments.length - baseIndex < + remainingRouteSegmentLength + ) { shouldMatchOptional = false } break From 91cb686a7c899a76db282e769b1bfafa007d1955 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 04:33:56 +0200 Subject: [PATCH 10/15] cleanup --- packages/router-core/src/path.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index fddffc8af8e..a8e8625fa43 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -814,11 +814,6 @@ function isMatch( ? routeSegments.length - 1 : routeSegments.length) - routeIndex - const routeSegmentLength = - (routeSegments.slice(-1)[0]?.value === '/' - ? routeSegments.length - 1 - : routeSegments.length) - routeIndex - // Look ahead to see if there's a later route segment that matches the current base segment for ( let lookAhead = routeIndex + 1; From 96e5190315266d6b930fb51d20d514be0dbfa1a4 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 04:49:27 +0200 Subject: [PATCH 11/15] nitpick cleanup --- packages/router-core/src/path.ts | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/packages/router-core/src/path.ts b/packages/router-core/src/path.ts index a8e8625fa43..6a47ec4e60c 100644 --- a/packages/router-core/src/path.ts +++ b/packages/router-core/src/path.ts @@ -612,7 +612,7 @@ function isMatch( let baseIndex = 0 let routeIndex = 0 let processedOptionals = 0 - let skippedOptionals = 0 + const optionalCount = routeSegments.filter( (seg) => seg.type === SEGMENT_TYPE_OPTIONAL_PARAM, ).length @@ -814,6 +814,10 @@ function isMatch( ? 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; @@ -843,10 +847,7 @@ function isMatch( // preference is given to the first optional param if (futureRouteSegment?.type === SEGMENT_TYPE_OPTIONAL_PARAM) { if ( - remainingRouteSegmentLength - - optionalCount + - processedOptionals >= - baseSegments.length - baseIndex + remainingRequiredRouteSegmentCount >= remainingBaseSegmentCount ) { shouldMatchOptional = false } @@ -899,16 +900,12 @@ function isMatch( if ( !remainingOptionals || // remaining required segments equals remaining base segments - remainingRouteSegmentLength - - (optionalCount - processedOptionals) === - baseSegments.length - baseIndex || + remainingRequiredRouteSegmentCount === + remainingBaseSegmentCount || // matched by probe further down isMatchedFurtherDown ) { - if ( - baseSegments.length - baseIndex < - remainingRouteSegmentLength - ) { + if (remainingBaseSegmentCount < remainingRouteSegmentLength) { shouldMatchOptional = false } break @@ -926,8 +923,6 @@ function isMatch( if (matched) { params[routeSegment.value.substring(1)] = _paramValue baseIndex++ - } else { - skippedOptionals++ } processedOptionals++ From b9337d8141cce76dffd91895a7e1586c559403e2 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 12:33:18 +0200 Subject: [PATCH 12/15] rename case-sensitive test to be aligned with non-case sensitive --- packages/router-core/tests/match-by-path.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/router-core/tests/match-by-path.test.ts b/packages/router-core/tests/match-by-path.test.ts index e6c0b86e993..db3e3b32827 100644 --- a/packages/router-core/tests/match-by-path.test.ts +++ b/packages/router-core/tests/match-by-path.test.ts @@ -414,7 +414,7 @@ describe('case insensitive path matching', () => { ['/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' }], - ])('optional preceding wildcard %s => %s', (from, to, result) => { + ])('complex optional usage %s => %s', (from, to, result) => { expect( matchByPath('/', from, { to, caseSensitive: false, fuzzy: false }), ).toEqual(result) From a3e3c380e31d3a849d1a3bb5dd86934ae3f1534c Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 15:18:10 +0200 Subject: [PATCH 13/15] add additional test --- .../basic-file-based/src/routeTree.gen.ts | 1894 ----------------- .../src/routes/optional-params/route.tsx | 36 + .../routes/optional-params/single/path.tsx | 13 + .../routes/optional-params/single/{-$id}.tsx | 17 + .../tests/optionalParams.spec.ts | 79 +- 5 files changed, 129 insertions(+), 1910 deletions(-) delete mode 100644 e2e/react-router/basic-file-based/src/routeTree.gen.ts create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/single/path.tsx create mode 100644 e2e/react-router/basic-file-based/src/routes/optional-params/single/{-$id}.tsx 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 e181b9ea864..00000000000 --- a/e2e/react-router/basic-file-based/src/routeTree.gen.ts +++ /dev/null @@ -1,1894 +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 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' -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 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)')() - -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 OptionalParamsRouteRoute = OptionalParamsRouteRouteImport.update({ - id: '/optional-params', - path: '/optional-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 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', - 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 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 - '/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 - '/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 - '/relative/useNavigate/nested': typeof RelativeUseNavigateNestedIndexRoute - '/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 - '/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 - '/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 - '/relative/useNavigate/nested': typeof RelativeUseNavigateNestedIndexRoute - '/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 - '/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 - '/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 - '/relative/useNavigate/nested/': typeof RelativeUseNavigateNestedIndexRoute - '/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' - | '/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' - | '/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' - | '/relative/useNavigate/nested' - | '/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' - | '/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' - | '/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' - | '/relative/useNavigate/nested' - | '/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' - | '/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' - | '/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/' - | '/relative/useNavigate/nested/' - | '/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 - 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 - } - '/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' - 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 - } - '/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' - fullPath: '/params-ps/non-nested/$foo/$bar' - 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' - 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 - } - '/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' - fullPath: '/params-ps/named/$foo/$bar/$baz' - 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 - } - } -} - -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 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 -} - -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, - OptionalParamsRouteRoute: OptionalParamsRouteRouteWithChildren, - 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/route.tsx b/e2e/react-router/basic-file-based/src/routes/optional-params/route.tsx index 2246ed77386..f815366a0c3 100644 --- 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 @@ -9,6 +9,42 @@ function RouteComponent() {

    optional path params

      +
    • +
      single optional only
      +
      +
        +
      • + + /optionals/single + {' '} +
      • +
      • + + /optionals/single/id + {' '} +
      • +
      • + + /optionals/single/path + {' '} +
      • +
      +
      +
      +
    • simple optional path param usage
      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/tests/optionalParams.spec.ts b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts index 54a03637642..42cd32de798 100644 --- a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts +++ b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts @@ -5,6 +5,53 @@ test.beforeEach(async ({ page }) => { }) 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/paht') + await expect(page.getByTestId('single-path-heading')).toBeVisible() + }) + test('simple optional param usage', async ({ page }) => { await page.goto('/optional-params') let pagePathname = '' @@ -35,7 +82,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('simple-index-heading')).toBeVisible() expect(await page.getByTestId('simple-index-params').innerText()).toEqual( JSON.stringify({}), ) @@ -44,7 +91,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('simple-index-heading')).toBeVisible() expect(await page.getByTestId('simple-index-params').innerText()).toEqual( JSON.stringify({ id: 'id' }), ) @@ -53,7 +100,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('simple-path-heading')).toBeVisible() expect(await page.getByTestId('simple-path-params').innerText()).toEqual( JSON.stringify({}), ) @@ -62,7 +109,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('simple-path-heading')).toBeVisible() expect(await page.getByTestId('simple-path-params').innerText()).toEqual( JSON.stringify({ id: 'id' }), ) @@ -108,7 +155,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('withIndex-index-heading')).toBeVisible() expect( await page.getByTestId('withIndex-index-params').innerText(), ).toEqual(JSON.stringify({ category: 'category' })) @@ -117,7 +164,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('withIndex-index-heading')).toBeVisible() expect( await page.getByTestId('withIndex-index-params').innerText(), ).toEqual(JSON.stringify({ id: 'id', category: 'category' })) @@ -126,7 +173,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('withIndex-path-heading')).toBeVisible() expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( JSON.stringify({ category: 'category' }), ) @@ -135,7 +182,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('withIndex-path-heading')).toBeVisible() expect(await page.getByTestId('withIndex-path-params').innerText()).toEqual( JSON.stringify({ id: 'id', category: 'category' }), ) @@ -179,7 +226,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('consecutive-heading')).toBeVisible() expect( await page .getByTestId('consecutive-id-slug-category-info-params') @@ -190,7 +237,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('consecutive-heading')).toBeVisible() expect( await page .getByTestId('consecutive-id-slug-category-info-params') @@ -201,7 +248,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => 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() + await expect(page.getByTestId('consecutive-heading')).toBeVisible() expect( await page .getByTestId('consecutive-id-slug-category-info-params') @@ -220,7 +267,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => expect(pagePathname).toBe( '/optional-params/consecutive/id/slug/category/info', ) - await expect(page.getByTestId('consecutive-heading')).toBeInViewport() + await expect(page.getByTestId('consecutive-heading')).toBeVisible() expect( await page .getByTestId('consecutive-id-slug-category-info-params') @@ -371,7 +418,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => expect(pagePathname).toBe( '/optional-params/withRequiredParam/category/info', ) - await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() expect( await page .getByTestId('withRequiredParam-id-category-slug-info-params') @@ -384,7 +431,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => expect(pagePathname).toBe( '/optional-params/withRequiredParam/id/category/info', ) - await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() expect( await page .getByTestId('withRequiredParam-id-category-slug-info-params') @@ -399,7 +446,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => expect(pagePathname).toBe( '/optional-params/withRequiredParam/category/slug/info', ) - await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() expect( await page .getByTestId('withRequiredParam-id-category-slug-info-params') @@ -420,7 +467,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => expect(pagePathname).toBe( '/optional-params/withRequiredParam/id/category/slug/info', ) - await expect(page.getByTestId('withRequiredParam-heading')).toBeInViewport() + await expect(page.getByTestId('withRequiredParam-heading')).toBeVisible() expect( await page .getByTestId('withRequiredParam-id-category-slug-info-params') From 956d51abb638b51a875796c924f8eda8c925c500 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 15:26:29 +0200 Subject: [PATCH 14/15] fix typo in test --- e2e/react-router/basic-file-based/tests/optionalParams.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts index 42cd32de798..72f7f46edf1 100644 --- a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts +++ b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts @@ -48,7 +48,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => await singlePathLink.click() await page.waitForURL('/optional-params/single/path') pagePathname = new URL(page.url()).pathname - expect(pagePathname).toBe('/optional-params/single/paht') + expect(pagePathname).toBe('/optional-params/single/path') await expect(page.getByTestId('single-path-heading')).toBeVisible() }) From 659b01158efe9e94bb5ee4f5741bfa9f9af78515 Mon Sep 17 00:00:00 2001 From: Nico Lynzaad Date: Sun, 21 Sep 2025 15:33:57 +0200 Subject: [PATCH 15/15] resolve flakiness --- .../basic-file-based/tests/optionalParams.spec.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts index 72f7f46edf1..a56db5e99cd 100644 --- a/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts +++ b/e2e/react-router/basic-file-based/tests/optionalParams.spec.ts @@ -319,7 +319,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => ) await expect( page.getByTestId('withRequiredInBetween-heading'), - ).toBeInViewport() + ).toBeVisible() expect( await page .getByTestId('withRequiredInBetween-id-category-path-slug-params') @@ -336,7 +336,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => ) await expect( page.getByTestId('withRequiredInBetween-heading'), - ).toBeInViewport() + ).toBeVisible() expect( await page .getByTestId('withRequiredInBetween-id-category-path-slug-params') @@ -353,7 +353,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => ) await expect( page.getByTestId('withRequiredInBetween-heading'), - ).toBeInViewport() + ).toBeVisible() expect( await page .getByTestId('withRequiredInBetween-id-category-path-slug-params') @@ -370,7 +370,7 @@ test.describe('ensure paths with optional params are resolved correctly', () => ) await expect( page.getByTestId('withRequiredInBetween-heading'), - ).toBeInViewport() + ).toBeVisible() expect( await page .getByTestId('withRequiredInBetween-id-category-path-slug-params')