@devp0nt/route0 1.0.0-next.57 → 1.0.0-next.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.test.ts CHANGED
@@ -960,6 +960,7 @@ describe('getLocation', () => {
960
960
  unmatched: false,
961
961
  ancestor: true,
962
962
  descendant: false,
963
+ params: {},
963
964
  })
964
965
  expect(Route0.create('/prefix/:x/some').getLocation('/prefix/xxx/some/extra/path')).toMatchObject({
965
966
  known: true,
@@ -967,6 +968,7 @@ describe('getLocation', () => {
967
968
  unmatched: false,
968
969
  ancestor: true,
969
970
  descendant: false,
971
+ params: { x: 'xxx' },
970
972
  })
971
973
  expect(Route0.create('/:y/:x/some').getLocation('/prefix/xxx/some/extra/path')).toMatchObject({
972
974
  known: true,
@@ -974,6 +976,7 @@ describe('getLocation', () => {
974
976
  unmatched: false,
975
977
  ancestor: true,
976
978
  descendant: false,
979
+ params: { y: 'prefix', x: 'xxx' },
977
980
  })
978
981
  })
979
982
 
@@ -984,6 +987,7 @@ describe('getLocation', () => {
984
987
  unmatched: false,
985
988
  ancestor: false,
986
989
  descendant: true,
990
+ params: {},
987
991
  })
988
992
  expect(Route0.create('/prefix/some/extra/:id').getLocation('/prefix/some')).toMatchObject({
989
993
  known: true,
@@ -991,6 +995,7 @@ describe('getLocation', () => {
991
995
  unmatched: false,
992
996
  ancestor: false,
993
997
  descendant: true,
998
+ params: {},
994
999
  })
995
1000
  expect(Route0.create('/:prefix/some/extra/:id').getLocation('/prefix/some')).toMatchObject({
996
1001
  known: true,
@@ -998,6 +1003,7 @@ describe('getLocation', () => {
998
1003
  unmatched: false,
999
1004
  ancestor: false,
1000
1005
  descendant: true,
1006
+ params: { prefix: 'prefix' },
1001
1007
  })
1002
1008
  })
1003
1009
 
@@ -1280,6 +1286,13 @@ describe('getLocation', () => {
1280
1286
  expect(loc.exact).toBe(true)
1281
1287
  expect(loc.route).toBe('/b/:c')
1282
1288
  })
1289
+
1290
+ it('any RoutesPretty type suitable to any RoutesPretty stype', () => {
1291
+ Routes.create({
1292
+ home: '/',
1293
+ v: '/b',
1294
+ }) satisfies RoutesPretty
1295
+ })
1283
1296
  })
1284
1297
  })
1285
1298
 
package/src/index.ts CHANGED
@@ -719,19 +719,20 @@ export class Route0<TDefinition extends string> {
719
719
  const exactRe = new RegExp(`^${this.getRegexBaseString()}$`)
720
720
  const ancestorRe = new RegExp(`^${this.getRegexBaseString()}(?:/.*)?$`) // route matches the beginning of the URL (may have more)
721
721
  const exactMatch = pathname.match(exactRe)
722
+ const ancestorMatch = pathname.match(ancestorRe)
723
+ const exact = !!exactMatch
724
+ const ancestor = !exact && !!ancestorMatch
722
725
 
723
- // Fill params only for exact match (keeps behavior predictable)
724
- if (exactMatch) {
725
- const values = exactMatch.slice(1)
726
+ // Parse params for exact and ancestor matches.
727
+ const paramsMatch = exactMatch || (ancestor ? ancestorMatch : null)
728
+ if (paramsMatch) {
729
+ const values = paramsMatch.slice(1, 1 + paramNames.length)
726
730
  const params = Object.fromEntries(paramNames.map((n, i) => [n, decodeURIComponent(values[i] ?? '')]))
727
731
  location.params = params
728
732
  } else {
729
733
  location.params = {}
730
734
  }
731
735
 
732
- const exact = !!exactMatch
733
- const ancestor = !exact && ancestorRe.test(pathname)
734
-
735
736
  // "descendant": the URL is a prefix of the route definition (params match any single segment)
736
737
  const getParts = (path: string) => (path === '/' ? ['/'] : path.split('/').filter(Boolean))
737
738
  const defParts = getParts(def)
@@ -758,6 +759,21 @@ export class Route0<TDefinition extends string> {
758
759
  const descendant = !exact && isPrefix
759
760
  const unmatched = !exact && !ancestor && !descendant
760
761
 
762
+ // For descendant matches, include only params that are already determined
763
+ // by the current (shorter) pathname prefix.
764
+ if (descendant) {
765
+ const descendantParams: Record<string, string> = {}
766
+ for (let i = 0; i < pathParts.length; i++) {
767
+ const defPart = defParts[i]
768
+ const pathPart = pathParts[i]
769
+ if (!defPart || !pathPart) continue
770
+ if (defPart.startsWith(':')) {
771
+ descendantParams[defPart.slice(1)] = decodeURIComponent(pathPart)
772
+ }
773
+ }
774
+ location.params = descendantParams
775
+ }
776
+
761
777
  return {
762
778
  ...location,
763
779
  known: true,
@@ -992,7 +1008,7 @@ export class Route0<TDefinition extends string> {
992
1008
  * `Routes.create()` accepts either plain string definitions or route objects
993
1009
  * and returns a "pretty" object with direct route access + helper methods under `._`.
994
1010
  */
995
- export class Routes<const T extends RoutesRecord = RoutesRecord> {
1011
+ export class Routes<const T extends RoutesRecord = any> {
996
1012
  _routes: RoutesRecordHydrated<T>
997
1013
  _pathsOrdering: string[]
998
1014
  _keysOrdering: string[]
@@ -1178,27 +1194,24 @@ export type RouteConfigInput = {
1178
1194
  /** User-provided routes map (plain definitions or route instances). */
1179
1195
  export type RoutesRecord = Record<string, AnyRoute | string>
1180
1196
  /** Same as `RoutesRecord` but all values normalized to callable routes. */
1181
- export type RoutesRecordHydrated<TRoutesRecord extends RoutesRecord = RoutesRecord> = {
1197
+ export type RoutesRecordHydrated<TRoutesRecord extends RoutesRecord = any> = {
1182
1198
  [K in keyof TRoutesRecord]: CallableRoute<TRoutesRecord[K]>
1183
1199
  }
1184
- /** Public shape returned by `Routes.create()`. */
1185
- export type RoutesPretty<TRoutesRecord extends RoutesRecord = RoutesRecord> = RoutesRecordHydrated<TRoutesRecord> &
1200
+ /** Public shape returned by `Routes.create()`. Default `any` so `satisfies RoutesPretty` accepts any created routes. */
1201
+ export type RoutesPretty<TRoutesRecord extends RoutesRecord = any> = RoutesRecordHydrated<TRoutesRecord> &
1186
1202
  Omit<
1187
1203
  Routes<TRoutesRecord>,
1188
1204
  '_routes' | '_getLocation' | '_override' | '_pathsOrdering' | '_keysOrdering' | '_ordered'
1189
1205
  >
1190
- export type ExtractRoutesKeys<TRoutes extends RoutesPretty<any> | RoutesRecord> =
1191
- TRoutes extends RoutesPretty<any>
1192
- ? Extract<keyof TRoutes['_']['routes'], string>
1193
- : TRoutes extends RoutesRecord
1194
- ? Extract<keyof TRoutes, string>
1195
- : never
1196
- export type ExtractRoute<TRoutes extends RoutesPretty<any> | RoutesRecord, TKey extends ExtractRoutesKeys<TRoutes>> =
1197
- TRoutes extends RoutesPretty<any>
1198
- ? TRoutes['_']['routes'][TKey]
1199
- : TRoutes extends RoutesRecord
1200
- ? TRoutes[TKey]
1201
- : never
1206
+ export type ExtractRoutesKeys<TRoutes extends RoutesPretty | RoutesRecord> = TRoutes extends RoutesPretty
1207
+ ? Extract<keyof TRoutes['_']['routes'], string>
1208
+ : TRoutes extends RoutesRecord
1209
+ ? Extract<keyof TRoutes, string>
1210
+ : never
1211
+ export type ExtractRoute<
1212
+ TRoutes extends RoutesPretty | RoutesRecord,
1213
+ TKey extends ExtractRoutesKeys<TRoutes>,
1214
+ > = TRoutes extends RoutesPretty ? TRoutes['_']['routes'][TKey] : TRoutes extends RoutesRecord ? TRoutes[TKey] : never
1202
1215
 
1203
1216
  // public utils
1204
1217
 
@@ -1414,7 +1427,7 @@ export type UnmatchedLocationState<TRoute extends AnyRoute | string = AnyRoute |
1414
1427
  known: true
1415
1428
  route: Definition<TRoute>
1416
1429
  params: Record<never, never>
1417
- searchParams: LooseSearchOutput<TRoute>
1430
+ searchParams: Record<string, string | undefined>
1418
1431
  exact: false
1419
1432
  ancestor: false
1420
1433
  descendant: false
@@ -1441,7 +1454,7 @@ export type ExactLocation<TRoute extends AnyRoute | string = AnyRoute | string>
1441
1454
  export type AncestorLocationState<TRoute extends AnyRoute | string = AnyRoute | string> = {
1442
1455
  known: true
1443
1456
  route: Definition<TRoute>
1444
- params: Partial<ParamsOutput<TRoute>>
1457
+ params: ParamsOutput<TRoute>
1445
1458
  searchParams: LooseSearchOutput<TRoute>
1446
1459
  exact: false
1447
1460
  ancestor: true
@@ -1451,11 +1464,11 @@ export type AncestorLocationState<TRoute extends AnyRoute | string = AnyRoute |
1451
1464
  export type AncestorLocation<TRoute extends AnyRoute | string = AnyRoute | string> =
1452
1465
  IsAny<TRoute> extends true ? any : _GeneralLocation & AncestorLocationState<TRoute>
1453
1466
 
1454
- /** Relaxed ancestor state variant for widened generic scenarios. */
1467
+ /** It is when route not match at all, but params match. */
1455
1468
  export type WeakAncestorLocationState<TRoute extends AnyRoute | string = AnyRoute | string> = {
1456
1469
  known: true
1457
1470
  route: Definition<TRoute>
1458
- params: Partial<ParamsOutput<TRoute>>
1471
+ params: ParamsOutput<TRoute>
1459
1472
  searchParams: LooseSearchOutput<TRoute>
1460
1473
  exact: false
1461
1474
  ancestor: true
@@ -1469,7 +1482,7 @@ export type WeakAncestorLocation<TRoute extends AnyRoute | string = AnyRoute | s
1469
1482
  export type DescendantLocationState<TRoute extends AnyRoute | string = AnyRoute | string> = {
1470
1483
  known: true
1471
1484
  route: Definition<TRoute>
1472
- params: ParamsOutput<TRoute>
1485
+ params: Partial<ParamsOutput<TRoute>>
1473
1486
  searchParams: LooseSearchOutput<TRoute>
1474
1487
  exact: false
1475
1488
  ancestor: false
@@ -1479,11 +1492,11 @@ export type DescendantLocationState<TRoute extends AnyRoute | string = AnyRoute
1479
1492
  export type DescendantLocation<TRoute extends AnyRoute | string = AnyRoute | string> =
1480
1493
  IsAny<TRoute> extends true ? any : _GeneralLocation & DescendantLocationState
1481
1494
 
1482
- /** Relaxed descendant state variant for widened generic scenarios. */
1495
+ /** It is when route not match at all, but params partially match. */
1483
1496
  export type WeakDescendantLocationState<TRoute extends AnyRoute | string = AnyRoute | string> = {
1484
1497
  known: true
1485
1498
  route: Definition<TRoute>
1486
- params: ParamsOutput<TRoute>
1499
+ params: Partial<ParamsOutput<TRoute>>
1487
1500
  searchParams: LooseSearchOutput<TRoute>
1488
1501
  exact: false
1489
1502
  ancestor: false