@tanstack/vue-router 0.0.1 → 1.140.1

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 (268) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -45
  3. package/dist/esm/Asset.d.ts +2 -0
  4. package/dist/esm/Asset.js +33 -0
  5. package/dist/esm/Asset.js.map +1 -0
  6. package/dist/esm/CatchBoundary.d.ts +19 -0
  7. package/dist/esm/CatchBoundary.js +135 -0
  8. package/dist/esm/CatchBoundary.js.map +1 -0
  9. package/dist/esm/ClientOnly.d.ts +67 -0
  10. package/dist/esm/HeadContent.d.ts +10 -0
  11. package/dist/esm/HeadContent.js +116 -0
  12. package/dist/esm/HeadContent.js.map +1 -0
  13. package/dist/esm/Match.d.ts +25 -0
  14. package/dist/esm/Match.js +262 -0
  15. package/dist/esm/Match.js.map +1 -0
  16. package/dist/esm/Matches.d.ts +39 -0
  17. package/dist/esm/Matches.js +186 -0
  18. package/dist/esm/Matches.js.map +1 -0
  19. package/dist/esm/RouterProvider.d.ts +33 -0
  20. package/dist/esm/RouterProvider.js +65 -0
  21. package/dist/esm/RouterProvider.js.map +1 -0
  22. package/dist/esm/SafeFragment.d.ts +4 -0
  23. package/dist/esm/ScriptOnce.d.ts +5 -0
  24. package/dist/esm/ScriptOnce.js +21 -0
  25. package/dist/esm/ScriptOnce.js.map +1 -0
  26. package/dist/esm/Scripts.d.ts +1 -0
  27. package/dist/esm/Scripts.js +46 -0
  28. package/dist/esm/Scripts.js.map +1 -0
  29. package/dist/esm/ScrollRestoration.d.ts +14 -0
  30. package/dist/esm/ScrollRestoration.js +36 -0
  31. package/dist/esm/ScrollRestoration.js.map +1 -0
  32. package/dist/esm/Transitioner.d.ts +2 -0
  33. package/dist/esm/Transitioner.js +154 -0
  34. package/dist/esm/Transitioner.js.map +1 -0
  35. package/dist/esm/awaited.d.ts +12 -0
  36. package/dist/esm/awaited.js +40 -0
  37. package/dist/esm/awaited.js.map +1 -0
  38. package/dist/esm/fileRoute.d.ts +54 -0
  39. package/dist/esm/fileRoute.js +103 -0
  40. package/dist/esm/fileRoute.js.map +1 -0
  41. package/dist/esm/history.d.ts +8 -0
  42. package/dist/esm/index.d.ts +51 -0
  43. package/dist/esm/index.js +138 -0
  44. package/dist/esm/index.js.map +1 -0
  45. package/dist/esm/lazyRouteComponent.d.ts +8 -0
  46. package/dist/esm/lazyRouteComponent.js +106 -0
  47. package/dist/esm/lazyRouteComponent.js.map +1 -0
  48. package/dist/esm/link.d.ts +61 -0
  49. package/dist/esm/link.js +376 -0
  50. package/dist/esm/link.js.map +1 -0
  51. package/dist/esm/matchContext.d.ts +20 -0
  52. package/dist/esm/matchContext.js +16 -0
  53. package/dist/esm/matchContext.js.map +1 -0
  54. package/dist/esm/not-found.d.ts +12 -0
  55. package/dist/esm/not-found.js +45 -0
  56. package/dist/esm/not-found.js.map +1 -0
  57. package/dist/esm/renderRouteNotFound.d.ts +11 -0
  58. package/dist/esm/renderRouteNotFound.js +19 -0
  59. package/dist/esm/renderRouteNotFound.js.map +1 -0
  60. package/dist/esm/route.d.ts +96 -0
  61. package/dist/esm/route.js +176 -0
  62. package/dist/esm/route.js.map +1 -0
  63. package/dist/esm/router.d.ts +69 -0
  64. package/dist/esm/router.js +14 -0
  65. package/dist/esm/router.js.map +1 -0
  66. package/dist/esm/routerContext.d.ts +21 -0
  67. package/dist/esm/routerContext.js +21 -0
  68. package/dist/esm/routerContext.js.map +1 -0
  69. package/dist/esm/scroll-restoration.d.ts +1 -0
  70. package/dist/esm/scroll-restoration.js +21 -0
  71. package/dist/esm/scroll-restoration.js.map +1 -0
  72. package/dist/esm/typePrimitives.d.ts +10 -0
  73. package/dist/esm/useBlocker.d.ts +66 -0
  74. package/dist/esm/useBlocker.js +295 -0
  75. package/dist/esm/useBlocker.js.map +1 -0
  76. package/dist/esm/useCanGoBack.d.ts +1 -0
  77. package/dist/esm/useCanGoBack.js +8 -0
  78. package/dist/esm/useCanGoBack.js.map +1 -0
  79. package/dist/esm/useLoaderData.d.ts +8 -0
  80. package/dist/esm/useLoaderData.js +14 -0
  81. package/dist/esm/useLoaderData.js.map +1 -0
  82. package/dist/esm/useLoaderDeps.d.ts +7 -0
  83. package/dist/esm/useLoaderDeps.js +17 -0
  84. package/dist/esm/useLoaderDeps.js.map +1 -0
  85. package/dist/esm/useLocation.d.ts +7 -0
  86. package/dist/esm/useLocation.js +10 -0
  87. package/dist/esm/useLocation.js.map +1 -0
  88. package/dist/esm/useMatch.d.ts +10 -0
  89. package/dist/esm/useMatch.js +39 -0
  90. package/dist/esm/useMatch.js.map +1 -0
  91. package/dist/esm/useNavigate.d.ts +5 -0
  92. package/dist/esm/useNavigate.js +29 -0
  93. package/dist/esm/useNavigate.js.map +1 -0
  94. package/dist/esm/useParams.d.ts +9 -0
  95. package/dist/esm/useParams.js +15 -0
  96. package/dist/esm/useParams.js.map +1 -0
  97. package/dist/esm/useRouteContext.d.ts +4 -0
  98. package/dist/esm/useRouteContext.js +11 -0
  99. package/dist/esm/useRouteContext.js.map +1 -0
  100. package/dist/esm/useRouter.d.ts +4 -0
  101. package/dist/esm/useRouter.js +12 -0
  102. package/dist/esm/useRouter.js.map +1 -0
  103. package/dist/esm/useRouterState.d.ts +8 -0
  104. package/dist/esm/useRouterState.js +20 -0
  105. package/dist/esm/useRouterState.js.map +1 -0
  106. package/dist/esm/useSearch.d.ts +9 -0
  107. package/dist/esm/useSearch.js +15 -0
  108. package/dist/esm/useSearch.js.map +1 -0
  109. package/dist/esm/utils.d.ts +40 -0
  110. package/dist/esm/utils.js +44 -0
  111. package/dist/esm/utils.js.map +1 -0
  112. package/dist/source/Asset.d.ts +2 -0
  113. package/dist/source/Asset.jsx +22 -0
  114. package/dist/source/Asset.jsx.map +1 -0
  115. package/dist/source/CatchBoundary.d.ts +19 -0
  116. package/dist/source/CatchBoundary.jsx +134 -0
  117. package/dist/source/CatchBoundary.jsx.map +1 -0
  118. package/dist/source/ClientOnly.d.ts +67 -0
  119. package/dist/source/ClientOnly.jsx +63 -0
  120. package/dist/source/ClientOnly.jsx.map +1 -0
  121. package/dist/source/HeadContent.d.ts +10 -0
  122. package/dist/source/HeadContent.jsx +133 -0
  123. package/dist/source/HeadContent.jsx.map +1 -0
  124. package/dist/source/Match.d.ts +25 -0
  125. package/dist/source/Match.jsx +316 -0
  126. package/dist/source/Match.jsx.map +1 -0
  127. package/dist/source/Matches.d.ts +39 -0
  128. package/dist/source/Matches.jsx +191 -0
  129. package/dist/source/Matches.jsx.map +1 -0
  130. package/dist/source/RouterProvider.d.ts +33 -0
  131. package/dist/source/RouterProvider.jsx +63 -0
  132. package/dist/source/RouterProvider.jsx.map +1 -0
  133. package/dist/source/SafeFragment.d.ts +4 -0
  134. package/dist/source/SafeFragment.jsx +10 -0
  135. package/dist/source/SafeFragment.jsx.map +1 -0
  136. package/dist/source/ScriptOnce.d.ts +5 -0
  137. package/dist/source/ScriptOnce.jsx +17 -0
  138. package/dist/source/ScriptOnce.jsx.map +1 -0
  139. package/dist/source/Scripts.d.ts +1 -0
  140. package/dist/source/Scripts.jsx +49 -0
  141. package/dist/source/Scripts.jsx.map +1 -0
  142. package/dist/source/ScrollRestoration.d.ts +14 -0
  143. package/dist/source/ScrollRestoration.jsx +37 -0
  144. package/dist/source/ScrollRestoration.jsx.map +1 -0
  145. package/dist/source/Transitioner.d.ts +2 -0
  146. package/dist/source/Transitioner.jsx +181 -0
  147. package/dist/source/Transitioner.jsx.map +1 -0
  148. package/dist/source/awaited.d.ts +12 -0
  149. package/dist/source/awaited.jsx +38 -0
  150. package/dist/source/awaited.jsx.map +1 -0
  151. package/dist/source/fileRoute.d.ts +54 -0
  152. package/dist/source/fileRoute.js +98 -0
  153. package/dist/source/fileRoute.js.map +1 -0
  154. package/dist/source/history.d.ts +8 -0
  155. package/dist/source/history.js +2 -0
  156. package/dist/source/history.js.map +1 -0
  157. package/dist/source/index.d.ts +51 -0
  158. package/dist/source/index.jsx +40 -0
  159. package/dist/source/index.jsx.map +1 -0
  160. package/dist/source/lazyRouteComponent.d.ts +8 -0
  161. package/dist/source/lazyRouteComponent.jsx +135 -0
  162. package/dist/source/lazyRouteComponent.jsx.map +1 -0
  163. package/dist/source/link.d.ts +61 -0
  164. package/dist/source/link.jsx +495 -0
  165. package/dist/source/link.jsx.map +1 -0
  166. package/dist/source/matchContext.d.ts +20 -0
  167. package/dist/source/matchContext.jsx +32 -0
  168. package/dist/source/matchContext.jsx.map +1 -0
  169. package/dist/source/not-found.d.ts +12 -0
  170. package/dist/source/not-found.jsx +48 -0
  171. package/dist/source/not-found.jsx.map +1 -0
  172. package/dist/source/renderRouteNotFound.d.ts +11 -0
  173. package/dist/source/renderRouteNotFound.jsx +24 -0
  174. package/dist/source/renderRouteNotFound.jsx.map +1 -0
  175. package/dist/source/route.d.ts +97 -0
  176. package/dist/source/route.js +167 -0
  177. package/dist/source/route.js.map +1 -0
  178. package/dist/source/router.d.ts +70 -0
  179. package/dist/source/router.js +10 -0
  180. package/dist/source/router.js.map +1 -0
  181. package/dist/source/routerContext.d.ts +21 -0
  182. package/dist/source/routerContext.jsx +37 -0
  183. package/dist/source/routerContext.jsx.map +1 -0
  184. package/dist/source/scroll-restoration.d.ts +1 -0
  185. package/dist/source/scroll-restoration.jsx +16 -0
  186. package/dist/source/scroll-restoration.jsx.map +1 -0
  187. package/dist/source/typePrimitives.d.ts +10 -0
  188. package/dist/source/typePrimitives.js +2 -0
  189. package/dist/source/typePrimitives.js.map +1 -0
  190. package/dist/source/useBlocker.d.ts +66 -0
  191. package/dist/source/useBlocker.jsx +308 -0
  192. package/dist/source/useBlocker.jsx.map +1 -0
  193. package/dist/source/useCanGoBack.d.ts +1 -0
  194. package/dist/source/useCanGoBack.js +5 -0
  195. package/dist/source/useCanGoBack.js.map +1 -0
  196. package/dist/source/useLoaderData.d.ts +8 -0
  197. package/dist/source/useLoaderData.jsx +11 -0
  198. package/dist/source/useLoaderData.jsx.map +1 -0
  199. package/dist/source/useLoaderDeps.d.ts +7 -0
  200. package/dist/source/useLoaderDeps.jsx +11 -0
  201. package/dist/source/useLoaderDeps.jsx.map +1 -0
  202. package/dist/source/useLocation.d.ts +7 -0
  203. package/dist/source/useLocation.jsx +7 -0
  204. package/dist/source/useLocation.jsx.map +1 -0
  205. package/dist/source/useMatch.d.ts +10 -0
  206. package/dist/source/useMatch.jsx +46 -0
  207. package/dist/source/useMatch.jsx.map +1 -0
  208. package/dist/source/useNavigate.d.ts +5 -0
  209. package/dist/source/useNavigate.jsx +18 -0
  210. package/dist/source/useNavigate.jsx.map +1 -0
  211. package/dist/source/useParams.d.ts +9 -0
  212. package/dist/source/useParams.jsx +12 -0
  213. package/dist/source/useParams.jsx.map +1 -0
  214. package/dist/source/useRouteContext.d.ts +4 -0
  215. package/dist/source/useRouteContext.js +8 -0
  216. package/dist/source/useRouteContext.js.map +1 -0
  217. package/dist/source/useRouter.d.ts +4 -0
  218. package/dist/source/useRouter.jsx +9 -0
  219. package/dist/source/useRouter.jsx.map +1 -0
  220. package/dist/source/useRouterState.d.ts +8 -0
  221. package/dist/source/useRouterState.jsx +19 -0
  222. package/dist/source/useRouterState.jsx.map +1 -0
  223. package/dist/source/useSearch.d.ts +9 -0
  224. package/dist/source/useSearch.jsx +12 -0
  225. package/dist/source/useSearch.jsx.map +1 -0
  226. package/dist/source/utils.d.ts +40 -0
  227. package/dist/source/utils.js +78 -0
  228. package/dist/source/utils.js.map +1 -0
  229. package/package.json +77 -7
  230. package/src/Asset.tsx +23 -0
  231. package/src/CatchBoundary.tsx +186 -0
  232. package/src/ClientOnly.tsx +75 -0
  233. package/src/HeadContent.tsx +159 -0
  234. package/src/Match.tsx +415 -0
  235. package/src/Matches.tsx +349 -0
  236. package/src/RouterProvider.tsx +117 -0
  237. package/src/SafeFragment.tsx +10 -0
  238. package/src/ScriptOnce.tsx +30 -0
  239. package/src/Scripts.tsx +65 -0
  240. package/src/ScrollRestoration.tsx +69 -0
  241. package/src/Transitioner.tsx +213 -0
  242. package/src/awaited.tsx +54 -0
  243. package/src/fileRoute.ts +271 -0
  244. package/src/history.ts +9 -0
  245. package/src/index.tsx +346 -0
  246. package/src/lazyRouteComponent.tsx +173 -0
  247. package/src/link.tsx +765 -0
  248. package/src/matchContext.tsx +41 -0
  249. package/src/not-found.tsx +55 -0
  250. package/src/renderRouteNotFound.tsx +35 -0
  251. package/src/route.ts +658 -0
  252. package/src/router.ts +103 -0
  253. package/src/routerContext.tsx +53 -0
  254. package/src/scroll-restoration.tsx +29 -0
  255. package/src/typePrimitives.ts +74 -0
  256. package/src/useBlocker.tsx +501 -0
  257. package/src/useCanGoBack.ts +5 -0
  258. package/src/useLoaderData.tsx +50 -0
  259. package/src/useLoaderDeps.tsx +46 -0
  260. package/src/useLocation.tsx +30 -0
  261. package/src/useMatch.tsx +127 -0
  262. package/src/useNavigate.tsx +40 -0
  263. package/src/useParams.tsx +71 -0
  264. package/src/useRouteContext.ts +31 -0
  265. package/src/useRouter.tsx +15 -0
  266. package/src/useRouterState.tsx +43 -0
  267. package/src/useSearch.tsx +71 -0
  268. package/src/utils.ts +111 -0
package/src/Match.tsx ADDED
@@ -0,0 +1,415 @@
1
+ import * as Vue from 'vue'
2
+ import invariant from 'tiny-invariant'
3
+ import warning from 'tiny-warning'
4
+ import {
5
+ createControlledPromise,
6
+ getLocationChangeInfo,
7
+ isNotFound,
8
+ isRedirect,
9
+ rootRouteId,
10
+ } from '@tanstack/router-core'
11
+ import { CatchBoundary, ErrorComponent } from './CatchBoundary'
12
+ import { useRouterState } from './useRouterState'
13
+ import { useRouter } from './useRouter'
14
+ import { CatchNotFound } from './not-found'
15
+ import { matchContext } from './matchContext'
16
+ import { renderRouteNotFound } from './renderRouteNotFound'
17
+ import { ScrollRestoration } from './scroll-restoration'
18
+ import type { VNode } from 'vue'
19
+ import type { AnyRoute } from '@tanstack/router-core'
20
+
21
+ export const Match = Vue.defineComponent({
22
+ name: 'Match',
23
+ props: {
24
+ matchId: {
25
+ type: String,
26
+ required: true,
27
+ },
28
+ },
29
+ setup(props) {
30
+ const router = useRouter()
31
+ const routeId = useRouterState({
32
+ select: (s) => {
33
+ return s.matches.find((d) => d.id === props.matchId)?.routeId as string
34
+ },
35
+ })
36
+
37
+ invariant(
38
+ routeId.value,
39
+ `Could not find routeId for matchId "${props.matchId}". Please file an issue!`,
40
+ )
41
+
42
+ const route = Vue.computed(() => router.routesById[routeId.value])
43
+
44
+ const PendingComponent = Vue.computed(
45
+ () =>
46
+ route.value?.options?.pendingComponent ??
47
+ router?.options?.defaultPendingComponent,
48
+ )
49
+
50
+ const routeErrorComponent = Vue.computed(
51
+ () =>
52
+ route.value?.options?.errorComponent ??
53
+ router?.options?.defaultErrorComponent,
54
+ )
55
+
56
+ const routeOnCatch = Vue.computed(
57
+ () => route.value?.options?.onCatch ?? router?.options?.defaultOnCatch,
58
+ )
59
+
60
+ const routeNotFoundComponent = Vue.computed(() =>
61
+ route.value?.isRoot
62
+ ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
63
+ (route.value?.options?.notFoundComponent ??
64
+ router?.options?.notFoundRoute?.options?.component)
65
+ : route.value?.options?.notFoundComponent,
66
+ )
67
+
68
+ const resetKey = useRouterState({
69
+ select: (s) => s.loadedAt,
70
+ })
71
+
72
+ const parentRouteId = useRouterState({
73
+ select: (s) => {
74
+ const index = s.matches.findIndex((d) => d.id === props.matchId)
75
+ return s.matches[index - 1]?.routeId as string
76
+ },
77
+ })
78
+
79
+ // Create a ref for the current matchId that we can provide to child components
80
+ const matchIdRef = Vue.ref(props.matchId)
81
+
82
+ // When props.matchId changes, update the ref
83
+ Vue.watch(
84
+ () => props.matchId,
85
+ (newMatchId) => {
86
+ matchIdRef.value = newMatchId
87
+ },
88
+ { immediate: true },
89
+ )
90
+
91
+ // Provide the matchId to child components
92
+ Vue.provide(matchContext, matchIdRef)
93
+
94
+ return (): VNode => {
95
+ // Determine which components to render
96
+ let content: VNode = Vue.h(MatchInner, { matchId: props.matchId })
97
+
98
+ // Wrap in NotFound boundary if needed
99
+ if (routeNotFoundComponent.value) {
100
+ content = Vue.h(CatchNotFound, {
101
+ fallback: (error: any) => {
102
+ // If the current not found handler doesn't exist or it has a
103
+ // route ID which doesn't match the current route, rethrow the error
104
+ if (
105
+ !routeNotFoundComponent.value ||
106
+ (error.routeId && error.routeId !== routeId.value) ||
107
+ (!error.routeId && route.value && !route.value.isRoot)
108
+ )
109
+ throw error
110
+
111
+ return Vue.h(routeNotFoundComponent.value, error)
112
+ },
113
+ children: content,
114
+ })
115
+ }
116
+
117
+ // Wrap in error boundary if needed
118
+ if (routeErrorComponent.value) {
119
+ content = CatchBoundary({
120
+ getResetKey: () => resetKey.value,
121
+ errorComponent: routeErrorComponent.value || ErrorComponent,
122
+ onCatch: (error: Error) => {
123
+ // Forward not found errors (we don't want to show the error component for these)
124
+ if (isNotFound(error)) throw error
125
+ warning(false, `Error in route match: ${props.matchId}`)
126
+ routeOnCatch.value?.(error)
127
+ },
128
+ children: content,
129
+ })
130
+ }
131
+
132
+ // Wrap in suspense if needed
133
+ // Root routes should also wrap in Suspense if they have a pendingComponent
134
+ const needsSuspense =
135
+ route.value &&
136
+ (route.value?.options?.wrapInSuspense ??
137
+ PendingComponent.value ??
138
+ false)
139
+
140
+ if (needsSuspense) {
141
+ content = Vue.h(
142
+ Vue.Suspense,
143
+ {
144
+ fallback: PendingComponent.value
145
+ ? Vue.h(PendingComponent.value)
146
+ : null,
147
+ },
148
+ {
149
+ default: () => content,
150
+ },
151
+ )
152
+ }
153
+
154
+ // Add scroll restoration if needed
155
+ const withScrollRestoration: Array<VNode> = [
156
+ content,
157
+ parentRouteId.value === rootRouteId && router.options.scrollRestoration
158
+ ? Vue.h(Vue.Fragment, null, [
159
+ Vue.h(OnRendered),
160
+ Vue.h(ScrollRestoration),
161
+ ])
162
+ : null,
163
+ ].filter(Boolean) as Array<VNode>
164
+
165
+ return Vue.h(Vue.Fragment, null, withScrollRestoration)
166
+ }
167
+ },
168
+ })
169
+
170
+ // On Rendered can't happen above the root layout because it actually
171
+ // renders a dummy dom element to track the rendered state of the app.
172
+ // We render a script tag with a key that changes based on the current
173
+ // location state.key. Also, because it's below the root layout, it
174
+ // allows us to fire onRendered events even after a hydration mismatch
175
+ // error that occurred above the root layout (like bad head/link tags,
176
+ // which is common).
177
+ const OnRendered = Vue.defineComponent({
178
+ name: 'OnRendered',
179
+ setup() {
180
+ const router = useRouter()
181
+
182
+ const location = useRouterState({
183
+ select: (s) => {
184
+ return s.resolvedLocation?.state.key
185
+ },
186
+ })
187
+
188
+ Vue.watchEffect(() => {
189
+ if (location.value) {
190
+ router.emit({
191
+ type: 'onRendered',
192
+ ...getLocationChangeInfo(router.state),
193
+ })
194
+ }
195
+ })
196
+
197
+ return () => null
198
+ },
199
+ })
200
+
201
+ export const MatchInner = Vue.defineComponent({
202
+ name: 'MatchInner',
203
+ props: {
204
+ matchId: {
205
+ type: String,
206
+ required: true,
207
+ },
208
+ },
209
+ setup(props) {
210
+ const router = useRouter()
211
+
212
+ // { match, key, routeId } =
213
+ const matchState = useRouterState({
214
+ select: (s) => {
215
+ const match = s.matches.find((d) => d.id === props.matchId)
216
+
217
+ // During navigation transitions, matches can be temporarily removed
218
+ if (!match) {
219
+ return null
220
+ }
221
+
222
+ const routeId = match.routeId as string
223
+
224
+ const remountFn =
225
+ (router.routesById[routeId] as AnyRoute).options.remountDeps ??
226
+ router.options.defaultRemountDeps
227
+ const remountDeps = remountFn?.({
228
+ routeId,
229
+ loaderDeps: match.loaderDeps,
230
+ params: match._strictParams,
231
+ search: match._strictSearch,
232
+ })
233
+ const key = remountDeps ? JSON.stringify(remountDeps) : undefined
234
+
235
+ return {
236
+ key,
237
+ routeId,
238
+ match: {
239
+ id: match.id,
240
+ status: match.status,
241
+ error: match.error,
242
+ },
243
+ }
244
+ },
245
+ })
246
+
247
+ const route = Vue.computed(() => {
248
+ if (!matchState.value) return null
249
+ return router.routesById[matchState.value.routeId]!
250
+ })
251
+
252
+ const match = Vue.computed(() => matchState.value?.match)
253
+
254
+ const out = Vue.computed((): VNode | null => {
255
+ if (!route.value) return null
256
+ const Comp =
257
+ route.value.options.component ?? router.options.defaultComponent
258
+ if (Comp) {
259
+ return Vue.h(Comp)
260
+ }
261
+ return Vue.h(Outlet)
262
+ })
263
+
264
+ return (): VNode | null => {
265
+ // If match doesn't exist, return null (component is being unmounted or not ready)
266
+ if (!matchState.value || !match.value || !route.value) {
267
+ return null
268
+ }
269
+
270
+ // Handle different match statuses
271
+ if (match.value.status === 'notFound') {
272
+ invariant(isNotFound(match.value.error), 'Expected a notFound error')
273
+ return renderRouteNotFound(router, route.value, match.value.error)
274
+ }
275
+
276
+ if (match.value.status === 'redirected') {
277
+ invariant(isRedirect(match.value.error), 'Expected a redirect error')
278
+ throw router.getMatch(match.value.id)?._nonReactive.loadPromise
279
+ }
280
+
281
+ if (match.value.status === 'error') {
282
+ // Check if this route or any parent has an error component
283
+ const RouteErrorComponent =
284
+ route.value.options.errorComponent ??
285
+ router.options.defaultErrorComponent
286
+
287
+ // If this route has an error component, render it directly
288
+ // This is more reliable than relying on Vue's error boundary
289
+ if (RouteErrorComponent) {
290
+ return Vue.h(RouteErrorComponent, {
291
+ error: match.value.error,
292
+ reset: () => {
293
+ router.invalidate()
294
+ },
295
+ info: {
296
+ componentStack: '',
297
+ },
298
+ })
299
+ }
300
+
301
+ // If there's no error component for this route, throw the error
302
+ // so it can bubble up to the nearest parent with an error component
303
+ throw match.value.error
304
+ }
305
+
306
+ if (match.value.status === 'pending') {
307
+ const pendingMinMs =
308
+ route.value.options.pendingMinMs ?? router.options.defaultPendingMinMs
309
+
310
+ const routerMatch = router.getMatch(match.value.id)
311
+ if (
312
+ pendingMinMs &&
313
+ routerMatch &&
314
+ !routerMatch._nonReactive.minPendingPromise
315
+ ) {
316
+ // Create a promise that will resolve after the minPendingMs
317
+ if (!router.isServer) {
318
+ const minPendingPromise = createControlledPromise<void>()
319
+
320
+ routerMatch._nonReactive.minPendingPromise = minPendingPromise
321
+
322
+ setTimeout(() => {
323
+ minPendingPromise.resolve()
324
+ // We've handled the minPendingPromise, so we can delete it
325
+ routerMatch._nonReactive.minPendingPromise = undefined
326
+ }, pendingMinMs)
327
+ }
328
+ }
329
+
330
+ // In Vue, we render the pending component directly instead of throwing a promise
331
+ // because Vue's Suspense doesn't catch thrown promises like React does
332
+ const PendingComponent =
333
+ route.value.options.pendingComponent ??
334
+ router.options.defaultPendingComponent
335
+
336
+ if (PendingComponent) {
337
+ return Vue.h(PendingComponent)
338
+ }
339
+
340
+ // If no pending component, return null while loading
341
+ return null
342
+ }
343
+
344
+ // Success status - render the component
345
+ return out.value
346
+ }
347
+ },
348
+ })
349
+
350
+ export const Outlet = Vue.defineComponent({
351
+ name: 'Outlet',
352
+ setup() {
353
+ const router = useRouter()
354
+ const matchId = Vue.inject(matchContext)
355
+ const safeMatchId = Vue.computed(() => matchId?.value || '')
356
+
357
+ const routeId = useRouterState({
358
+ select: (s) =>
359
+ s.matches.find((d) => d.id === safeMatchId.value)?.routeId as string,
360
+ })
361
+
362
+ const route = Vue.computed(() => router.routesById[routeId.value]!)
363
+
364
+ const parentGlobalNotFound = useRouterState({
365
+ select: (s) => {
366
+ const matches = s.matches
367
+ const parentMatch = matches.find((d) => d.id === safeMatchId.value)
368
+
369
+ // During navigation transitions, parent match can be temporarily removed
370
+ // Return false to avoid errors - the component will handle this gracefully
371
+ if (!parentMatch) {
372
+ return false
373
+ }
374
+
375
+ return parentMatch.globalNotFound
376
+ },
377
+ })
378
+
379
+ const childMatchId = useRouterState({
380
+ select: (s) => {
381
+ const matches = s.matches
382
+ const index = matches.findIndex((d) => d.id === safeMatchId.value)
383
+ return matches[index + 1]?.id
384
+ },
385
+ })
386
+
387
+ return (): VNode | null => {
388
+ if (parentGlobalNotFound.value) {
389
+ return renderRouteNotFound(router, route.value, undefined)
390
+ }
391
+
392
+ if (!childMatchId.value) {
393
+ return null
394
+ }
395
+
396
+ const nextMatch = Vue.h(Match, { matchId: childMatchId.value })
397
+
398
+ if (safeMatchId.value === rootRouteId) {
399
+ return Vue.h(
400
+ Vue.Suspense,
401
+ {
402
+ fallback: router.options.defaultPendingComponent
403
+ ? Vue.h(router.options.defaultPendingComponent)
404
+ : null,
405
+ },
406
+ {
407
+ default: () => nextMatch,
408
+ },
409
+ )
410
+ }
411
+
412
+ return nextMatch
413
+ }
414
+ },
415
+ })