@tanstack/solid-router 1.108.0

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 (271) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +29 -0
  3. package/dist/cjs/Asset.cjs +59 -0
  4. package/dist/cjs/Asset.cjs.map +1 -0
  5. package/dist/cjs/Asset.d.cts +2 -0
  6. package/dist/cjs/CatchBoundary.cjs +92 -0
  7. package/dist/cjs/CatchBoundary.cjs.map +1 -0
  8. package/dist/cjs/CatchBoundary.d.cts +11 -0
  9. package/dist/cjs/HeadContent.cjs +129 -0
  10. package/dist/cjs/HeadContent.cjs.map +1 -0
  11. package/dist/cjs/HeadContent.d.cts +8 -0
  12. package/dist/cjs/Match.cjs +340 -0
  13. package/dist/cjs/Match.cjs.map +1 -0
  14. package/dist/cjs/Match.d.cts +8 -0
  15. package/dist/cjs/Matches.cjs +151 -0
  16. package/dist/cjs/Matches.cjs.map +1 -0
  17. package/dist/cjs/Matches.d.cts +69 -0
  18. package/dist/cjs/RouterProvider.cjs +45 -0
  19. package/dist/cjs/RouterProvider.cjs.map +1 -0
  20. package/dist/cjs/RouterProvider.d.cts +35 -0
  21. package/dist/cjs/SafeFragment.cjs +8 -0
  22. package/dist/cjs/SafeFragment.cjs.map +1 -0
  23. package/dist/cjs/SafeFragment.d.cts +1 -0
  24. package/dist/cjs/ScriptOnce.cjs +23 -0
  25. package/dist/cjs/ScriptOnce.cjs.map +1 -0
  26. package/dist/cjs/ScriptOnce.d.cts +5 -0
  27. package/dist/cjs/Scripts.cjs +48 -0
  28. package/dist/cjs/Scripts.cjs.map +1 -0
  29. package/dist/cjs/Scripts.d.cts +1 -0
  30. package/dist/cjs/ScrollRestoration.cjs +37 -0
  31. package/dist/cjs/ScrollRestoration.cjs.map +1 -0
  32. package/dist/cjs/ScrollRestoration.d.cts +15 -0
  33. package/dist/cjs/Transitioner.cjs +132 -0
  34. package/dist/cjs/Transitioner.cjs.map +1 -0
  35. package/dist/cjs/Transitioner.d.cts +1 -0
  36. package/dist/cjs/awaited.cjs +53 -0
  37. package/dist/cjs/awaited.cjs.map +1 -0
  38. package/dist/cjs/awaited.d.cts +11 -0
  39. package/dist/cjs/fileRoute.cjs +90 -0
  40. package/dist/cjs/fileRoute.cjs.map +1 -0
  41. package/dist/cjs/fileRoute.d.cts +58 -0
  42. package/dist/cjs/history.d.cts +8 -0
  43. package/dist/cjs/index.cjs +260 -0
  44. package/dist/cjs/index.cjs.map +1 -0
  45. package/dist/cjs/index.d.cts +53 -0
  46. package/dist/cjs/lazyRouteComponent.cjs +74 -0
  47. package/dist/cjs/lazyRouteComponent.cjs.map +1 -0
  48. package/dist/cjs/lazyRouteComponent.d.cts +7 -0
  49. package/dist/cjs/link.cjs +279 -0
  50. package/dist/cjs/link.cjs.map +1 -0
  51. package/dist/cjs/link.d.cts +113 -0
  52. package/dist/cjs/matchContext.cjs +25 -0
  53. package/dist/cjs/matchContext.cjs.map +1 -0
  54. package/dist/cjs/matchContext.d.cts +3 -0
  55. package/dist/cjs/not-found.cjs +51 -0
  56. package/dist/cjs/not-found.cjs.map +1 -0
  57. package/dist/cjs/not-found.d.cts +27 -0
  58. package/dist/cjs/redirects.cjs +29 -0
  59. package/dist/cjs/redirects.cjs.map +1 -0
  60. package/dist/cjs/redirects.d.cts +21 -0
  61. package/dist/cjs/renderRouteNotFound.cjs +23 -0
  62. package/dist/cjs/renderRouteNotFound.cjs.map +1 -0
  63. package/dist/cjs/renderRouteNotFound.d.cts +3 -0
  64. package/dist/cjs/route.cjs +233 -0
  65. package/dist/cjs/route.cjs.map +1 -0
  66. package/dist/cjs/route.d.cts +297 -0
  67. package/dist/cjs/routeInfo.d.cts +53 -0
  68. package/dist/cjs/router.cjs +1687 -0
  69. package/dist/cjs/router.cjs.map +1 -0
  70. package/dist/cjs/router.d.cts +555 -0
  71. package/dist/cjs/routerContext.cjs +33 -0
  72. package/dist/cjs/routerContext.cjs.map +1 -0
  73. package/dist/cjs/routerContext.d.cts +8 -0
  74. package/dist/cjs/scroll-restoration.cjs +183 -0
  75. package/dist/cjs/scroll-restoration.cjs.map +1 -0
  76. package/dist/cjs/scroll-restoration.d.cts +29 -0
  77. package/dist/cjs/typePrimitives.d.cts +66 -0
  78. package/dist/cjs/useBlocker.cjs +165 -0
  79. package/dist/cjs/useBlocker.cjs.map +1 -0
  80. package/dist/cjs/useBlocker.d.cts +68 -0
  81. package/dist/cjs/useCanGoBack.cjs +8 -0
  82. package/dist/cjs/useCanGoBack.cjs.map +1 -0
  83. package/dist/cjs/useCanGoBack.d.cts +1 -0
  84. package/dist/cjs/useLoaderData.cjs +14 -0
  85. package/dist/cjs/useLoaderData.cjs.map +1 -0
  86. package/dist/cjs/useLoaderData.d.cts +13 -0
  87. package/dist/cjs/useLoaderDeps.cjs +17 -0
  88. package/dist/cjs/useLoaderDeps.cjs.map +1 -0
  89. package/dist/cjs/useLoaderDeps.d.cts +12 -0
  90. package/dist/cjs/useLocation.cjs +10 -0
  91. package/dist/cjs/useLocation.cjs.map +1 -0
  92. package/dist/cjs/useLocation.d.cts +7 -0
  93. package/dist/cjs/useMatch.cjs +39 -0
  94. package/dist/cjs/useMatch.cjs.map +1 -0
  95. package/dist/cjs/useMatch.d.cts +14 -0
  96. package/dist/cjs/useNavigate.cjs +45 -0
  97. package/dist/cjs/useNavigate.cjs.map +1 -0
  98. package/dist/cjs/useNavigate.d.cts +7 -0
  99. package/dist/cjs/useParams.cjs +15 -0
  100. package/dist/cjs/useParams.cjs.map +1 -0
  101. package/dist/cjs/useParams.d.cts +15 -0
  102. package/dist/cjs/useRouteContext.cjs +11 -0
  103. package/dist/cjs/useRouteContext.cjs.map +1 -0
  104. package/dist/cjs/useRouteContext.d.cts +13 -0
  105. package/dist/cjs/useRouter.cjs +29 -0
  106. package/dist/cjs/useRouter.cjs.map +1 -0
  107. package/dist/cjs/useRouter.d.cts +4 -0
  108. package/dist/cjs/useRouterState.cjs +16 -0
  109. package/dist/cjs/useRouterState.cjs.map +1 -0
  110. package/dist/cjs/useRouterState.d.cts +8 -0
  111. package/dist/cjs/useSearch.cjs +15 -0
  112. package/dist/cjs/useSearch.cjs.map +1 -0
  113. package/dist/cjs/useSearch.d.cts +15 -0
  114. package/dist/cjs/utils.cjs +58 -0
  115. package/dist/cjs/utils.cjs.map +1 -0
  116. package/dist/cjs/utils.d.cts +44 -0
  117. package/dist/esm/Asset.d.ts +2 -0
  118. package/dist/esm/Asset.js +59 -0
  119. package/dist/esm/Asset.js.map +1 -0
  120. package/dist/esm/CatchBoundary.d.ts +11 -0
  121. package/dist/esm/CatchBoundary.js +75 -0
  122. package/dist/esm/CatchBoundary.js.map +1 -0
  123. package/dist/esm/HeadContent.d.ts +8 -0
  124. package/dist/esm/HeadContent.js +112 -0
  125. package/dist/esm/HeadContent.js.map +1 -0
  126. package/dist/esm/Match.d.ts +8 -0
  127. package/dist/esm/Match.js +323 -0
  128. package/dist/esm/Match.js.map +1 -0
  129. package/dist/esm/Matches.d.ts +69 -0
  130. package/dist/esm/Matches.js +134 -0
  131. package/dist/esm/Matches.js.map +1 -0
  132. package/dist/esm/RouterProvider.d.ts +35 -0
  133. package/dist/esm/RouterProvider.js +45 -0
  134. package/dist/esm/RouterProvider.js.map +1 -0
  135. package/dist/esm/SafeFragment.d.ts +1 -0
  136. package/dist/esm/SafeFragment.js +8 -0
  137. package/dist/esm/SafeFragment.js.map +1 -0
  138. package/dist/esm/ScriptOnce.d.ts +5 -0
  139. package/dist/esm/ScriptOnce.js +23 -0
  140. package/dist/esm/ScriptOnce.js.map +1 -0
  141. package/dist/esm/Scripts.d.ts +1 -0
  142. package/dist/esm/Scripts.js +48 -0
  143. package/dist/esm/Scripts.js.map +1 -0
  144. package/dist/esm/ScrollRestoration.d.ts +15 -0
  145. package/dist/esm/ScrollRestoration.js +37 -0
  146. package/dist/esm/ScrollRestoration.js.map +1 -0
  147. package/dist/esm/Transitioner.d.ts +1 -0
  148. package/dist/esm/Transitioner.js +115 -0
  149. package/dist/esm/Transitioner.js.map +1 -0
  150. package/dist/esm/awaited.d.ts +11 -0
  151. package/dist/esm/awaited.js +36 -0
  152. package/dist/esm/awaited.js.map +1 -0
  153. package/dist/esm/fileRoute.d.ts +58 -0
  154. package/dist/esm/fileRoute.js +90 -0
  155. package/dist/esm/fileRoute.js.map +1 -0
  156. package/dist/esm/history.d.ts +8 -0
  157. package/dist/esm/index.d.ts +53 -0
  158. package/dist/esm/index.js +149 -0
  159. package/dist/esm/index.js.map +1 -0
  160. package/dist/esm/lazyRouteComponent.d.ts +7 -0
  161. package/dist/esm/lazyRouteComponent.js +74 -0
  162. package/dist/esm/lazyRouteComponent.js.map +1 -0
  163. package/dist/esm/link.d.ts +113 -0
  164. package/dist/esm/link.js +262 -0
  165. package/dist/esm/link.js.map +1 -0
  166. package/dist/esm/matchContext.d.ts +3 -0
  167. package/dist/esm/matchContext.js +8 -0
  168. package/dist/esm/matchContext.js.map +1 -0
  169. package/dist/esm/not-found.d.ts +27 -0
  170. package/dist/esm/not-found.js +51 -0
  171. package/dist/esm/not-found.js.map +1 -0
  172. package/dist/esm/redirects.d.ts +21 -0
  173. package/dist/esm/redirects.js +29 -0
  174. package/dist/esm/redirects.js.map +1 -0
  175. package/dist/esm/renderRouteNotFound.d.ts +3 -0
  176. package/dist/esm/renderRouteNotFound.js +23 -0
  177. package/dist/esm/renderRouteNotFound.js.map +1 -0
  178. package/dist/esm/route.d.ts +297 -0
  179. package/dist/esm/route.js +233 -0
  180. package/dist/esm/route.js.map +1 -0
  181. package/dist/esm/routeInfo.d.ts +53 -0
  182. package/dist/esm/router.d.ts +555 -0
  183. package/dist/esm/router.js +1687 -0
  184. package/dist/esm/router.js.map +1 -0
  185. package/dist/esm/routerContext.d.ts +8 -0
  186. package/dist/esm/routerContext.js +16 -0
  187. package/dist/esm/routerContext.js.map +1 -0
  188. package/dist/esm/scroll-restoration.d.ts +29 -0
  189. package/dist/esm/scroll-restoration.js +183 -0
  190. package/dist/esm/scroll-restoration.js.map +1 -0
  191. package/dist/esm/typePrimitives.d.ts +66 -0
  192. package/dist/esm/useBlocker.d.ts +68 -0
  193. package/dist/esm/useBlocker.js +148 -0
  194. package/dist/esm/useBlocker.js.map +1 -0
  195. package/dist/esm/useCanGoBack.d.ts +1 -0
  196. package/dist/esm/useCanGoBack.js +8 -0
  197. package/dist/esm/useCanGoBack.js.map +1 -0
  198. package/dist/esm/useLoaderData.d.ts +13 -0
  199. package/dist/esm/useLoaderData.js +14 -0
  200. package/dist/esm/useLoaderData.js.map +1 -0
  201. package/dist/esm/useLoaderDeps.d.ts +12 -0
  202. package/dist/esm/useLoaderDeps.js +17 -0
  203. package/dist/esm/useLoaderDeps.js.map +1 -0
  204. package/dist/esm/useLocation.d.ts +7 -0
  205. package/dist/esm/useLocation.js +10 -0
  206. package/dist/esm/useLocation.js.map +1 -0
  207. package/dist/esm/useMatch.d.ts +14 -0
  208. package/dist/esm/useMatch.js +22 -0
  209. package/dist/esm/useMatch.js.map +1 -0
  210. package/dist/esm/useNavigate.d.ts +7 -0
  211. package/dist/esm/useNavigate.js +28 -0
  212. package/dist/esm/useNavigate.js.map +1 -0
  213. package/dist/esm/useParams.d.ts +15 -0
  214. package/dist/esm/useParams.js +15 -0
  215. package/dist/esm/useParams.js.map +1 -0
  216. package/dist/esm/useRouteContext.d.ts +13 -0
  217. package/dist/esm/useRouteContext.js +11 -0
  218. package/dist/esm/useRouteContext.js.map +1 -0
  219. package/dist/esm/useRouter.d.ts +4 -0
  220. package/dist/esm/useRouter.js +12 -0
  221. package/dist/esm/useRouter.js.map +1 -0
  222. package/dist/esm/useRouterState.d.ts +8 -0
  223. package/dist/esm/useRouterState.js +16 -0
  224. package/dist/esm/useRouterState.js.map +1 -0
  225. package/dist/esm/useSearch.d.ts +15 -0
  226. package/dist/esm/useSearch.js +15 -0
  227. package/dist/esm/useSearch.js.map +1 -0
  228. package/dist/esm/utils.d.ts +44 -0
  229. package/dist/esm/utils.js +41 -0
  230. package/dist/esm/utils.js.map +1 -0
  231. package/package.json +75 -0
  232. package/src/Asset.tsx +23 -0
  233. package/src/CatchBoundary.tsx +78 -0
  234. package/src/HeadContent.tsx +146 -0
  235. package/src/Match.tsx +356 -0
  236. package/src/Matches.tsx +348 -0
  237. package/src/RouterProvider.tsx +130 -0
  238. package/src/SafeFragment.tsx +3 -0
  239. package/src/ScriptOnce.tsx +30 -0
  240. package/src/Scripts.tsx +65 -0
  241. package/src/ScrollRestoration.tsx +65 -0
  242. package/src/Transitioner.tsx +152 -0
  243. package/src/awaited.tsx +49 -0
  244. package/src/fileRoute.ts +274 -0
  245. package/src/history.ts +9 -0
  246. package/src/index.tsx +359 -0
  247. package/src/lazyRouteComponent.tsx +114 -0
  248. package/src/link.tsx +1002 -0
  249. package/src/matchContext.tsx +10 -0
  250. package/src/not-found.tsx +69 -0
  251. package/src/redirects.ts +71 -0
  252. package/src/renderRouteNotFound.tsx +27 -0
  253. package/src/route.ts +1477 -0
  254. package/src/routeInfo.ts +239 -0
  255. package/src/router.ts +3066 -0
  256. package/src/routerContext.tsx +26 -0
  257. package/src/scroll-restoration.tsx +337 -0
  258. package/src/typePrimitives.ts +195 -0
  259. package/src/useBlocker.tsx +298 -0
  260. package/src/useCanGoBack.ts +5 -0
  261. package/src/useLoaderData.tsx +64 -0
  262. package/src/useLoaderDeps.tsx +52 -0
  263. package/src/useLocation.tsx +26 -0
  264. package/src/useMatch.tsx +96 -0
  265. package/src/useNavigate.tsx +61 -0
  266. package/src/useParams.tsx +83 -0
  267. package/src/useRouteContext.ts +62 -0
  268. package/src/useRouter.tsx +15 -0
  269. package/src/useRouterState.tsx +32 -0
  270. package/src/useSearch.tsx +84 -0
  271. package/src/utils.ts +96 -0
package/src/Match.tsx ADDED
@@ -0,0 +1,356 @@
1
+ import * as Solid from 'solid-js'
2
+ import invariant from 'tiny-invariant'
3
+ import warning from 'tiny-warning'
4
+ import {
5
+ createControlledPromise,
6
+ getLocationChangeInfo,
7
+ pick,
8
+ rootRouteId,
9
+ } from '@tanstack/router-core'
10
+ import { Dynamic } from 'solid-js/web'
11
+ import { CatchBoundary, ErrorComponent } from './CatchBoundary'
12
+ import { useRouterState } from './useRouterState'
13
+ import { useRouter } from './useRouter'
14
+ import { CatchNotFound, isNotFound } from './not-found'
15
+ import { isRedirect } from './redirects'
16
+ import { matchContext } from './matchContext'
17
+ import { SafeFragment } from './SafeFragment'
18
+ import { renderRouteNotFound } from './renderRouteNotFound'
19
+ import { ScrollRestoration } from './scroll-restoration'
20
+ import type { AnyRoute } from './route'
21
+
22
+ export const Match = (props: { matchId: string }) => {
23
+ const router = useRouter()
24
+ const routeId = useRouterState({
25
+ select: (s) => {
26
+ return s.matches.find((d) => d.id === props.matchId)?.routeId as string
27
+ },
28
+ })
29
+
30
+ invariant(
31
+ routeId,
32
+ `Could not find routeId for matchId "${props.matchId}". Please file an issue!`,
33
+ )
34
+
35
+ const route: () => AnyRoute = () => router.routesById[routeId()]
36
+
37
+ const PendingComponent = () =>
38
+ route().options.pendingComponent ?? router.options.defaultPendingComponent
39
+
40
+ const routeErrorComponent = () =>
41
+ route().options.errorComponent ?? router.options.defaultErrorComponent
42
+
43
+ const routeOnCatch = () =>
44
+ route().options.onCatch ?? router.options.defaultOnCatch
45
+
46
+ const routeNotFoundComponent = () =>
47
+ route().isRoot
48
+ ? // If it's the root route, use the globalNotFound option, with fallback to the notFoundRoute's component
49
+ (route().options.notFoundComponent ??
50
+ router.options.notFoundRoute?.options.component)
51
+ : route().options.notFoundComponent
52
+
53
+ const ResolvedSuspenseBoundary = () =>
54
+ // If we're on the root route, allow forcefully wrapping in suspense
55
+ (!route().isRoot || route().options.wrapInSuspense) &&
56
+ (route().options.wrapInSuspense ??
57
+ PendingComponent() ??
58
+ (route().options.errorComponent as any)?.preload)
59
+ ? Solid.Suspense
60
+ : SafeFragment
61
+
62
+ const ResolvedCatchBoundary = () =>
63
+ routeErrorComponent() ? CatchBoundary : SafeFragment
64
+
65
+ const ResolvedNotFoundBoundary = () =>
66
+ routeNotFoundComponent() ? CatchNotFound : SafeFragment
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
+ return (
80
+ <>
81
+ <matchContext.Provider value={() => props.matchId}>
82
+ <Dynamic
83
+ component={ResolvedSuspenseBoundary()}
84
+ fallback={<Dynamic component={PendingComponent()} />}
85
+ >
86
+ <Dynamic
87
+ component={ResolvedCatchBoundary()}
88
+ getResetKey={() => resetKey()}
89
+ errorComponent={routeErrorComponent() || ErrorComponent}
90
+ onCatch={(error: Error) => {
91
+ // Forward not found errors (we don't want to show the error component for these)
92
+ if (isNotFound(error)) throw error
93
+ warning(false, `Error in route match: ${props.matchId}`)
94
+ routeOnCatch()?.(error)
95
+ }}
96
+ >
97
+ <Dynamic
98
+ component={ResolvedNotFoundBoundary()}
99
+ fallback={(error: any) => {
100
+ // If the current not found handler doesn't exist or it has a
101
+ // route ID which doesn't match the current route, rethrow the error
102
+ if (
103
+ !routeNotFoundComponent() ||
104
+ (error.routeId && error.routeId !== routeId) ||
105
+ (!error.routeId && !route().isRoot)
106
+ )
107
+ throw error
108
+
109
+ return (
110
+ <Dynamic component={routeNotFoundComponent()} {...error} />
111
+ )
112
+ }}
113
+ >
114
+ <MatchInner matchId={props.matchId} />
115
+ </Dynamic>
116
+ </Dynamic>
117
+ </Dynamic>
118
+ </matchContext.Provider>
119
+
120
+ {parentRouteId() === rootRouteId ? (
121
+ <>
122
+ <OnRendered />
123
+ <ScrollRestoration />
124
+ </>
125
+ ) : null}
126
+ </>
127
+ )
128
+ }
129
+
130
+ // On Rendered can't happen above the root layout because it actually
131
+ // renders a dummy dom element to track the rendered state of the app.
132
+ // We render a script tag with a key that changes based on the current
133
+ // location state.key. Also, because it's below the root layout, it
134
+ // allows us to fire onRendered events even after a hydration mismatch
135
+ // error that occurred above the root layout (like bad head/link tags,
136
+ // which is common).
137
+ function OnRendered() {
138
+ const router = useRouter()
139
+
140
+ const location = useRouterState({
141
+ select: (s) => {
142
+ return s.resolvedLocation?.state.key
143
+ },
144
+ })
145
+ Solid.createEffect(
146
+ Solid.on([location], () => {
147
+ router.emit({
148
+ type: 'onRendered',
149
+ ...getLocationChangeInfo(router.state),
150
+ })
151
+ }),
152
+ )
153
+ return null
154
+ }
155
+
156
+ export const MatchInner = (props: { matchId: string }): any => {
157
+ const router = useRouter()
158
+
159
+ // { match, key, routeId } =
160
+ const matchState: Solid.Accessor<any> = useRouterState({
161
+ select: (s) => {
162
+ const matchIndex = s.matches.findIndex((d) => d.id === props.matchId)
163
+ const match = s.matches[matchIndex]!
164
+ const routeId = match.routeId as string
165
+
166
+ const remountFn =
167
+ (router.routesById[routeId] as AnyRoute).options.remountDeps ??
168
+ router.options.defaultRemountDeps
169
+ const remountDeps = remountFn?.({
170
+ routeId,
171
+ loaderDeps: match.loaderDeps,
172
+ params: match._strictParams,
173
+ search: match._strictSearch,
174
+ })
175
+ const key = remountDeps ? JSON.stringify(remountDeps) : undefined
176
+
177
+ return {
178
+ key,
179
+ routeId,
180
+ match: pick(match, ['id', 'status', 'error']),
181
+ }
182
+ },
183
+ })
184
+
185
+ const route = () => router.routesById[matchState().routeId]!
186
+
187
+ // function useChangedDiff(value: any) {
188
+ // const ref = Solid.useRef(value)
189
+ // const changed = ref.current !== value
190
+ // if (changed) {
191
+ // console.log(
192
+ // 'Changed:',
193
+ // value,
194
+ // Object.fromEntries(
195
+ // Object.entries(value).filter(
196
+ // ([key, val]) => val !== ref.current[key],
197
+ // ),
198
+ // ),
199
+ // )
200
+ // }
201
+ // ref.current = value
202
+ // }
203
+
204
+ // useChangedDiff(match)
205
+ const match = () => matchState().match
206
+
207
+ const out = () => {
208
+ const Comp = route().options.component ?? router.options.defaultComponent
209
+ if (Comp) {
210
+ return <Comp />
211
+ }
212
+ return <Outlet />
213
+ }
214
+
215
+ return (
216
+ <Solid.Switch>
217
+ <Solid.Match when={match().status === 'notFound'}>
218
+ {(_) => {
219
+ invariant(isNotFound(match().error), 'Expected a notFound error')
220
+
221
+ return renderRouteNotFound(router, route(), match().error)
222
+ }}
223
+ </Solid.Match>
224
+ <Solid.Match when={match().status === 'redirected'}>
225
+ {(_) => {
226
+ invariant(isRedirect(match().error), 'Expected a redirect error')
227
+
228
+ const [loaderResult] = Solid.createResource(async () => {
229
+ await new Promise((r) => setTimeout(r, 0))
230
+ return router.getMatch(match().id)?.loadPromise
231
+ })
232
+
233
+ return <>{loaderResult()}</>
234
+ }}
235
+ </Solid.Match>
236
+ <Solid.Match when={match().status === 'error'}>
237
+ {(_) => {
238
+ if (router.isServer) {
239
+ const RouteErrorComponent =
240
+ (route().options.errorComponent ??
241
+ router.options.defaultErrorComponent) ||
242
+ ErrorComponent
243
+
244
+ return (
245
+ <RouteErrorComponent
246
+ error={match().error}
247
+ info={{
248
+ componentStack: '',
249
+ }}
250
+ />
251
+ )
252
+ }
253
+
254
+ throw match().error
255
+ }}
256
+ </Solid.Match>
257
+ <Solid.Match when={match().status === 'pending'}>
258
+ {(_) => {
259
+ const pendingMinMs =
260
+ route().options.pendingMinMs ?? router.options.defaultPendingMinMs
261
+
262
+ if (pendingMinMs && !router.getMatch(match().id)?.minPendingPromise) {
263
+ // Create a promise that will resolve after the minPendingMs
264
+ if (!router.isServer) {
265
+ const minPendingPromise = createControlledPromise<void>()
266
+
267
+ Promise.resolve().then(() => {
268
+ router.updateMatch(match().id, (prev) => ({
269
+ ...prev,
270
+ minPendingPromise,
271
+ }))
272
+ })
273
+
274
+ setTimeout(() => {
275
+ minPendingPromise.resolve()
276
+
277
+ // We've handled the minPendingPromise, so we can delete it
278
+ router.updateMatch(match().id, (prev) => ({
279
+ ...prev,
280
+ minPendingPromise: undefined,
281
+ }))
282
+ }, pendingMinMs)
283
+ }
284
+ }
285
+
286
+ const [loaderResult] = Solid.createResource(async () => {
287
+ await new Promise((r) => setTimeout(r, 0))
288
+ return router.getMatch(match().id)?.loadPromise
289
+ })
290
+
291
+ return <>{loaderResult()}</>
292
+ }}
293
+ </Solid.Match>
294
+ <Solid.Match when={match().status === 'success'}>{out()}</Solid.Match>
295
+ </Solid.Switch>
296
+ )
297
+ }
298
+
299
+ export const Outlet = () => {
300
+ const router = useRouter()
301
+ const matchId = Solid.useContext(matchContext)
302
+ const routeId = useRouterState({
303
+ select: (s) => s.matches.find((d) => d.id === matchId())?.routeId as string,
304
+ })
305
+
306
+ const route = () => router.routesById[routeId()]!
307
+
308
+ const parentGlobalNotFound = useRouterState({
309
+ select: (s) => {
310
+ const matches = s.matches
311
+ const parentMatch = matches.find((d) => d.id === matchId())
312
+ invariant(
313
+ parentMatch,
314
+ `Could not find parent match for matchId "${matchId()}"`,
315
+ )
316
+ return parentMatch.globalNotFound
317
+ },
318
+ })
319
+
320
+ const childMatchId = useRouterState({
321
+ select: (s) => {
322
+ const matches = s.matches
323
+ const index = matches.findIndex((d) => d.id === matchId())
324
+ const v = matches[index + 1]?.id
325
+ return v
326
+ },
327
+ })
328
+
329
+ return (
330
+ <Solid.Switch>
331
+ <Solid.Match when={parentGlobalNotFound()}>
332
+ {renderRouteNotFound(router, route(), undefined)}
333
+ </Solid.Match>
334
+ <Solid.Match when={childMatchId()}>
335
+ {(matchId) => {
336
+ // const nextMatch = <Match matchId={matchId()} />
337
+
338
+ return (
339
+ <Solid.Show
340
+ when={matchId() === rootRouteId}
341
+ fallback={<Match matchId={matchId()} />}
342
+ >
343
+ <Solid.Suspense
344
+ fallback={
345
+ <Dynamic component={router.options.defaultPendingComponent} />
346
+ }
347
+ >
348
+ <Match matchId={matchId()} />
349
+ </Solid.Suspense>
350
+ </Solid.Show>
351
+ )
352
+ }}
353
+ </Solid.Match>
354
+ </Solid.Switch>
355
+ )
356
+ }
@@ -0,0 +1,348 @@
1
+ import * as Solid from 'solid-js'
2
+ import warning from 'tiny-warning'
3
+ import { CatchBoundary, ErrorComponent } from './CatchBoundary'
4
+ import { useRouterState } from './useRouterState'
5
+ import { useRouter } from './useRouter'
6
+ import { Transitioner } from './Transitioner'
7
+ import { matchContext } from './matchContext'
8
+ import { Match } from './Match'
9
+ import { SafeFragment } from './SafeFragment'
10
+ import type { AnyRoute } from './route'
11
+ import type {
12
+ ControlledPromise,
13
+ DeepPartial,
14
+ NoInfer,
15
+ ResolveRelativePath,
16
+ StaticDataRouteOption,
17
+ } from '@tanstack/router-core'
18
+ import type { AnyRouter, RegisteredRouter, RouterState } from './router'
19
+ import type {
20
+ MakeOptionalPathParams,
21
+ MakeOptionalSearchParams,
22
+ MaskOptions,
23
+ ResolveRoute,
24
+ ToSubOptionsProps,
25
+ } from './link'
26
+ import type {
27
+ AllContext,
28
+ AllLoaderData,
29
+ AllParams,
30
+ FullSearchSchema,
31
+ ParseRoute,
32
+ RouteById,
33
+ RouteByPath,
34
+ RouteIds,
35
+ } from './routeInfo'
36
+
37
+ export type MakeRouteMatchFromRoute<TRoute extends AnyRoute> = RouteMatch<
38
+ TRoute['types']['id'],
39
+ TRoute['types']['fullPath'],
40
+ TRoute['types']['allParams'],
41
+ TRoute['types']['fullSearchSchema'],
42
+ TRoute['types']['loaderData'],
43
+ TRoute['types']['allContext'],
44
+ TRoute['types']['loaderDeps']
45
+ >
46
+
47
+ export interface RouteMatch<
48
+ out TRouteId,
49
+ out TFullPath,
50
+ out TAllParams,
51
+ out TFullSearchSchema,
52
+ out TLoaderData,
53
+ out TAllContext,
54
+ out TLoaderDeps,
55
+ > {
56
+ id: string
57
+ routeId: TRouteId
58
+ fullPath: TFullPath
59
+ index: number
60
+ pathname: string
61
+ params: TAllParams
62
+ _strictParams: TAllParams
63
+ status: 'pending' | 'success' | 'error' | 'redirected' | 'notFound'
64
+ isFetching: false | 'beforeLoad' | 'loader'
65
+ error: unknown
66
+ paramsError: unknown
67
+ searchError: unknown
68
+ updatedAt: number
69
+ loadPromise?: ControlledPromise<void>
70
+ beforeLoadPromise?: ControlledPromise<void>
71
+ loaderPromise?: ControlledPromise<void>
72
+ loaderData?: TLoaderData
73
+ __routeContext: Record<string, unknown>
74
+ __beforeLoadContext: Record<string, unknown>
75
+ context: TAllContext
76
+ search: TFullSearchSchema
77
+ _strictSearch: TFullSearchSchema
78
+ fetchCount: number
79
+ abortController: AbortController
80
+ cause: 'preload' | 'enter' | 'stay'
81
+ loaderDeps: TLoaderDeps
82
+ preload: boolean
83
+ invalid: boolean
84
+ meta?: Array<Solid.JSX.IntrinsicElements['meta'] | undefined>
85
+ links?: Array<Solid.JSX.IntrinsicElements['link'] | undefined>
86
+ scripts?: Array<Solid.JSX.IntrinsicElements['script'] | undefined>
87
+ headScripts?: Array<Solid.JSX.IntrinsicElements['script'] | undefined>
88
+ headers?: Record<string, string>
89
+ globalNotFound?: boolean
90
+ staticData: StaticDataRouteOption
91
+ minPendingPromise?: ControlledPromise<void>
92
+ pendingTimeout?: ReturnType<typeof setTimeout>
93
+ }
94
+
95
+ export type MakeRouteMatch<
96
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
97
+ TRouteId = RouteIds<TRouteTree>,
98
+ TStrict extends boolean = true,
99
+ > = RouteMatch<
100
+ TRouteId,
101
+ RouteById<TRouteTree, TRouteId>['types']['fullPath'],
102
+ TStrict extends false
103
+ ? AllParams<TRouteTree>
104
+ : RouteById<TRouteTree, TRouteId>['types']['allParams'],
105
+ TStrict extends false
106
+ ? FullSearchSchema<TRouteTree>
107
+ : RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema'],
108
+ TStrict extends false
109
+ ? AllLoaderData<TRouteTree>
110
+ : RouteById<TRouteTree, TRouteId>['types']['loaderData'],
111
+ TStrict extends false
112
+ ? AllContext<TRouteTree>
113
+ : RouteById<TRouteTree, TRouteId>['types']['allContext'],
114
+ RouteById<TRouteTree, TRouteId>['types']['loaderDeps']
115
+ >
116
+
117
+ export type AnyRouteMatch = RouteMatch<any, any, any, any, any, any, any>
118
+
119
+ export function Matches() {
120
+ const router = useRouter()
121
+
122
+ const pendingElement = router.options.defaultPendingComponent ? (
123
+ <router.options.defaultPendingComponent />
124
+ ) : null
125
+
126
+ // Do not render a root Suspense during SSR or hydrating from SSR
127
+ const ResolvedSuspense =
128
+ router.isServer || (typeof document !== 'undefined' && router.clientSsr)
129
+ ? SafeFragment
130
+ : Solid.Suspense
131
+
132
+ const inner = (
133
+ <ResolvedSuspense fallback={pendingElement}>
134
+ <Transitioner />
135
+ <MatchesInner />
136
+ </ResolvedSuspense>
137
+ )
138
+
139
+ return router.options.InnerWrap ? (
140
+ <router.options.InnerWrap>{inner}</router.options.InnerWrap>
141
+ ) : (
142
+ inner
143
+ )
144
+ }
145
+
146
+ function MatchesInner() {
147
+ const matchId = useRouterState({
148
+ select: (s) => {
149
+ return s.matches[0]?.id
150
+ },
151
+ })
152
+
153
+ const resetKey = useRouterState({
154
+ select: (s) => s.loadedAt,
155
+ })
156
+
157
+ return (
158
+ <matchContext.Provider value={matchId}>
159
+ <CatchBoundary
160
+ getResetKey={() => resetKey()}
161
+ errorComponent={ErrorComponent}
162
+ onCatch={(error) => {
163
+ warning(
164
+ false,
165
+ `The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
166
+ )
167
+ warning(false, error.message || error.toString())
168
+ }}
169
+ >
170
+ {matchId() ? <Match matchId={matchId()!} /> : null}
171
+ </CatchBoundary>
172
+ </matchContext.Provider>
173
+ )
174
+ }
175
+
176
+ export interface MatchRouteOptions {
177
+ pending?: boolean
178
+ caseSensitive?: boolean
179
+ includeSearch?: boolean
180
+ fuzzy?: boolean
181
+ }
182
+
183
+ export type UseMatchRouteOptions<
184
+ TRouter extends AnyRouter = RegisteredRouter,
185
+ TFrom extends string = string,
186
+ TTo extends string | undefined = undefined,
187
+ TMaskFrom extends string = TFrom,
188
+ TMaskTo extends string = '',
189
+ > = ToSubOptionsProps<TRouter, TFrom, TTo> &
190
+ DeepPartial<MakeOptionalSearchParams<TRouter, TFrom, TTo>> &
191
+ DeepPartial<MakeOptionalPathParams<TRouter, TFrom, TTo>> &
192
+ MaskOptions<TRouter, TMaskFrom, TMaskTo> &
193
+ MatchRouteOptions
194
+
195
+ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
196
+ const router = useRouter()
197
+
198
+ const status = useRouterState({
199
+ select: (s) => s.status,
200
+ })
201
+
202
+ return <
203
+ const TFrom extends string = string,
204
+ const TTo extends string | undefined = undefined,
205
+ const TMaskFrom extends string = TFrom,
206
+ const TMaskTo extends string = '',
207
+ >(
208
+ opts: UseMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
209
+ ): Solid.Accessor<
210
+ false | ResolveRoute<TRouter, TFrom, TTo>['types']['allParams']
211
+ > => {
212
+ const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts
213
+
214
+ const matchRoute = Solid.createMemo(() => {
215
+ status()
216
+ return router.matchRoute(rest as any, {
217
+ pending,
218
+ caseSensitive,
219
+ fuzzy,
220
+ includeSearch,
221
+ })
222
+ })
223
+
224
+ return matchRoute
225
+ }
226
+ }
227
+
228
+ export type MakeMatchRouteOptions<
229
+ TRouter extends AnyRouter = RegisteredRouter,
230
+ TFrom extends string = string,
231
+ TTo extends string | undefined = undefined,
232
+ TMaskFrom extends string = TFrom,
233
+ TMaskTo extends string = '',
234
+ > = UseMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & {
235
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
236
+ children?:
237
+ | ((
238
+ params?: RouteByPath<
239
+ TRouter['routeTree'],
240
+ ResolveRelativePath<TFrom, NoInfer<TTo>>
241
+ >['types']['allParams'],
242
+ ) => Solid.JSX.Element)
243
+ | Solid.JSX.Element
244
+ }
245
+
246
+ export function MatchRoute<
247
+ TRouter extends AnyRouter = RegisteredRouter,
248
+ const TFrom extends string = string,
249
+ const TTo extends string | undefined = undefined,
250
+ const TMaskFrom extends string = TFrom,
251
+ const TMaskTo extends string = '',
252
+ >(props: MakeMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): any {
253
+ const status = useRouterState({
254
+ select: (s) => s.status,
255
+ })
256
+
257
+ return (
258
+ <Solid.Show when={status()} keyed>
259
+ {(_) => {
260
+ const matchRoute = useMatchRoute()
261
+ const params = matchRoute(props as any)() as boolean
262
+
263
+ if (typeof props.children === 'function') {
264
+ return (props.children as any)(params)
265
+ }
266
+
267
+ return params ? props.children : null
268
+ }}
269
+ </Solid.Show>
270
+ )
271
+ }
272
+
273
+ export type MakeRouteMatchUnion<
274
+ TRouter extends AnyRouter = RegisteredRouter,
275
+ TRoute extends AnyRoute = ParseRoute<TRouter['routeTree']>,
276
+ > = TRoute extends any
277
+ ? RouteMatch<
278
+ TRoute['id'],
279
+ TRoute['fullPath'],
280
+ TRoute['types']['allParams'],
281
+ TRoute['types']['fullSearchSchema'],
282
+ TRoute['types']['loaderData'],
283
+ TRoute['types']['allContext'],
284
+ TRoute['types']['loaderDeps']
285
+ >
286
+ : never
287
+
288
+ export interface UseMatchesBaseOptions<TRouter extends AnyRouter, TSelected> {
289
+ select?: (matches: Array<MakeRouteMatchUnion<TRouter>>) => TSelected
290
+ }
291
+
292
+ export type UseMatchesResult<
293
+ TRouter extends AnyRouter,
294
+ TSelected,
295
+ > = unknown extends TSelected ? Array<MakeRouteMatchUnion<TRouter>> : TSelected
296
+
297
+ export function useMatches<
298
+ TRouter extends AnyRouter = RegisteredRouter,
299
+ TSelected = unknown,
300
+ >(
301
+ opts?: UseMatchesBaseOptions<TRouter, TSelected>,
302
+ ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
303
+ return useRouterState({
304
+ select: (state: RouterState<TRouter['routeTree']>) => {
305
+ const matches = state.matches
306
+ return opts?.select
307
+ ? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
308
+ : matches
309
+ },
310
+ } as any) as Solid.Accessor<UseMatchesResult<TRouter, TSelected>>
311
+ }
312
+
313
+ export function useParentMatches<
314
+ TRouter extends AnyRouter = RegisteredRouter,
315
+ TSelected = unknown,
316
+ >(
317
+ opts?: UseMatchesBaseOptions<TRouter, TSelected>,
318
+ ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
319
+ const contextMatchId = Solid.useContext(matchContext)
320
+
321
+ return useMatches({
322
+ select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
323
+ matches = matches.slice(
324
+ 0,
325
+ matches.findIndex((d) => d.id === contextMatchId()),
326
+ )
327
+ return opts?.select ? opts.select(matches) : matches
328
+ },
329
+ } as any)
330
+ }
331
+
332
+ export function useChildMatches<
333
+ TRouter extends AnyRouter = RegisteredRouter,
334
+ TSelected = unknown,
335
+ >(
336
+ opts?: UseMatchesBaseOptions<TRouter, TSelected>,
337
+ ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
338
+ const contextMatchId = Solid.useContext(matchContext)
339
+
340
+ return useMatches({
341
+ select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
342
+ matches = matches.slice(
343
+ matches.findIndex((d) => d.id === contextMatchId()) + 1,
344
+ )
345
+ return opts?.select ? opts.select(matches) : matches
346
+ },
347
+ } as any)
348
+ }