@tanstack/router-core 1.132.0-alpha.2 → 1.132.0-alpha.20

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.
Files changed (142) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +2 -2
  3. package/dist/cjs/config.cjs +10 -0
  4. package/dist/cjs/config.cjs.map +1 -0
  5. package/dist/cjs/config.d.cts +17 -0
  6. package/dist/cjs/fileRoute.d.cts +3 -2
  7. package/dist/cjs/index.cjs +15 -3
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +11 -4
  10. package/dist/cjs/load-matches.cjs +636 -0
  11. package/dist/cjs/load-matches.cjs.map +1 -0
  12. package/dist/cjs/load-matches.d.cts +16 -0
  13. package/dist/cjs/location.d.cts +38 -0
  14. package/dist/cjs/path.cjs +7 -49
  15. package/dist/cjs/path.cjs.map +1 -1
  16. package/dist/cjs/path.d.cts +3 -6
  17. package/dist/cjs/qss.cjs +19 -19
  18. package/dist/cjs/qss.cjs.map +1 -1
  19. package/dist/cjs/qss.d.cts +6 -4
  20. package/dist/cjs/redirect.cjs +3 -3
  21. package/dist/cjs/redirect.cjs.map +1 -1
  22. package/dist/cjs/rewrite.cjs +63 -0
  23. package/dist/cjs/rewrite.cjs.map +1 -0
  24. package/dist/cjs/rewrite.d.cts +22 -0
  25. package/dist/cjs/route.cjs.map +1 -1
  26. package/dist/cjs/route.d.cts +42 -37
  27. package/dist/cjs/router.cjs +134 -780
  28. package/dist/cjs/router.cjs.map +1 -1
  29. package/dist/cjs/router.d.cts +68 -36
  30. package/dist/cjs/scroll-restoration.cjs +32 -29
  31. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  32. package/dist/cjs/scroll-restoration.d.cts +1 -1
  33. package/dist/cjs/searchParams.cjs +7 -15
  34. package/dist/cjs/searchParams.cjs.map +1 -1
  35. package/dist/cjs/ssr/constants.cjs +5 -0
  36. package/dist/cjs/ssr/constants.cjs.map +1 -0
  37. package/dist/cjs/ssr/constants.d.cts +1 -0
  38. package/dist/cjs/ssr/{seroval-plugins.cjs → serializer/ShallowErrorPlugin.cjs} +2 -2
  39. package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -0
  40. package/dist/cjs/ssr/{seroval-plugins.d.cts → serializer/ShallowErrorPlugin.d.cts} +1 -2
  41. package/dist/cjs/ssr/serializer/seroval-plugins.cjs +11 -0
  42. package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -0
  43. package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -0
  44. package/dist/cjs/ssr/serializer/transformer.cjs +52 -0
  45. package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -0
  46. package/dist/cjs/ssr/serializer/transformer.d.cts +56 -0
  47. package/dist/cjs/ssr/server.d.cts +5 -0
  48. package/dist/cjs/ssr/ssr-client.cjs +15 -1
  49. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  50. package/dist/cjs/ssr/ssr-client.d.cts +5 -1
  51. package/dist/cjs/ssr/ssr-server.cjs +12 -10
  52. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  53. package/dist/cjs/ssr/ssr-server.d.cts +0 -1
  54. package/dist/cjs/ssr/tsrScript.cjs +1 -1
  55. package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
  56. package/dist/cjs/utils.cjs +8 -7
  57. package/dist/cjs/utils.cjs.map +1 -1
  58. package/dist/cjs/utils.d.cts +1 -1
  59. package/dist/esm/Matches.d.ts +2 -2
  60. package/dist/esm/Matches.js.map +1 -1
  61. package/dist/esm/config.d.ts +17 -0
  62. package/dist/esm/config.js +10 -0
  63. package/dist/esm/config.js.map +1 -0
  64. package/dist/esm/fileRoute.d.ts +3 -2
  65. package/dist/esm/index.d.ts +11 -4
  66. package/dist/esm/index.js +17 -5
  67. package/dist/esm/index.js.map +1 -1
  68. package/dist/esm/load-matches.d.ts +16 -0
  69. package/dist/esm/load-matches.js +636 -0
  70. package/dist/esm/load-matches.js.map +1 -0
  71. package/dist/esm/location.d.ts +38 -0
  72. package/dist/esm/path.d.ts +3 -6
  73. package/dist/esm/path.js +7 -49
  74. package/dist/esm/path.js.map +1 -1
  75. package/dist/esm/qss.d.ts +6 -4
  76. package/dist/esm/qss.js +19 -19
  77. package/dist/esm/qss.js.map +1 -1
  78. package/dist/esm/redirect.js +3 -3
  79. package/dist/esm/redirect.js.map +1 -1
  80. package/dist/esm/rewrite.d.ts +22 -0
  81. package/dist/esm/rewrite.js +63 -0
  82. package/dist/esm/rewrite.js.map +1 -0
  83. package/dist/esm/route.d.ts +42 -37
  84. package/dist/esm/route.js.map +1 -1
  85. package/dist/esm/router.d.ts +68 -36
  86. package/dist/esm/router.js +136 -782
  87. package/dist/esm/router.js.map +1 -1
  88. package/dist/esm/scroll-restoration.d.ts +1 -1
  89. package/dist/esm/scroll-restoration.js +32 -29
  90. package/dist/esm/scroll-restoration.js.map +1 -1
  91. package/dist/esm/searchParams.js +7 -15
  92. package/dist/esm/searchParams.js.map +1 -1
  93. package/dist/esm/ssr/constants.d.ts +1 -0
  94. package/dist/esm/ssr/constants.js +5 -0
  95. package/dist/esm/ssr/constants.js.map +1 -0
  96. package/dist/esm/ssr/{seroval-plugins.d.ts → serializer/ShallowErrorPlugin.d.ts} +1 -2
  97. package/dist/esm/ssr/{seroval-plugins.js → serializer/ShallowErrorPlugin.js} +2 -2
  98. package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -0
  99. package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -0
  100. package/dist/esm/ssr/serializer/seroval-plugins.js +11 -0
  101. package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -0
  102. package/dist/esm/ssr/serializer/transformer.d.ts +56 -0
  103. package/dist/esm/ssr/serializer/transformer.js +52 -0
  104. package/dist/esm/ssr/serializer/transformer.js.map +1 -0
  105. package/dist/esm/ssr/server.d.ts +5 -0
  106. package/dist/esm/ssr/ssr-client.d.ts +5 -1
  107. package/dist/esm/ssr/ssr-client.js +15 -1
  108. package/dist/esm/ssr/ssr-client.js.map +1 -1
  109. package/dist/esm/ssr/ssr-server.d.ts +0 -1
  110. package/dist/esm/ssr/ssr-server.js +12 -10
  111. package/dist/esm/ssr/ssr-server.js.map +1 -1
  112. package/dist/esm/ssr/tsrScript.js +1 -1
  113. package/dist/esm/ssr/tsrScript.js.map +1 -1
  114. package/dist/esm/utils.d.ts +1 -1
  115. package/dist/esm/utils.js +8 -7
  116. package/dist/esm/utils.js.map +1 -1
  117. package/package.json +1 -1
  118. package/src/Matches.ts +2 -2
  119. package/src/config.ts +42 -0
  120. package/src/fileRoute.ts +15 -3
  121. package/src/index.ts +32 -3
  122. package/src/load-matches.ts +955 -0
  123. package/src/location.ts +38 -0
  124. package/src/path.ts +9 -66
  125. package/src/qss.ts +27 -24
  126. package/src/redirect.ts +3 -3
  127. package/src/rewrite.ts +70 -0
  128. package/src/route.ts +136 -33
  129. package/src/router.ts +271 -1170
  130. package/src/scroll-restoration.ts +42 -37
  131. package/src/searchParams.ts +8 -19
  132. package/src/ssr/constants.ts +1 -0
  133. package/src/ssr/{seroval-plugins.ts → serializer/ShallowErrorPlugin.ts} +2 -2
  134. package/src/ssr/serializer/seroval-plugins.ts +9 -0
  135. package/src/ssr/serializer/transformer.ts +215 -0
  136. package/src/ssr/server.ts +6 -0
  137. package/src/ssr/ssr-client.ts +30 -3
  138. package/src/ssr/ssr-server.ts +18 -10
  139. package/src/ssr/tsrScript.ts +5 -1
  140. package/src/utils.ts +11 -10
  141. package/dist/cjs/ssr/seroval-plugins.cjs.map +0 -1
  142. package/dist/esm/ssr/seroval-plugins.js.map +0 -1
package/src/location.ts CHANGED
@@ -2,12 +2,50 @@ import type { ParsedHistoryState } from '@tanstack/history'
2
2
  import type { AnySchema } from './validators'
3
3
 
4
4
  export interface ParsedLocation<TSearchObj extends AnySchema = {}> {
5
+ /**
6
+ * The full path of the location, including pathname, search, and hash.
7
+ * Does not include the origin. Is the equivalent of calling
8
+ * `url.replace(url.origin, '')`
9
+ */
5
10
  href: string
11
+ /**
12
+ * @description The pathname of the location, including the leading slash.
13
+ */
6
14
  pathname: string
15
+ /**
16
+ * The parsed search parameters of the location in object form.
17
+ */
7
18
  search: TSearchObj
19
+ /**
20
+ * The search string of the location, including the leading question mark.
21
+ */
8
22
  searchStr: string
23
+ /**
24
+ * The in-memory state of the location as it *may* exist in the browser's history.
25
+ */
9
26
  state: ParsedHistoryState
27
+ /**
28
+ * The hash of the location, including the leading hash character.
29
+ */
10
30
  hash: string
31
+ /**
32
+ * The masked location of the location.
33
+ */
11
34
  maskedLocation?: ParsedLocation<TSearchObj>
35
+ /**
36
+ * Whether to unmask the location on reload.
37
+ */
12
38
  unmaskOnReload?: boolean
39
+ /**
40
+ * @private
41
+ * @description The public href of the location, including the origin before any rewrites.
42
+ * If a rewrite is applied, the `href` property will be the rewritten URL.
43
+ */
44
+ publicHref: string
45
+ /**
46
+ * @private
47
+ * @description The full URL of the location, including the origin.
48
+ * @private
49
+ */
50
+ url: string
13
51
  }
package/src/path.ts CHANGED
@@ -97,11 +97,9 @@ export function exactPathTest(
97
97
  // /a/b/c + d/ = /a/b/c/d
98
98
  // /a/b/c + d/e = /a/b/c/d/e
99
99
  interface ResolvePathOptions {
100
- basepath: string
101
100
  base: string
102
101
  to: string
103
102
  trailingSlash?: 'always' | 'never' | 'preserve'
104
- caseSensitive?: boolean
105
103
  parseCache?: ParsePathnameCache
106
104
  }
107
105
 
@@ -151,16 +149,11 @@ function segmentToString(segment: Segment): string {
151
149
  }
152
150
 
153
151
  export function resolvePath({
154
- basepath,
155
152
  base,
156
153
  to,
157
154
  trailingSlash = 'never',
158
- caseSensitive,
159
155
  parseCache,
160
156
  }: ResolvePathOptions) {
161
- base = removeBasepath(basepath, base, caseSensitive)
162
- to = removeBasepath(basepath, to, caseSensitive)
163
-
164
157
  let baseSegments = parsePathname(base, parseCache).slice()
165
158
  const toSegments = parsePathname(to, parseCache)
166
159
 
@@ -201,7 +194,8 @@ export function resolvePath({
201
194
  }
202
195
 
203
196
  const segmentValues = baseSegments.map(segmentToString)
204
- const joined = joinPaths([basepath, ...segmentValues])
197
+ // const joined = joinPaths([basepath, ...segmentValues])
198
+ const joined = joinPaths(segmentValues)
205
199
  return joined
206
200
  }
207
201
 
@@ -403,6 +397,10 @@ export function interpolatePath({
403
397
 
404
398
  if (segment.type === SEGMENT_TYPE_WILDCARD) {
405
399
  usedParams._splat = params._splat
400
+
401
+ // TODO: Deprecate *
402
+ usedParams['*'] = params._splat
403
+
406
404
  const segmentPrefix = segment.prefixSegment || ''
407
405
  const segmentSuffix = segment.suffixSegment || ''
408
406
 
@@ -491,17 +489,11 @@ function encodePathParam(value: string, decodeCharMap?: Map<string, string>) {
491
489
  }
492
490
 
493
491
  export function matchPathname(
494
- basepath: string,
495
492
  currentPathname: string,
496
493
  matchLocation: Pick<MatchLocation, 'to' | 'fuzzy' | 'caseSensitive'>,
497
494
  parseCache?: ParsePathnameCache,
498
495
  ): AnyPathParams | undefined {
499
- const pathParams = matchByPath(
500
- basepath,
501
- currentPathname,
502
- matchLocation,
503
- parseCache,
504
- )
496
+ const pathParams = matchByPath(currentPathname, matchLocation, parseCache)
505
497
  // const searchMatched = matchBySearch(location.search, matchLocation)
506
498
 
507
499
  if (matchLocation.to && !pathParams) {
@@ -511,49 +503,7 @@ export function matchPathname(
511
503
  return pathParams ?? {}
512
504
  }
513
505
 
514
- export function removeBasepath(
515
- basepath: string,
516
- pathname: string,
517
- caseSensitive: boolean = false,
518
- ) {
519
- // normalize basepath and pathname for case-insensitive comparison if needed
520
- const normalizedBasepath = caseSensitive ? basepath : basepath.toLowerCase()
521
- const normalizedPathname = caseSensitive ? pathname : pathname.toLowerCase()
522
-
523
- switch (true) {
524
- // default behaviour is to serve app from the root - pathname
525
- // left untouched
526
- case normalizedBasepath === '/':
527
- return pathname
528
-
529
- // shortcut for removing the basepath if it matches the pathname
530
- case normalizedPathname === normalizedBasepath:
531
- return ''
532
-
533
- // in case pathname is shorter than basepath - there is
534
- // nothing to remove
535
- case pathname.length < basepath.length:
536
- return pathname
537
-
538
- // avoid matching partial segments - strict equality handled
539
- // earlier, otherwise, basepath separated from pathname with
540
- // separator, therefore lack of separator means partial
541
- // segment match (`/app` should not match `/application`)
542
- case normalizedPathname[normalizedBasepath.length] !== '/':
543
- return pathname
544
-
545
- // remove the basepath from the pathname if it starts with it
546
- case normalizedPathname.startsWith(normalizedBasepath):
547
- return pathname.slice(basepath.length)
548
-
549
- // otherwise, return the pathname as is
550
- default:
551
- return pathname
552
- }
553
- }
554
-
555
506
  export function matchByPath(
556
- basepath: string,
557
507
  from: string,
558
508
  {
559
509
  to,
@@ -562,14 +512,7 @@ export function matchByPath(
562
512
  }: Pick<MatchLocation, 'to' | 'caseSensitive' | 'fuzzy'>,
563
513
  parseCache?: ParsePathnameCache,
564
514
  ): Record<string, string> | undefined {
565
- // check basepath first
566
- if (basepath !== '/' && !from.startsWith(basepath)) {
567
- return undefined
568
- }
569
- // Remove the base path from the pathname
570
- from = removeBasepath(basepath, from, caseSensitive)
571
- // Default to to $ (wildcard)
572
- to = removeBasepath(basepath, `${to ?? '$'}`, caseSensitive)
515
+ const stringTo = to as string
573
516
 
574
517
  // Parse the from and to
575
518
  const baseSegments = parsePathname(
@@ -577,7 +520,7 @@ export function matchByPath(
577
520
  parseCache,
578
521
  )
579
522
  const routeSegments = parsePathname(
580
- to.startsWith('/') ? to : `/${to}`,
523
+ stringTo.startsWith('/') ? stringTo : `/${stringTo}`,
581
524
  parseCache,
582
525
  )
583
526
 
package/src/qss.ts CHANGED
@@ -6,12 +6,15 @@
6
6
  * This reimplementation uses modern browser APIs
7
7
  * (namely URLSearchParams) and TypeScript while still
8
8
  * maintaining the original functionality and interface.
9
+ *
10
+ * Update: this implementation has also been mangled to
11
+ * fit exactly our use-case (single value per key in encoding).
9
12
  */
10
13
 
11
14
  /**
12
15
  * Encodes an object into a query string.
13
16
  * @param obj - The object to encode into a query string.
14
- * @param [pfx] - An optional prefix to add before the query string.
17
+ * @param stringify - An optional custom stringify function.
15
18
  * @returns The encoded query string.
16
19
  * @example
17
20
  * ```
@@ -19,18 +22,20 @@
19
22
  * // Expected output: "token=foo&key=value"
20
23
  * ```
21
24
  */
22
- export function encode(obj: any, pfx?: string) {
23
- const normalizedObject = Object.entries(obj).flatMap(([key, value]) => {
24
- if (Array.isArray(value)) {
25
- return value.map((v) => [key, String(v)])
26
- } else {
27
- return [[key, String(value)]]
28
- }
29
- })
25
+ export function encode(
26
+ obj: Record<string, any>,
27
+ stringify: (value: any) => string = String,
28
+ ): string {
29
+ const result = new URLSearchParams()
30
30
 
31
- const searchParams = new URLSearchParams(normalizedObject)
31
+ for (const key in obj) {
32
+ const val = obj[key]
33
+ if (val !== undefined) {
34
+ result.set(key, stringify(val))
35
+ }
36
+ }
32
37
 
33
- return (pfx || '') + searchParams.toString()
38
+ return result.toString()
34
39
  }
35
40
 
36
41
  /**
@@ -52,28 +57,26 @@ function toValue(str: unknown) {
52
57
  /**
53
58
  * Decodes a query string into an object.
54
59
  * @param str - The query string to decode.
55
- * @param [pfx] - An optional prefix to filter out from the query string.
56
60
  * @returns The decoded key-value pairs in an object format.
57
61
  * @example
58
62
  * // Example input: decode("token=foo&key=value")
59
63
  * // Expected output: { "token": "foo", "key": "value" }
60
64
  */
61
- export function decode(str: any, pfx?: string): any {
62
- const searchParamsPart = pfx ? str.slice(pfx.length) : str
63
- const searchParams = new URLSearchParams(searchParamsPart)
65
+ export function decode(str: any): any {
66
+ const searchParams = new URLSearchParams(str)
64
67
 
65
- const entries = [...searchParams.entries()]
68
+ const result: Record<string, unknown> = {}
66
69
 
67
- return entries.reduce<Record<string, unknown>>((acc, [key, value]) => {
68
- const previousValue = acc[key]
70
+ for (const [key, value] of searchParams.entries()) {
71
+ const previousValue = result[key]
69
72
  if (previousValue == null) {
70
- acc[key] = toValue(value)
73
+ result[key] = toValue(value)
74
+ } else if (Array.isArray(previousValue)) {
75
+ previousValue.push(toValue(value))
71
76
  } else {
72
- acc[key] = Array.isArray(previousValue)
73
- ? [...previousValue, toValue(value)]
74
- : [previousValue, toValue(value)]
77
+ result[key] = [previousValue, toValue(value)]
75
78
  }
79
+ }
76
80
 
77
- return acc
78
- }, {})
81
+ return result
79
82
  }
package/src/redirect.ts CHANGED
@@ -65,9 +65,9 @@ export function redirect<
65
65
  ): Redirect<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> {
66
66
  opts.statusCode = opts.statusCode || opts.code || 307
67
67
 
68
- if (!opts.reloadDocument) {
68
+ if (!opts.reloadDocument && typeof opts.href === 'string') {
69
69
  try {
70
- new URL(`${opts.href}`)
70
+ new URL(opts.href)
71
71
  opts.reloadDocument = true
72
72
  } catch {}
73
73
  }
@@ -103,7 +103,7 @@ export function isResolvedRedirect(
103
103
  }
104
104
 
105
105
  export function parseRedirect(obj: any) {
106
- if (typeof obj === 'object' && obj.isSerializedRedirect) {
106
+ if (obj !== null && typeof obj === 'object' && obj.isSerializedRedirect) {
107
107
  return redirect(obj)
108
108
  }
109
109
 
package/src/rewrite.ts ADDED
@@ -0,0 +1,70 @@
1
+ import { joinPaths, trimPath } from './path'
2
+ import type { LocationRewrite } from './router'
3
+
4
+ export function composeRewrites(rewrites: Array<LocationRewrite>) {
5
+ return {
6
+ input: ({ url }) => {
7
+ for (const rewrite of rewrites) {
8
+ url = executeRewriteInput(rewrite, url)
9
+ }
10
+ return url
11
+ },
12
+ output: ({ url }) => {
13
+ for (let i = rewrites.length - 1; i >= 0; i--) {
14
+ url = executeRewriteOutput(rewrites[i], url)
15
+ }
16
+ return url
17
+ },
18
+ } satisfies LocationRewrite
19
+ }
20
+
21
+ export function rewriteBasepath(opts: {
22
+ basepath: string
23
+ caseSensitive?: boolean
24
+ }) {
25
+ const trimmedBasepath = trimPath(opts.basepath)
26
+ const regex = new RegExp(
27
+ `^/${trimmedBasepath}/`,
28
+ opts.caseSensitive ? '' : 'i',
29
+ )
30
+ return {
31
+ input: ({ url }) => {
32
+ url.pathname = url.pathname.replace(regex, '/')
33
+ return url
34
+ },
35
+ output: ({ url }) => {
36
+ url.pathname = joinPaths(['/', trimmedBasepath, url.pathname])
37
+ return url
38
+ },
39
+ } satisfies LocationRewrite
40
+ }
41
+
42
+ export function executeRewriteInput(
43
+ rewrite: LocationRewrite | undefined,
44
+ url: URL,
45
+ ): URL {
46
+ const res = rewrite?.input?.({ url })
47
+ if (res) {
48
+ if (typeof res === 'string') {
49
+ return new URL(res)
50
+ } else if (res instanceof URL) {
51
+ return res
52
+ }
53
+ }
54
+ return url
55
+ }
56
+
57
+ export function executeRewriteOutput(
58
+ rewrite: LocationRewrite | undefined,
59
+ url: URL,
60
+ ): URL {
61
+ const res = rewrite?.output?.({ url })
62
+ if (res) {
63
+ if (typeof res === 'string') {
64
+ return new URL(res)
65
+ } else if (res instanceof URL) {
66
+ return res
67
+ }
68
+ }
69
+ return url
70
+ }