@tanstack/router-core 1.136.3 → 1.136.5

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 (70) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +2 -0
  3. package/dist/cjs/index.cjs +0 -5
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/index.d.cts +1 -4
  6. package/dist/cjs/lru-cache.cjs +5 -0
  7. package/dist/cjs/lru-cache.cjs.map +1 -1
  8. package/dist/cjs/lru-cache.d.cts +1 -0
  9. package/dist/cjs/new-process-route-tree.cjs +655 -0
  10. package/dist/cjs/new-process-route-tree.cjs.map +1 -0
  11. package/dist/cjs/new-process-route-tree.d.cts +177 -0
  12. package/dist/cjs/path.cjs +133 -434
  13. package/dist/cjs/path.cjs.map +1 -1
  14. package/dist/cjs/path.d.cts +3 -39
  15. package/dist/cjs/router.cjs +47 -98
  16. package/dist/cjs/router.cjs.map +1 -1
  17. package/dist/cjs/router.d.cts +7 -11
  18. package/dist/cjs/ssr/constants.cjs.map +1 -1
  19. package/dist/cjs/ssr/constants.d.cts +1 -0
  20. package/dist/cjs/ssr/ssr-client.cjs +2 -0
  21. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  22. package/dist/cjs/ssr/ssr-client.d.cts +4 -1
  23. package/dist/cjs/ssr/ssr-server.cjs +64 -12
  24. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  25. package/dist/cjs/ssr/tsrScript.cjs +1 -1
  26. package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
  27. package/dist/esm/Matches.d.ts +2 -0
  28. package/dist/esm/Matches.js.map +1 -1
  29. package/dist/esm/index.d.ts +1 -4
  30. package/dist/esm/index.js +1 -6
  31. package/dist/esm/index.js.map +1 -1
  32. package/dist/esm/lru-cache.d.ts +1 -0
  33. package/dist/esm/lru-cache.js +5 -0
  34. package/dist/esm/lru-cache.js.map +1 -1
  35. package/dist/esm/new-process-route-tree.d.ts +177 -0
  36. package/dist/esm/new-process-route-tree.js +655 -0
  37. package/dist/esm/new-process-route-tree.js.map +1 -0
  38. package/dist/esm/path.d.ts +3 -39
  39. package/dist/esm/path.js +133 -434
  40. package/dist/esm/path.js.map +1 -1
  41. package/dist/esm/router.d.ts +7 -11
  42. package/dist/esm/router.js +48 -99
  43. package/dist/esm/router.js.map +1 -1
  44. package/dist/esm/ssr/constants.d.ts +1 -0
  45. package/dist/esm/ssr/constants.js.map +1 -1
  46. package/dist/esm/ssr/ssr-client.d.ts +4 -1
  47. package/dist/esm/ssr/ssr-client.js +2 -0
  48. package/dist/esm/ssr/ssr-client.js.map +1 -1
  49. package/dist/esm/ssr/ssr-server.js +64 -12
  50. package/dist/esm/ssr/ssr-server.js.map +1 -1
  51. package/dist/esm/ssr/tsrScript.js +1 -1
  52. package/dist/esm/ssr/tsrScript.js.map +1 -1
  53. package/package.json +1 -1
  54. package/src/Matches.ts +2 -0
  55. package/src/index.ts +0 -6
  56. package/src/lru-cache.ts +6 -0
  57. package/src/new-process-route-tree.ts +1036 -0
  58. package/src/path.ts +168 -639
  59. package/src/router.ts +58 -126
  60. package/src/ssr/constants.ts +1 -0
  61. package/src/ssr/ssr-client.ts +10 -1
  62. package/src/ssr/ssr-server.ts +69 -12
  63. package/src/ssr/tsrScript.ts +4 -0
  64. package/dist/cjs/process-route-tree.cjs +0 -144
  65. package/dist/cjs/process-route-tree.cjs.map +0 -1
  66. package/dist/cjs/process-route-tree.d.cts +0 -18
  67. package/dist/esm/process-route-tree.d.ts +0 -18
  68. package/dist/esm/process-route-tree.js +0 -144
  69. package/dist/esm/process-route-tree.js.map +0 -1
  70. package/src/process-route-tree.ts +0 -241
package/src/router.ts CHANGED
@@ -9,21 +9,26 @@ import {
9
9
  last,
10
10
  replaceEqualDeep,
11
11
  } from './utils'
12
- import { processRouteTree } from './process-route-tree'
12
+ import {
13
+ findFlatMatch,
14
+ findRouteMatch,
15
+ findSingleMatch,
16
+ processRouteMasks,
17
+ processRouteTree,
18
+ } from './new-process-route-tree'
13
19
  import {
14
20
  cleanPath,
15
21
  interpolatePath,
16
- matchPathname,
17
22
  resolvePath,
18
23
  trimPath,
19
24
  trimPathRight,
20
25
  } from './path'
26
+ import { createLRUCache } from './lru-cache'
21
27
  import { isNotFound } from './not-found'
22
28
  import { setupScrollRestoration } from './scroll-restoration'
23
29
  import { defaultParseSearch, defaultStringifySearch } from './searchParams'
24
30
  import { rootRouteId } from './root'
25
31
  import { isRedirect, redirect } from './redirect'
26
- import { createLRUCache } from './lru-cache'
27
32
  import { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches'
28
33
  import {
29
34
  composeRewrites,
@@ -31,7 +36,7 @@ import {
31
36
  executeRewriteOutput,
32
37
  rewriteBasepath,
33
38
  } from './rewrite'
34
- import type { ParsePathnameCache } from './path'
39
+ import type { ProcessedTree } from './new-process-route-tree'
35
40
  import type { SearchParser, SearchSerializer } from './searchParams'
36
41
  import type { AnyRedirect, ResolvedRedirect } from './redirect'
37
42
  import type {
@@ -691,10 +696,7 @@ export type ParseLocationFn<TRouteTree extends AnyRoute> = (
691
696
  previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
692
697
  ) => ParsedLocation<FullSearchSchema<TRouteTree>>
693
698
 
694
- export type GetMatchRoutesFn = (
695
- pathname: string,
696
- routePathname: string | undefined,
697
- ) => {
699
+ export type GetMatchRoutesFn = (pathname: string) => {
698
700
  matchedRoutes: Array<AnyRoute>
699
701
  routeParams: Record<string, string>
700
702
  foundRoute: AnyRoute | undefined
@@ -754,6 +756,7 @@ export interface ServerSsr {
754
756
  isDehydrated: () => boolean
755
757
  onRenderFinished: (listener: () => void) => void
756
758
  dehydrate: () => Promise<void>
759
+ takeBufferedScripts: () => string | undefined
757
760
  }
758
761
 
759
762
  export type AnyRouterWithContext<TContext> = RouterCore<
@@ -901,7 +904,7 @@ export class RouterCore<
901
904
  routeTree!: TRouteTree
902
905
  routesById!: RoutesById<TRouteTree>
903
906
  routesByPath!: RoutesByPath<TRouteTree>
904
- flatRoutes!: Array<AnyRoute>
907
+ processedTree!: ProcessedTree<TRouteTree, any, any>
905
908
  isServer!: boolean
906
909
  pathParamsDecodeCharMap?: Map<string, string>
907
910
 
@@ -1093,18 +1096,22 @@ export class RouterCore<
1093
1096
  }
1094
1097
 
1095
1098
  buildRouteTree = () => {
1096
- const { routesById, routesByPath, flatRoutes } = processRouteTree({
1097
- routeTree: this.routeTree,
1098
- initRoute: (route, i) => {
1099
+ const { routesById, routesByPath, processedTree } = processRouteTree(
1100
+ this.routeTree,
1101
+ this.options.caseSensitive,
1102
+ (route, i) => {
1099
1103
  route.init({
1100
1104
  originalIndex: i,
1101
1105
  })
1102
1106
  },
1103
- })
1107
+ )
1108
+ if (this.options.routeMasks) {
1109
+ processRouteMasks(this.options.routeMasks, processedTree)
1110
+ }
1104
1111
 
1105
1112
  this.routesById = routesById as RoutesById<TRouteTree>
1106
1113
  this.routesByPath = routesByPath as RoutesByPath<TRouteTree>
1107
- this.flatRoutes = flatRoutes as Array<AnyRoute>
1114
+ this.processedTree = processedTree
1108
1115
 
1109
1116
  const notFoundRoute = this.options.notFoundRoute
1110
1117
 
@@ -1202,13 +1209,15 @@ export class RouterCore<
1202
1209
  return location
1203
1210
  }
1204
1211
 
1212
+ resolvePathCache = createLRUCache<string, string>(1000)
1213
+
1205
1214
  /** Resolve a path against the router basepath and trailing-slash policy. */
1206
1215
  resolvePathWithBase = (from: string, path: string) => {
1207
1216
  const resolvedPath = resolvePath({
1208
1217
  base: from,
1209
1218
  to: cleanPath(path),
1210
1219
  trailingSlash: this.options.trailingSlash,
1211
- parseCache: this.parsePathnameCache,
1220
+ cache: this.resolvePathCache,
1212
1221
  })
1213
1222
  return resolvedPath
1214
1223
  }
@@ -1241,7 +1250,6 @@ export class RouterCore<
1241
1250
  ): Array<AnyRouteMatch> {
1242
1251
  const { foundRoute, matchedRoutes, routeParams } = this.getMatchedRoutes(
1243
1252
  next.pathname,
1244
- opts?.dest?.to as string,
1245
1253
  )
1246
1254
  let isGlobalNotFound = false
1247
1255
 
@@ -1534,21 +1542,11 @@ export class RouterCore<
1534
1542
  return matches
1535
1543
  }
1536
1544
 
1537
- /** a cache for `parsePathname` */
1538
- private parsePathnameCache: ParsePathnameCache = createLRUCache(1000)
1539
-
1540
- getMatchedRoutes: GetMatchRoutesFn = (
1541
- pathname: string,
1542
- routePathname: string | undefined,
1543
- ) => {
1545
+ getMatchedRoutes: GetMatchRoutesFn = (pathname) => {
1544
1546
  return getMatchedRoutes({
1545
1547
  pathname,
1546
- routePathname,
1547
- caseSensitive: this.options.caseSensitive,
1548
- routesByPath: this.routesByPath,
1549
1548
  routesById: this.routesById,
1550
- flatRoutes: this.flatRoutes,
1551
- parseCache: this.parsePathnameCache,
1549
+ processedTree: this.processedTree,
1552
1550
  })
1553
1551
  }
1554
1552
 
@@ -1611,10 +1609,7 @@ export class RouterCore<
1611
1609
  process.env.NODE_ENV !== 'production' &&
1612
1610
  dest._isNavigate
1613
1611
  ) {
1614
- const allFromMatches = this.getMatchedRoutes(
1615
- dest.from,
1616
- undefined,
1617
- ).matchedRoutes
1612
+ const allFromMatches = this.getMatchedRoutes(dest.from).matchedRoutes
1618
1613
 
1619
1614
  const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
1620
1615
  return comparePaths(d.fullPath, dest.from!)
@@ -1665,7 +1660,6 @@ export class RouterCore<
1665
1660
  const interpolatedNextTo = interpolatePath({
1666
1661
  path: nextTo,
1667
1662
  params: nextParams,
1668
- parseCache: this.parsePathnameCache,
1669
1663
  }).interpolatedPath
1670
1664
 
1671
1665
  const destRoutes = this.matchRoutes(interpolatedNextTo, undefined, {
@@ -1692,7 +1686,6 @@ export class RouterCore<
1692
1686
  path: nextTo,
1693
1687
  params: nextParams,
1694
1688
  decodeCharMap: this.pathParamsDecodeCharMap,
1695
- parseCache: this.parsePathnameCache,
1696
1689
  }).interpolatedPath,
1697
1690
  )
1698
1691
 
@@ -1785,35 +1778,23 @@ export class RouterCore<
1785
1778
  let maskedNext = maskedDest ? build(maskedDest) : undefined
1786
1779
 
1787
1780
  if (!maskedNext) {
1788
- let params = {}
1781
+ const params = {}
1789
1782
 
1790
- const foundMask = this.options.routeMasks?.find((d) => {
1791
- const match = matchPathname(
1783
+ if (this.options.routeMasks) {
1784
+ const match = findFlatMatch<RouteMask<TRouteTree>>(
1792
1785
  next.pathname,
1793
- {
1794
- to: d.from,
1795
- caseSensitive: false,
1796
- fuzzy: false,
1797
- },
1798
- this.parsePathnameCache,
1786
+ this.processedTree,
1799
1787
  )
1800
-
1801
1788
  if (match) {
1802
- params = match
1803
- return true
1804
- }
1805
-
1806
- return false
1807
- })
1808
-
1809
- if (foundMask) {
1810
- const { from: _from, ...maskProps } = foundMask
1811
- maskedDest = {
1812
- from: opts.from,
1813
- ...maskProps,
1814
- params,
1789
+ Object.assign(params, match.params) // Copy params, because they're cached
1790
+ const { from: _from, ...maskProps } = match.route
1791
+ maskedDest = {
1792
+ from: opts.from,
1793
+ ...maskProps,
1794
+ params,
1795
+ }
1796
+ maskedNext = build(maskedDest)
1815
1797
  }
1816
- maskedNext = build(maskedDest)
1817
1798
  }
1818
1799
  }
1819
1800
 
@@ -2524,31 +2505,31 @@ export class RouterCore<
2524
2505
  ? this.latestLocation
2525
2506
  : this.state.resolvedLocation || this.state.location
2526
2507
 
2527
- const match = matchPathname(
2508
+ const match = findSingleMatch(
2509
+ next.pathname,
2510
+ opts?.caseSensitive ?? false,
2511
+ opts?.fuzzy ?? false,
2528
2512
  baseLocation.pathname,
2529
- {
2530
- ...opts,
2531
- to: next.pathname,
2532
- },
2533
- this.parsePathnameCache,
2534
- ) as any
2513
+ this.processedTree,
2514
+ )
2535
2515
 
2536
2516
  if (!match) {
2537
2517
  return false
2538
2518
  }
2519
+
2539
2520
  if (location.params) {
2540
- if (!deepEqual(match, location.params, { partial: true })) {
2521
+ if (!deepEqual(match.params, location.params, { partial: true })) {
2541
2522
  return false
2542
2523
  }
2543
2524
  }
2544
2525
 
2545
- if (match && (opts?.includeSearch ?? true)) {
2526
+ if (opts?.includeSearch ?? true) {
2546
2527
  return deepEqual(baseLocation.search, next.search, { partial: true })
2547
- ? match
2528
+ ? match.params
2548
2529
  : false
2549
2530
  }
2550
2531
 
2551
- return match
2532
+ return match.params
2552
2533
  }
2553
2534
 
2554
2535
  ssr?: {
@@ -2644,70 +2625,21 @@ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
2644
2625
  */
2645
2626
  export function getMatchedRoutes<TRouteLike extends RouteLike>({
2646
2627
  pathname,
2647
- routePathname,
2648
- caseSensitive,
2649
- routesByPath,
2650
2628
  routesById,
2651
- flatRoutes,
2652
- parseCache,
2629
+ processedTree,
2653
2630
  }: {
2654
2631
  pathname: string
2655
- routePathname?: string
2656
- caseSensitive?: boolean
2657
- routesByPath: Record<string, TRouteLike>
2658
2632
  routesById: Record<string, TRouteLike>
2659
- flatRoutes: Array<TRouteLike>
2660
- parseCache?: ParsePathnameCache
2633
+ processedTree: ProcessedTree<any, any, any>
2661
2634
  }) {
2662
- let routeParams: Record<string, string> = {}
2635
+ const routeParams: Record<string, string> = {}
2663
2636
  const trimmedPath = trimPathRight(pathname)
2664
- const getMatchedParams = (route: TRouteLike) => {
2665
- const result = matchPathname(
2666
- trimmedPath,
2667
- {
2668
- to: route.fullPath,
2669
- caseSensitive: route.options?.caseSensitive ?? caseSensitive,
2670
- // we need fuzzy matching for `notFoundMode: 'fuzzy'`
2671
- fuzzy: true,
2672
- },
2673
- parseCache,
2674
- )
2675
- return result
2676
- }
2677
2637
 
2678
- let foundRoute: TRouteLike | undefined =
2679
- routePathname !== undefined ? routesByPath[routePathname] : undefined
2680
- if (foundRoute) {
2681
- routeParams = getMatchedParams(foundRoute)!
2682
- } else {
2683
- // iterate over flatRoutes to find the best match
2684
- // if we find a fuzzy matching route, keep looking for a perfect fit
2685
- let fuzzyMatch:
2686
- | { foundRoute: TRouteLike; routeParams: Record<string, string> }
2687
- | undefined = undefined
2688
- for (const route of flatRoutes) {
2689
- const matchedParams = getMatchedParams(route)
2690
-
2691
- if (matchedParams) {
2692
- if (
2693
- route.path !== '/' &&
2694
- (matchedParams as Record<string, string>)['**']
2695
- ) {
2696
- if (!fuzzyMatch) {
2697
- fuzzyMatch = { foundRoute: route, routeParams: matchedParams }
2698
- }
2699
- } else {
2700
- foundRoute = route
2701
- routeParams = matchedParams
2702
- break
2703
- }
2704
- }
2705
- }
2706
- // did not find a perfect fit, so take the fuzzy matching route if it exists
2707
- if (!foundRoute && fuzzyMatch) {
2708
- foundRoute = fuzzyMatch.foundRoute
2709
- routeParams = fuzzyMatch.routeParams
2710
- }
2638
+ let foundRoute: TRouteLike | undefined = undefined
2639
+ const match = findRouteMatch<TRouteLike>(trimmedPath, processedTree, true)
2640
+ if (match) {
2641
+ foundRoute = match.route
2642
+ Object.assign(routeParams, match.params) // Copy params, because they're cached
2711
2643
  }
2712
2644
 
2713
2645
  let routeCursor: TRouteLike = foundRoute || routesById[rootRouteId]!
@@ -1 +1,2 @@
1
1
  export const GLOBAL_TSR = '$_TSR'
2
+ export declare const GLOBAL_SEROVAL: '$R'
@@ -6,11 +6,12 @@ import type { AnyRouter } from '../router'
6
6
  import type { Manifest } from '../manifest'
7
7
  import type { RouteContextOptions } from '../route'
8
8
  import type { AnySerializationAdapter } from './serializer/transformer'
9
- import type { GLOBAL_TSR } from './constants'
9
+ import type { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants'
10
10
 
11
11
  declare global {
12
12
  interface Window {
13
13
  [GLOBAL_TSR]?: TsrSsrGlobal
14
+ [GLOBAL_SEROVAL]?: any
14
15
  }
15
16
  }
16
17
 
@@ -25,6 +26,10 @@ export interface TsrSsrGlobal {
25
26
  t?: Map<string, (value: any) => any>
26
27
  // this flag indicates whether the transformers were initialized
27
28
  initialized?: boolean
29
+ // router is hydrated and doesnt need the streamed values anymore
30
+ hydrated?: boolean
31
+ // stream has ended
32
+ streamEnd?: boolean
28
33
  }
29
34
 
30
35
  function hydrateMatch(
@@ -165,6 +170,10 @@ export async function hydrate(router: AnyRouter): Promise<any> {
165
170
  // Allow the user to handle custom hydration data
166
171
  await router.options.hydrate?.(dehydratedData)
167
172
 
173
+ window.$_TSR.hydrated = true
174
+ // potentially clean up streamed values IF stream has ended already
175
+ window.$_TSR.c()
176
+
168
177
  // now that all necessary data is hydrated:
169
178
  // 1) fully reconstruct the route context
170
179
  // 2) execute `head()` and `scripts()` for each match
@@ -48,6 +48,54 @@ export function dehydrateMatch(match: AnyRouteMatch): DehydratedMatch {
48
48
  return dehydratedMatch
49
49
  }
50
50
 
51
+ const INITIAL_SCRIPTS = [
52
+ getCrossReferenceHeader(SCOPE_ID),
53
+ minifiedTsrBootStrapScript,
54
+ ]
55
+
56
+ class ScriptBuffer {
57
+ constructor(private router: AnyRouter) {}
58
+ private _queue: Array<string> = [...INITIAL_SCRIPTS]
59
+ private _scriptBarrierLifted = false
60
+
61
+ enqueue(script: string) {
62
+ if (this._scriptBarrierLifted && this._queue.length === 0) {
63
+ queueMicrotask(() => {
64
+ this.injectBufferedScripts()
65
+ })
66
+ }
67
+ this._queue.push(script)
68
+ }
69
+
70
+ liftBarrier() {
71
+ if (this._scriptBarrierLifted) return
72
+ this._scriptBarrierLifted = true
73
+ if (this._queue.length > 0) {
74
+ queueMicrotask(() => {
75
+ this.injectBufferedScripts()
76
+ })
77
+ }
78
+ }
79
+
80
+ takeAll() {
81
+ const bufferedScripts = this._queue
82
+ this._queue = []
83
+ if (bufferedScripts.length === 0) {
84
+ return undefined
85
+ }
86
+ bufferedScripts.push(`${GLOBAL_TSR}.c()`)
87
+ const joinedScripts = bufferedScripts.join(';')
88
+ return joinedScripts
89
+ }
90
+
91
+ injectBufferedScripts() {
92
+ const scriptsToInject = this.takeAll()
93
+ if (scriptsToInject) {
94
+ this.router.serverSsr!.injectScript(() => scriptsToInject)
95
+ }
96
+ }
97
+ }
98
+
51
99
  export function attachRouterServerSsrUtils({
52
100
  router,
53
101
  manifest,
@@ -58,16 +106,9 @@ export function attachRouterServerSsrUtils({
58
106
  router.ssr = {
59
107
  manifest,
60
108
  }
61
- let initialScriptSent = false
62
- const getInitialScript = () => {
63
- if (initialScriptSent) {
64
- return ''
65
- }
66
- initialScriptSent = true
67
- return `${getCrossReferenceHeader(SCOPE_ID)};${minifiedTsrBootStrapScript};`
68
- }
69
109
  let _dehydrated = false
70
110
  const listeners: Array<() => void> = []
111
+ const scriptBuffer = new ScriptBuffer(router)
71
112
 
72
113
  router.serverSsr = {
73
114
  injectedHtml: [],
@@ -84,7 +125,10 @@ export function attachRouterServerSsrUtils({
84
125
  injectScript: (getScript) => {
85
126
  return router.serverSsr!.injectHtml(async () => {
86
127
  const script = await getScript()
87
- return `<script ${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}'` : ''} class='$tsr'>${getInitialScript()}${script};$_TSR.c()</script>`
128
+ if (!script) {
129
+ return ''
130
+ }
131
+ return `<script${router.options.ssr?.nonce ? `nonce='${router.options.ssr.nonce}' ` : ''} class='$tsr'>${script}</script>`
88
132
  })
89
133
  },
90
134
  dehydrate: async () => {
@@ -104,7 +148,10 @@ export function attachRouterServerSsrUtils({
104
148
  if (lastMatchId) {
105
149
  dehydratedRouter.lastMatchId = lastMatchId
106
150
  }
107
- dehydratedRouter.dehydratedData = await router.options.dehydrate?.()
151
+ const dehydratedData = await router.options.dehydrate?.()
152
+ if (dehydratedData) {
153
+ dehydratedRouter.dehydratedData = dehydratedData
154
+ }
108
155
  _dehydrated = true
109
156
 
110
157
  const p = createControlledPromise<string>()
@@ -115,6 +162,7 @@ export function attachRouterServerSsrUtils({
115
162
  | Array<AnySerializationAdapter>
116
163
  | undefined
117
164
  )?.map((t) => makeSsrSerovalPlugin(t, trackPlugins)) ?? []
165
+
118
166
  crossSerializeStream(dehydratedRouter, {
119
167
  refs: new Map(),
120
168
  plugins: [...plugins, ...defaultSerovalPlugins],
@@ -123,10 +171,13 @@ export function attachRouterServerSsrUtils({
123
171
  if (trackPlugins.didRun) {
124
172
  serialized = GLOBAL_TSR + '.p(()=>' + serialized + ')'
125
173
  }
126
- router.serverSsr!.injectScript(() => serialized)
174
+ scriptBuffer.enqueue(serialized)
127
175
  },
128
176
  scopeId: SCOPE_ID,
129
- onDone: () => p.resolve(''),
177
+ onDone: () => {
178
+ scriptBuffer.enqueue(GLOBAL_TSR + '.streamEnd=true')
179
+ p.resolve('')
180
+ },
130
181
  onError: (err) => p.reject(err),
131
182
  })
132
183
  // make sure the stream is kept open until the promise is resolved
@@ -138,6 +189,12 @@ export function attachRouterServerSsrUtils({
138
189
  onRenderFinished: (listener) => listeners.push(listener),
139
190
  setRenderFinished: () => {
140
191
  listeners.forEach((l) => l())
192
+ scriptBuffer.liftBarrier()
193
+ },
194
+ takeBufferedScripts() {
195
+ const scripts = scriptBuffer.takeAll()
196
+ scriptBuffer.liftBarrier()
197
+ return scripts
141
198
  },
142
199
  }
143
200
  }
@@ -3,6 +3,10 @@ self.$_TSR = {
3
3
  document.querySelectorAll('.\\$tsr').forEach((o) => {
4
4
  o.remove()
5
5
  })
6
+ if (this.hydrated && this.streamEnd) {
7
+ delete self.$_TSR
8
+ delete self.$R['tsr']
9
+ }
6
10
  },
7
11
  p(script) {
8
12
  !this.initialized ? this.buffer.push(script) : script()
@@ -1,144 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const invariant = require("tiny-invariant");
4
- const path = require("./path.cjs");
5
- const SLASH_SCORE = 0.75;
6
- const STATIC_SEGMENT_SCORE = 1;
7
- const REQUIRED_PARAM_BASE_SCORE = 0.5;
8
- const OPTIONAL_PARAM_BASE_SCORE = 0.4;
9
- const WILDCARD_PARAM_BASE_SCORE = 0.25;
10
- const STATIC_AFTER_DYNAMIC_BONUS_SCORE = 0.2;
11
- const BOTH_PRESENCE_BASE_SCORE = 0.05;
12
- const PREFIX_PRESENCE_BASE_SCORE = 0.02;
13
- const SUFFIX_PRESENCE_BASE_SCORE = 0.01;
14
- const PREFIX_LENGTH_SCORE_MULTIPLIER = 2e-4;
15
- const SUFFIX_LENGTH_SCORE_MULTIPLIER = 1e-4;
16
- function handleParam(segment, baseScore) {
17
- if (segment.prefixSegment && segment.suffixSegment) {
18
- return baseScore + BOTH_PRESENCE_BASE_SCORE + PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length + SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length;
19
- }
20
- if (segment.prefixSegment) {
21
- return baseScore + PREFIX_PRESENCE_BASE_SCORE + PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length;
22
- }
23
- if (segment.suffixSegment) {
24
- return baseScore + SUFFIX_PRESENCE_BASE_SCORE + SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length;
25
- }
26
- return baseScore;
27
- }
28
- function sortRoutes(routes) {
29
- const scoredRoutes = [];
30
- routes.forEach((d, i) => {
31
- if (d.isRoot || !d.path) {
32
- return;
33
- }
34
- const trimmed = path.trimPathLeft(d.fullPath);
35
- let parsed = path.parsePathname(trimmed);
36
- let skip = 0;
37
- while (parsed.length > skip + 1 && parsed[skip]?.value === "/") {
38
- skip++;
39
- }
40
- if (skip > 0) parsed = parsed.slice(skip);
41
- let optionalParamCount = 0;
42
- let hasStaticAfter = false;
43
- const scores = parsed.map((segment, index) => {
44
- if (segment.value === "/") {
45
- return SLASH_SCORE;
46
- }
47
- if (segment.type === path.SEGMENT_TYPE_PATHNAME) {
48
- return STATIC_SEGMENT_SCORE;
49
- }
50
- let baseScore = void 0;
51
- if (segment.type === path.SEGMENT_TYPE_PARAM) {
52
- baseScore = REQUIRED_PARAM_BASE_SCORE;
53
- } else if (segment.type === path.SEGMENT_TYPE_OPTIONAL_PARAM) {
54
- baseScore = OPTIONAL_PARAM_BASE_SCORE;
55
- optionalParamCount++;
56
- } else {
57
- baseScore = WILDCARD_PARAM_BASE_SCORE;
58
- }
59
- for (let i2 = index + 1; i2 < parsed.length; i2++) {
60
- const nextSegment = parsed[i2];
61
- if (nextSegment.type === path.SEGMENT_TYPE_PATHNAME && nextSegment.value !== "/") {
62
- hasStaticAfter = true;
63
- return handleParam(
64
- segment,
65
- baseScore + STATIC_AFTER_DYNAMIC_BONUS_SCORE
66
- );
67
- }
68
- }
69
- return handleParam(segment, baseScore);
70
- });
71
- scoredRoutes.push({
72
- child: d,
73
- trimmed,
74
- parsed,
75
- index: i,
76
- scores,
77
- optionalParamCount,
78
- hasStaticAfter
79
- });
80
- });
81
- const flatRoutes = scoredRoutes.sort((a, b) => {
82
- const minLength = Math.min(a.scores.length, b.scores.length);
83
- for (let i = 0; i < minLength; i++) {
84
- if (a.scores[i] !== b.scores[i]) {
85
- return b.scores[i] - a.scores[i];
86
- }
87
- }
88
- if (a.scores.length !== b.scores.length) {
89
- if (a.optionalParamCount !== b.optionalParamCount) {
90
- if (a.hasStaticAfter === b.hasStaticAfter) {
91
- return a.optionalParamCount - b.optionalParamCount;
92
- } else if (a.hasStaticAfter && !b.hasStaticAfter) {
93
- return -1;
94
- } else if (!a.hasStaticAfter && b.hasStaticAfter) {
95
- return 1;
96
- }
97
- }
98
- return b.scores.length - a.scores.length;
99
- }
100
- for (let i = 0; i < minLength; i++) {
101
- if (a.parsed[i].value !== b.parsed[i].value) {
102
- return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
103
- }
104
- }
105
- return a.index - b.index;
106
- }).map((d, i) => {
107
- d.child.rank = i;
108
- return d.child;
109
- });
110
- return flatRoutes;
111
- }
112
- function processRouteTree({
113
- routeTree,
114
- initRoute
115
- }) {
116
- const routesById = {};
117
- const routesByPath = {};
118
- const recurseRoutes = (childRoutes) => {
119
- childRoutes.forEach((childRoute, i) => {
120
- initRoute?.(childRoute, i);
121
- const existingRoute = routesById[childRoute.id];
122
- invariant(
123
- !existingRoute,
124
- `Duplicate routes found with id: ${String(childRoute.id)}`
125
- );
126
- routesById[childRoute.id] = childRoute;
127
- if (!childRoute.isRoot && childRoute.path) {
128
- const trimmedFullPath = path.trimPathRight(childRoute.fullPath);
129
- if (!routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith("/")) {
130
- routesByPath[trimmedFullPath] = childRoute;
131
- }
132
- }
133
- const children = childRoute.children;
134
- if (children?.length) {
135
- recurseRoutes(children);
136
- }
137
- });
138
- };
139
- recurseRoutes([routeTree]);
140
- const flatRoutes = sortRoutes(Object.values(routesById));
141
- return { routesById, routesByPath, flatRoutes };
142
- }
143
- exports.processRouteTree = processRouteTree;
144
- //# sourceMappingURL=process-route-tree.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"process-route-tree.cjs","sources":["../../src/process-route-tree.ts"],"sourcesContent":["import invariant from 'tiny-invariant'\nimport {\n SEGMENT_TYPE_OPTIONAL_PARAM,\n SEGMENT_TYPE_PARAM,\n SEGMENT_TYPE_PATHNAME,\n parsePathname,\n trimPathLeft,\n trimPathRight,\n} from './path'\nimport type { Segment } from './path'\nimport type { RouteLike } from './route'\n\nconst SLASH_SCORE = 0.75\nconst STATIC_SEGMENT_SCORE = 1\nconst REQUIRED_PARAM_BASE_SCORE = 0.5\nconst OPTIONAL_PARAM_BASE_SCORE = 0.4\nconst WILDCARD_PARAM_BASE_SCORE = 0.25\nconst STATIC_AFTER_DYNAMIC_BONUS_SCORE = 0.2\nconst BOTH_PRESENCE_BASE_SCORE = 0.05\nconst PREFIX_PRESENCE_BASE_SCORE = 0.02\nconst SUFFIX_PRESENCE_BASE_SCORE = 0.01\nconst PREFIX_LENGTH_SCORE_MULTIPLIER = 0.0002\nconst SUFFIX_LENGTH_SCORE_MULTIPLIER = 0.0001\n\nfunction handleParam(segment: Segment, baseScore: number) {\n if (segment.prefixSegment && segment.suffixSegment) {\n return (\n baseScore +\n BOTH_PRESENCE_BASE_SCORE +\n PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length +\n SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length\n )\n }\n\n if (segment.prefixSegment) {\n return (\n baseScore +\n PREFIX_PRESENCE_BASE_SCORE +\n PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length\n )\n }\n\n if (segment.suffixSegment) {\n return (\n baseScore +\n SUFFIX_PRESENCE_BASE_SCORE +\n SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length\n )\n }\n\n return baseScore\n}\n\nfunction sortRoutes<TRouteLike extends RouteLike>(\n routes: ReadonlyArray<TRouteLike>,\n): Array<TRouteLike> {\n const scoredRoutes: Array<{\n child: TRouteLike\n trimmed: string\n parsed: ReadonlyArray<Segment>\n index: number\n scores: Array<number>\n hasStaticAfter: boolean\n optionalParamCount: number\n }> = []\n\n routes.forEach((d, i) => {\n if (d.isRoot || !d.path) {\n return\n }\n\n const trimmed = trimPathLeft(d.fullPath)\n let parsed = parsePathname(trimmed)\n\n // Removes the leading slash if it is not the only remaining segment\n let skip = 0\n while (parsed.length > skip + 1 && parsed[skip]?.value === '/') {\n skip++\n }\n if (skip > 0) parsed = parsed.slice(skip)\n\n let optionalParamCount = 0\n let hasStaticAfter = false\n const scores = parsed.map((segment, index) => {\n if (segment.value === '/') {\n return SLASH_SCORE\n }\n\n if (segment.type === SEGMENT_TYPE_PATHNAME) {\n return STATIC_SEGMENT_SCORE\n }\n\n let baseScore: number | undefined = undefined\n if (segment.type === SEGMENT_TYPE_PARAM) {\n baseScore = REQUIRED_PARAM_BASE_SCORE\n } else if (segment.type === SEGMENT_TYPE_OPTIONAL_PARAM) {\n baseScore = OPTIONAL_PARAM_BASE_SCORE\n optionalParamCount++\n } else {\n baseScore = WILDCARD_PARAM_BASE_SCORE\n }\n\n // if there is any static segment (that is not an index) after a required / optional param,\n // we will boost this param so it ranks higher than a required/optional param without a static segment after it\n // JUST FOR SORTING, NOT FOR MATCHING\n for (let i = index + 1; i < parsed.length; i++) {\n const nextSegment = parsed[i]!\n if (\n nextSegment.type === SEGMENT_TYPE_PATHNAME &&\n nextSegment.value !== '/'\n ) {\n hasStaticAfter = true\n return handleParam(\n segment,\n baseScore + STATIC_AFTER_DYNAMIC_BONUS_SCORE,\n )\n }\n }\n\n return handleParam(segment, baseScore)\n })\n\n scoredRoutes.push({\n child: d,\n trimmed,\n parsed,\n index: i,\n scores,\n optionalParamCount,\n hasStaticAfter,\n })\n })\n\n const flatRoutes = scoredRoutes\n .sort((a, b) => {\n const minLength = Math.min(a.scores.length, b.scores.length)\n\n // Sort by segment-by-segment score comparison ONLY for the common prefix\n for (let i = 0; i < minLength; i++) {\n if (a.scores[i] !== b.scores[i]) {\n return b.scores[i]! - a.scores[i]!\n }\n }\n\n // If all common segments have equal scores, then consider length and specificity\n if (a.scores.length !== b.scores.length) {\n // If different number of optional parameters, fewer optional parameters wins (more specific)\n // only if both or none of the routes has static segments after the params\n if (a.optionalParamCount !== b.optionalParamCount) {\n if (a.hasStaticAfter === b.hasStaticAfter) {\n return a.optionalParamCount - b.optionalParamCount\n } else if (a.hasStaticAfter && !b.hasStaticAfter) {\n return -1\n } else if (!a.hasStaticAfter && b.hasStaticAfter) {\n return 1\n }\n }\n\n // If same number of optional parameters, longer path wins (for static segments)\n return b.scores.length - a.scores.length\n }\n\n // Sort by min available parsed value for alphabetical ordering\n for (let i = 0; i < minLength; i++) {\n if (a.parsed[i]!.value !== b.parsed[i]!.value) {\n return a.parsed[i]!.value > b.parsed[i]!.value ? 1 : -1\n }\n }\n\n // Sort by original index\n return a.index - b.index\n })\n .map((d, i) => {\n d.child.rank = i\n return d.child\n })\n\n return flatRoutes\n}\n\nexport type ProcessRouteTreeResult<TRouteLike extends RouteLike> = {\n routesById: Record<string, TRouteLike>\n routesByPath: Record<string, TRouteLike>\n flatRoutes: Array<TRouteLike>\n}\n\n/**\n * Build lookup maps and a specificity-sorted flat list from a route tree.\n * Returns `routesById`, `routesByPath`, and `flatRoutes`.\n */\n/**\n * Build lookup maps and a specificity-sorted flat list from a route tree.\n * Returns `routesById`, `routesByPath`, and `flatRoutes`.\n */\nexport function processRouteTree<TRouteLike extends RouteLike>({\n routeTree,\n initRoute,\n}: {\n routeTree: TRouteLike\n initRoute?: (route: TRouteLike, index: number) => void\n}): ProcessRouteTreeResult<TRouteLike> {\n const routesById = {} as Record<string, TRouteLike>\n const routesByPath = {} as Record<string, TRouteLike>\n\n const recurseRoutes = (childRoutes: Array<TRouteLike>) => {\n childRoutes.forEach((childRoute, i) => {\n initRoute?.(childRoute, i)\n\n const existingRoute = routesById[childRoute.id]\n\n invariant(\n !existingRoute,\n `Duplicate routes found with id: ${String(childRoute.id)}`,\n )\n\n routesById[childRoute.id] = childRoute\n\n if (!childRoute.isRoot && childRoute.path) {\n const trimmedFullPath = trimPathRight(childRoute.fullPath)\n if (\n !routesByPath[trimmedFullPath] ||\n childRoute.fullPath.endsWith('/')\n ) {\n routesByPath[trimmedFullPath] = childRoute\n }\n }\n\n const children = childRoute.children as Array<TRouteLike>\n\n if (children?.length) {\n recurseRoutes(children)\n }\n })\n }\n\n recurseRoutes([routeTree])\n\n const flatRoutes = sortRoutes(Object.values(routesById))\n\n return { routesById, routesByPath, flatRoutes }\n}\n"],"names":["trimPathLeft","parsePathname","SEGMENT_TYPE_PATHNAME","SEGMENT_TYPE_PARAM","SEGMENT_TYPE_OPTIONAL_PARAM","i","trimPathRight"],"mappings":"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"}