@tanstack/solid-router 2.0.0-alpha.5 → 2.0.0-alpha.7

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 (219) hide show
  1. package/dist/cjs/Asset.cjs +2 -2
  2. package/dist/cjs/Asset.cjs.map +1 -1
  3. package/dist/cjs/HeadContent.cjs +11 -1
  4. package/dist/cjs/HeadContent.cjs.map +1 -1
  5. package/dist/cjs/HeadContent.dev.cjs +11 -1
  6. package/dist/cjs/HeadContent.dev.cjs.map +1 -1
  7. package/dist/cjs/Match.cjs +265 -248
  8. package/dist/cjs/Match.cjs.map +1 -1
  9. package/dist/cjs/Match.d.cts +1 -3
  10. package/dist/cjs/Matches.cjs +35 -34
  11. package/dist/cjs/Matches.cjs.map +1 -1
  12. package/dist/cjs/RouterProvider.cjs +12 -8
  13. package/dist/cjs/RouterProvider.cjs.map +1 -1
  14. package/dist/cjs/RouterProvider.d.cts +1 -1
  15. package/dist/cjs/Scripts.cjs +23 -12
  16. package/dist/cjs/Scripts.cjs.map +1 -1
  17. package/dist/cjs/Scripts.d.cts +2 -1
  18. package/dist/cjs/Transitioner.cjs +57 -36
  19. package/dist/cjs/Transitioner.cjs.map +1 -1
  20. package/dist/cjs/headContentUtils.cjs +26 -23
  21. package/dist/cjs/headContentUtils.cjs.map +1 -1
  22. package/dist/cjs/headContentUtils.d.cts +2 -1
  23. package/dist/cjs/index.cjs +1 -1
  24. package/dist/cjs/index.dev.cjs +1 -1
  25. package/dist/cjs/link.cjs +143 -101
  26. package/dist/cjs/link.cjs.map +1 -1
  27. package/dist/cjs/matchContext.cjs +7 -5
  28. package/dist/cjs/matchContext.cjs.map +1 -1
  29. package/dist/cjs/matchContext.d.cts +8 -2
  30. package/dist/cjs/not-found.cjs +8 -4
  31. package/dist/cjs/not-found.cjs.map +1 -1
  32. package/dist/cjs/not-found.d.cts +1 -1
  33. package/dist/cjs/router.cjs +2 -1
  34. package/dist/cjs/router.cjs.map +1 -1
  35. package/dist/cjs/routerStores.cjs +75 -0
  36. package/dist/cjs/routerStores.cjs.map +1 -0
  37. package/dist/cjs/routerStores.d.cts +10 -0
  38. package/dist/cjs/ssr/RouterClient.cjs +1 -1
  39. package/dist/cjs/ssr/RouterClient.cjs.map +1 -1
  40. package/dist/cjs/ssr/renderRouterToStream.cjs +1 -1
  41. package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -1
  42. package/dist/cjs/ssr/renderRouterToString.cjs +1 -1
  43. package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -1
  44. package/dist/cjs/useBlocker.cjs +12 -3
  45. package/dist/cjs/useBlocker.cjs.map +1 -1
  46. package/dist/cjs/useCanGoBack.cjs +6 -2
  47. package/dist/cjs/useCanGoBack.cjs.map +1 -1
  48. package/dist/cjs/useCanGoBack.d.cts +2 -1
  49. package/dist/cjs/useLoaderDeps.cjs +2 -3
  50. package/dist/cjs/useLoaderDeps.cjs.map +1 -1
  51. package/dist/cjs/useLocation.cjs +13 -2
  52. package/dist/cjs/useLocation.cjs.map +1 -1
  53. package/dist/cjs/useMatch.cjs +27 -15
  54. package/dist/cjs/useMatch.cjs.map +1 -1
  55. package/dist/cjs/useParams.cjs +1 -1
  56. package/dist/cjs/useParams.cjs.map +1 -1
  57. package/dist/cjs/useRouterState.cjs +12 -30
  58. package/dist/cjs/useRouterState.cjs.map +1 -1
  59. package/dist/cjs/useSearch.cjs +2 -1
  60. package/dist/cjs/useSearch.cjs.map +1 -1
  61. package/dist/cjs/utils.cjs +3 -17
  62. package/dist/cjs/utils.cjs.map +1 -1
  63. package/dist/cjs/utils.d.cts +0 -5
  64. package/dist/esm/Asset.js +6 -6
  65. package/dist/esm/Asset.js.map +1 -1
  66. package/dist/esm/HeadContent.dev.js +12 -2
  67. package/dist/esm/HeadContent.dev.js.map +1 -1
  68. package/dist/esm/HeadContent.js +12 -2
  69. package/dist/esm/HeadContent.js.map +1 -1
  70. package/dist/esm/Match.d.ts +1 -3
  71. package/dist/esm/Match.js +267 -250
  72. package/dist/esm/Match.js.map +1 -1
  73. package/dist/esm/Matches.js +40 -39
  74. package/dist/esm/Matches.js.map +1 -1
  75. package/dist/esm/RouterProvider.d.ts +1 -1
  76. package/dist/esm/RouterProvider.js +10 -7
  77. package/dist/esm/RouterProvider.js.map +1 -1
  78. package/dist/esm/ScriptOnce.js +2 -2
  79. package/dist/esm/ScriptOnce.js.map +1 -1
  80. package/dist/esm/Scripts.d.ts +2 -1
  81. package/dist/esm/Scripts.js +21 -11
  82. package/dist/esm/Scripts.js.map +1 -1
  83. package/dist/esm/Transitioner.js +58 -37
  84. package/dist/esm/Transitioner.js.map +1 -1
  85. package/dist/esm/headContentUtils.d.ts +2 -1
  86. package/dist/esm/headContentUtils.js +26 -23
  87. package/dist/esm/headContentUtils.js.map +1 -1
  88. package/dist/esm/index.dev.js +1 -1
  89. package/dist/esm/index.js +1 -1
  90. package/dist/esm/link.js +146 -104
  91. package/dist/esm/link.js.map +1 -1
  92. package/dist/esm/matchContext.d.ts +8 -2
  93. package/dist/esm/matchContext.js +7 -4
  94. package/dist/esm/matchContext.js.map +1 -1
  95. package/dist/esm/not-found.d.ts +1 -1
  96. package/dist/esm/not-found.js +6 -3
  97. package/dist/esm/not-found.js.map +1 -1
  98. package/dist/esm/router.js +2 -1
  99. package/dist/esm/router.js.map +1 -1
  100. package/dist/esm/routerStores.d.ts +10 -0
  101. package/dist/esm/routerStores.js +73 -0
  102. package/dist/esm/routerStores.js.map +1 -0
  103. package/dist/esm/scroll-restoration.js +2 -2
  104. package/dist/esm/scroll-restoration.js.map +1 -1
  105. package/dist/esm/ssr/RouterClient.js +1 -1
  106. package/dist/esm/ssr/RouterClient.js.map +1 -1
  107. package/dist/esm/ssr/renderRouterToStream.js +1 -1
  108. package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
  109. package/dist/esm/ssr/renderRouterToString.js +1 -1
  110. package/dist/esm/ssr/renderRouterToString.js.map +1 -1
  111. package/dist/esm/useBlocker.js +12 -3
  112. package/dist/esm/useBlocker.js.map +1 -1
  113. package/dist/esm/useCanGoBack.d.ts +2 -1
  114. package/dist/esm/useCanGoBack.js +4 -2
  115. package/dist/esm/useCanGoBack.js.map +1 -1
  116. package/dist/esm/useLoaderDeps.js +2 -3
  117. package/dist/esm/useLoaderDeps.js.map +1 -1
  118. package/dist/esm/useLocation.js +11 -2
  119. package/dist/esm/useLocation.js.map +1 -1
  120. package/dist/esm/useMatch.js +28 -16
  121. package/dist/esm/useMatch.js.map +1 -1
  122. package/dist/esm/useParams.js +1 -1
  123. package/dist/esm/useParams.js.map +1 -1
  124. package/dist/esm/useRouterState.js +11 -30
  125. package/dist/esm/useRouterState.js.map +1 -1
  126. package/dist/esm/useSearch.js +2 -1
  127. package/dist/esm/useSearch.js.map +1 -1
  128. package/dist/esm/utils.d.ts +0 -5
  129. package/dist/esm/utils.js +4 -17
  130. package/dist/esm/utils.js.map +1 -1
  131. package/dist/source/Asset.jsx +3 -3
  132. package/dist/source/Asset.jsx.map +1 -1
  133. package/dist/source/HeadContent.dev.jsx +5 -1
  134. package/dist/source/HeadContent.dev.jsx.map +1 -1
  135. package/dist/source/HeadContent.jsx +5 -1
  136. package/dist/source/HeadContent.jsx.map +1 -1
  137. package/dist/source/Match.d.ts +1 -3
  138. package/dist/source/Match.jsx +260 -264
  139. package/dist/source/Match.jsx.map +1 -1
  140. package/dist/source/Matches.jsx +46 -46
  141. package/dist/source/Matches.jsx.map +1 -1
  142. package/dist/source/RouterProvider.d.ts +1 -1
  143. package/dist/source/RouterProvider.jsx +13 -9
  144. package/dist/source/RouterProvider.jsx.map +1 -1
  145. package/dist/source/Scripts.d.ts +2 -1
  146. package/dist/source/Scripts.jsx +46 -47
  147. package/dist/source/Scripts.jsx.map +1 -1
  148. package/dist/source/Transitioner.jsx +80 -44
  149. package/dist/source/Transitioner.jsx.map +1 -1
  150. package/dist/source/headContentUtils.d.ts +2 -1
  151. package/dist/source/headContentUtils.jsx +79 -80
  152. package/dist/source/headContentUtils.jsx.map +1 -1
  153. package/dist/source/link.jsx +145 -112
  154. package/dist/source/link.jsx.map +1 -1
  155. package/dist/source/matchContext.d.ts +8 -2
  156. package/dist/source/matchContext.jsx +7 -3
  157. package/dist/source/matchContext.jsx.map +1 -1
  158. package/dist/source/not-found.d.ts +1 -1
  159. package/dist/source/not-found.jsx +6 -5
  160. package/dist/source/not-found.jsx.map +1 -1
  161. package/dist/source/router.js +2 -1
  162. package/dist/source/router.js.map +1 -1
  163. package/dist/source/routerStores.d.ts +10 -0
  164. package/dist/source/routerStores.js +82 -0
  165. package/dist/source/routerStores.js.map +1 -0
  166. package/dist/source/ssr/RouterClient.jsx +1 -1
  167. package/dist/source/ssr/RouterClient.jsx.map +1 -1
  168. package/dist/source/ssr/renderRouterToStream.jsx +1 -1
  169. package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
  170. package/dist/source/ssr/renderRouterToString.jsx +1 -1
  171. package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
  172. package/dist/source/useBlocker.jsx +19 -8
  173. package/dist/source/useBlocker.jsx.map +1 -1
  174. package/dist/source/useCanGoBack.d.ts +2 -1
  175. package/dist/source/useCanGoBack.js +4 -2
  176. package/dist/source/useCanGoBack.js.map +1 -1
  177. package/dist/source/useLoaderDeps.jsx +2 -3
  178. package/dist/source/useLoaderDeps.jsx.map +1 -1
  179. package/dist/source/useLocation.jsx +13 -3
  180. package/dist/source/useLocation.jsx.map +1 -1
  181. package/dist/source/useMatch.jsx +33 -23
  182. package/dist/source/useMatch.jsx.map +1 -1
  183. package/dist/source/useParams.jsx +1 -1
  184. package/dist/source/useParams.jsx.map +1 -1
  185. package/dist/source/useRouterState.jsx +14 -55
  186. package/dist/source/useRouterState.jsx.map +1 -1
  187. package/dist/source/useSearch.jsx +2 -1
  188. package/dist/source/useSearch.jsx.map +1 -1
  189. package/dist/source/utils.d.ts +0 -5
  190. package/dist/source/utils.js +2 -15
  191. package/dist/source/utils.js.map +1 -1
  192. package/package.json +7 -7
  193. package/skills/solid-router/SKILL.md +2 -0
  194. package/src/Asset.tsx +3 -3
  195. package/src/HeadContent.dev.tsx +10 -1
  196. package/src/HeadContent.tsx +10 -1
  197. package/src/Match.tsx +395 -349
  198. package/src/Matches.tsx +55 -54
  199. package/src/RouterProvider.tsx +13 -10
  200. package/src/Scripts.tsx +55 -54
  201. package/src/Transitioner.tsx +103 -60
  202. package/src/headContentUtils.tsx +104 -96
  203. package/src/link.tsx +188 -146
  204. package/src/matchContext.tsx +16 -7
  205. package/src/not-found.tsx +6 -6
  206. package/src/router.ts +2 -1
  207. package/src/routerStores.ts +119 -0
  208. package/src/ssr/RouterClient.tsx +1 -1
  209. package/src/ssr/renderRouterToStream.tsx +1 -1
  210. package/src/ssr/renderRouterToString.tsx +1 -1
  211. package/src/useBlocker.tsx +80 -63
  212. package/src/useCanGoBack.ts +6 -2
  213. package/src/useLoaderDeps.tsx +2 -3
  214. package/src/useLocation.tsx +18 -5
  215. package/src/useMatch.tsx +37 -38
  216. package/src/useParams.tsx +2 -3
  217. package/src/useRouterState.tsx +21 -67
  218. package/src/useSearch.tsx +2 -1
  219. package/src/utils.ts +2 -24
package/src/Matches.tsx CHANGED
@@ -1,12 +1,11 @@
1
1
  import * as Solid from 'solid-js'
2
2
  import warning from 'tiny-warning'
3
- import { rootRouteId } from '@tanstack/router-core'
3
+ import { replaceEqualDeep, rootRouteId } from '@tanstack/router-core'
4
4
  import { isServer } from '@tanstack/router-core/isServer'
5
5
  import { CatchBoundary, ErrorComponent } from './CatchBoundary'
6
- import { useRouterState } from './useRouterState'
7
6
  import { useRouter } from './useRouter'
8
7
  import { Transitioner } from './Transitioner'
9
- import { matchContext } from './matchContext'
8
+ import { nearestMatchContext } from './matchContext'
10
9
  import { SafeFragment } from './SafeFragment'
11
10
  import { Match } from './Match'
12
11
  import type {
@@ -24,11 +23,10 @@ import type {
24
23
  ResolveRelativePath,
25
24
  ResolveRoute,
26
25
  RouteByPath,
27
- RouterState,
28
26
  ToSubOptionsProps,
29
27
  } from '@tanstack/router-core'
30
28
 
31
- const MatchContext = matchContext as unknown as Solid.Component<{
29
+ const NearestMatchContext = nearestMatchContext as unknown as Solid.Component<{
32
30
  value: any
33
31
  children: any
34
32
  }>
@@ -77,15 +75,23 @@ export function Matches() {
77
75
 
78
76
  function MatchesInner() {
79
77
  const router = useRouter()
80
- const matchId = useRouterState({
81
- select: (s) => {
82
- return s.matches[0]?.id
83
- },
84
- })
85
-
86
- const resetKey = useRouterState({
87
- select: (s) => s.loadedAt,
88
- })
78
+ const matchId = () => router.stores.firstMatchId.state
79
+ const routeId = () => (matchId() ? rootRouteId : undefined)
80
+ const match = () =>
81
+ routeId()
82
+ ? router.stores.getMatchStoreByRouteId(rootRouteId).state
83
+ : undefined
84
+ const hasPendingMatch = () =>
85
+ routeId()
86
+ ? Boolean(router.stores.pendingRouteIds.state[rootRouteId])
87
+ : false
88
+ const resetKey = () => router.stores.loadedAt.state
89
+ const nearestMatch = {
90
+ matchId,
91
+ routeId,
92
+ match,
93
+ hasPending: hasPendingMatch,
94
+ }
89
95
 
90
96
  const matchContent = () => (
91
97
  <Solid.Show when={matchId()}>
@@ -96,11 +102,15 @@ function MatchesInner() {
96
102
  if (router.options.disableGlobalCatchBoundary) {
97
103
  // When disableGlobalCatchBoundary is true, render without any internal
98
104
  // error boundary so errors bubble up freely to an external Errored boundary.
99
- return <MatchContext value={matchId}>{matchContent()}</MatchContext>
105
+ return (
106
+ <NearestMatchContext value={nearestMatch}>
107
+ {matchContent()}
108
+ </NearestMatchContext>
109
+ )
100
110
  }
101
111
 
102
112
  return (
103
- <MatchContext value={matchId}>
113
+ <NearestMatchContext value={nearestMatch}>
104
114
  <CatchBoundary
105
115
  getResetKey={() => resetKey()}
106
116
  errorComponent={ErrorComponent}
@@ -109,8 +119,7 @@ function MatchesInner() {
109
119
  ? (error) => {
110
120
  warning(
111
121
  false,
112
- `The following error wasn't caught by any route! At the very leas
113
- t, consider setting an 'errorComponent' in your RootRoute!`,
122
+ `The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
114
123
  )
115
124
  warning(false, error.message || error.toString())
116
125
  }
@@ -119,7 +128,7 @@ function MatchesInner() {
119
128
  >
120
129
  {matchContent()}
121
130
  </CatchBoundary>
122
- </MatchContext>
131
+ </NearestMatchContext>
123
132
  )
124
133
  }
125
134
 
@@ -138,10 +147,6 @@ export type UseMatchRouteOptions<
138
147
  export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
139
148
  const router = useRouter()
140
149
 
141
- const status = useRouterState({
142
- select: (s) => s.status,
143
- })
144
-
145
150
  return <
146
151
  const TFrom extends string = string,
147
152
  const TTo extends string | undefined = undefined,
@@ -152,10 +157,10 @@ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
152
157
  ): Solid.Accessor<
153
158
  false | Expand<ResolveRoute<TRouter, TFrom, TTo>['types']['allParams']>
154
159
  > => {
155
- const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts
160
+ return Solid.createMemo(() => {
161
+ const { pending, caseSensitive, fuzzy, includeSearch, ...rest } = opts
156
162
 
157
- const matchRoute = Solid.createMemo(() => {
158
- status()
163
+ router.stores.matchRouteReactivity.state
159
164
  return router.matchRoute(rest as any, {
160
165
  pending,
161
166
  caseSensitive,
@@ -163,8 +168,6 @@ export function useMatchRoute<TRouter extends AnyRouter = RegisteredRouter>() {
163
168
  includeSearch,
164
169
  })
165
170
  })
166
-
167
- return matchRoute
168
171
  }
169
172
  }
170
173
 
@@ -193,24 +196,21 @@ export function MatchRoute<
193
196
  const TMaskFrom extends string = TFrom,
194
197
  const TMaskTo extends string = '',
195
198
  >(props: MakeMatchRouteOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): any {
196
- const status = useRouterState({
197
- select: (s) => s.status,
198
- })
199
+ const matchRoute = useMatchRoute()
200
+ const params = matchRoute(props as any)
199
201
 
200
- return (
201
- <Solid.Show when={status()} keyed>
202
- {(_) => {
203
- const matchRoute = useMatchRoute()
204
- const params = matchRoute(props as any)() as boolean
205
- const child = props.children
206
- if (typeof child === 'function') {
207
- return (child as any)(params)
208
- }
202
+ const renderedChild = Solid.createMemo(() => {
203
+ const matchedParams = params()
204
+ const child = props.children
209
205
 
210
- return params ? child : null
211
- }}
212
- </Solid.Show>
213
- )
206
+ if (typeof child === 'function') {
207
+ return (child as any)(matchedParams)
208
+ }
209
+
210
+ return matchedParams ? child : null
211
+ })
212
+
213
+ return <>{renderedChild()}</>
214
214
  }
215
215
 
216
216
  export interface UseMatchesBaseOptions<TRouter extends AnyRouter, TSelected> {
@@ -228,14 +228,15 @@ export function useMatches<
228
228
  >(
229
229
  opts?: UseMatchesBaseOptions<TRouter, TSelected>,
230
230
  ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
231
- return useRouterState({
232
- select: (state: RouterState<TRouter['routeTree']>) => {
233
- const matches = state.matches
234
- return opts?.select
235
- ? opts.select(matches as Array<MakeRouteMatchUnion<TRouter>>)
236
- : matches
237
- },
238
- } as any) as Solid.Accessor<UseMatchesResult<TRouter, TSelected>>
231
+ const router = useRouter<TRouter>()
232
+ return Solid.createMemo((prev: TSelected | undefined) => {
233
+ const matches = router.stores.activeMatchesSnapshot.state as Array<
234
+ MakeRouteMatchUnion<TRouter>
235
+ >
236
+ const res = opts?.select ? opts.select(matches) : matches
237
+ if (prev === undefined) return res
238
+ return replaceEqualDeep(prev, res) as any
239
+ }) as Solid.Accessor<UseMatchesResult<TRouter, TSelected>>
239
240
  }
240
241
 
241
242
  export function useParentMatches<
@@ -244,7 +245,7 @@ export function useParentMatches<
244
245
  >(
245
246
  opts?: UseMatchesBaseOptions<TRouter, TSelected>,
246
247
  ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
247
- const contextMatchId = Solid.useContext(matchContext)
248
+ const contextMatchId = Solid.useContext(nearestMatchContext).matchId
248
249
 
249
250
  return useMatches({
250
251
  select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
@@ -263,7 +264,7 @@ export function useChildMatches<
263
264
  >(
264
265
  opts?: UseMatchesBaseOptions<TRouter, TSelected>,
265
266
  ): Solid.Accessor<UseMatchesResult<TRouter, TSelected>> {
266
- const contextMatchId = Solid.useContext(matchContext)
267
+ const contextMatchId = Solid.useContext(nearestMatchContext).matchId
267
268
 
268
269
  return useMatches({
269
270
  select: (matches: Array<MakeRouteMatchUnion<TRouter>>) => {
@@ -1,3 +1,4 @@
1
+ import * as Solid from 'solid-js'
1
2
  import { routerContext } from './routerContext'
2
3
  import { SafeFragment } from './SafeFragment'
3
4
  import { Matches } from './Matches'
@@ -6,7 +7,6 @@ import type {
6
7
  RegisteredRouter,
7
8
  RouterOptions,
8
9
  } from '@tanstack/router-core'
9
- import type * as Solid from 'solid-js'
10
10
 
11
11
  const RouterContext = routerContext as unknown as Solid.Component<{
12
12
  value: any
@@ -23,15 +23,18 @@ export function RouterContextProvider<
23
23
  }: RouterProps<TRouter, TDehydrated> & {
24
24
  children: () => Solid.JSX.Element
25
25
  }) {
26
- // Allow the router to update options on the router instance
27
- router.update({
28
- ...router.options,
29
- ...rest,
30
- context: {
31
- ...router.options.context,
32
- ...rest.context,
33
- },
34
- } as any)
26
+ if (Object.keys(rest).length > 0) {
27
+ Solid.runWithOwner(null, () => {
28
+ router.update({
29
+ ...router.options,
30
+ ...rest,
31
+ context: {
32
+ ...router.options.context,
33
+ ...rest.context,
34
+ },
35
+ } as any)
36
+ })
37
+ }
35
38
 
36
39
  const OptionalWrapper = router.options.Wrap || SafeFragment
37
40
 
package/src/Scripts.tsx CHANGED
@@ -1,77 +1,78 @@
1
1
  import { NoHydration } from '@solidjs/web'
2
+ import * as Solid from 'solid-js'
2
3
  import { Asset } from './Asset'
3
- import { useRouterState } from './useRouterState'
4
4
  import { useRouter } from './useRouter'
5
5
  import type { RouterManagedTag } from '@tanstack/router-core'
6
6
 
7
7
  export const Scripts = () => {
8
8
  const router = useRouter()
9
9
  const nonce = router.options.ssr?.nonce
10
- const assetScripts = useRouterState({
11
- select: (state) => {
12
- const assetScripts: Array<RouterManagedTag> = []
13
- const manifest = router.ssr?.manifest
10
+ const activeMatches = Solid.createMemo(
11
+ () => router.stores.activeMatchesSnapshot.state,
12
+ )
13
+ const assetScripts = Solid.createMemo(() => {
14
+ const assetScripts: Array<RouterManagedTag> = []
15
+ const manifest = router.ssr?.manifest
14
16
 
15
- if (!manifest) {
16
- return []
17
- }
17
+ if (!manifest) {
18
+ return []
19
+ }
18
20
 
19
- state.matches
20
- .map((match) => router.looseRoutesById[match.routeId]!)
21
- .forEach((route) =>
22
- manifest.routes[route.id]?.assets
23
- ?.filter((d) => d.tag === 'script')
24
- .forEach((asset) => {
25
- assetScripts.push({
26
- tag: 'script',
27
- attrs: { ...asset.attrs, nonce },
28
- children: asset.children,
29
- } as any)
30
- }),
31
- )
21
+ activeMatches()
22
+ .map((match) => router.looseRoutesById[match.routeId]!)
23
+ .forEach((route) =>
24
+ manifest.routes[route.id]?.assets
25
+ ?.filter((d) => d.tag === 'script')
26
+ .forEach((asset) => {
27
+ assetScripts.push({
28
+ tag: 'script',
29
+ attrs: { ...asset.attrs, nonce },
30
+ children: asset.children,
31
+ } as any)
32
+ }),
33
+ )
32
34
 
33
- return assetScripts
34
- },
35
+ return assetScripts
35
36
  })
36
37
 
37
- const scripts = useRouterState({
38
- select: (state) => ({
39
- scripts: (
40
- state.matches
41
- .map((match) => match.scripts!)
42
- .flat(1)
43
- .filter(Boolean) as Array<RouterManagedTag>
44
- ).map(({ children, ...script }) => ({
45
- tag: 'script',
46
- attrs: {
47
- ...script,
48
- nonce,
49
- },
50
- children,
51
- })),
52
- }),
53
- })
38
+ const scripts = Solid.createMemo(() =>
39
+ (
40
+ activeMatches()
41
+ .map((match) => match.scripts!)
42
+ .flat(1)
43
+ .filter(Boolean) as Array<RouterManagedTag>
44
+ ).map(({ children, ...script }) => ({
45
+ tag: 'script',
46
+ attrs: {
47
+ ...script,
48
+ nonce,
49
+ },
50
+ children,
51
+ })),
52
+ )
54
53
 
55
- let serverBufferedScript: RouterManagedTag | undefined = undefined
54
+ const serverBufferedScript: RouterManagedTag | undefined = router.serverSsr
55
+ ? router.serverSsr.takeBufferedScripts()
56
+ : undefined
56
57
 
57
- if (router.serverSsr) {
58
- serverBufferedScript = router.serverSsr.takeBufferedScripts()
59
- }
58
+ const allScripts = Solid.createMemo(() => {
59
+ const result = [...scripts(), ...assetScripts()] as Array<RouterManagedTag>
60
60
 
61
- const allScripts = [
62
- ...scripts().scripts,
63
- ...assetScripts(),
64
- ] as Array<RouterManagedTag>
61
+ if (serverBufferedScript) {
62
+ result.unshift(serverBufferedScript)
63
+ }
65
64
 
66
- if (serverBufferedScript) {
67
- allScripts.unshift(serverBufferedScript)
68
- }
65
+ return result
66
+ })
69
67
 
70
68
  return (
71
69
  <NoHydration>
72
- {allScripts.map((asset, _i) => (
73
- <Asset {...asset} />
74
- ))}
70
+ <Solid.For each={allScripts()}>
71
+ {(asset) => {
72
+ const a = Solid.untrack(asset)
73
+ return <Asset {...a} />
74
+ }}
75
+ </Solid.For>
75
76
  </NoHydration>
76
77
  )
77
78
  }
@@ -1,44 +1,62 @@
1
1
  import * as Solid from 'solid-js'
2
- import {
3
- getLocationChangeInfo,
4
- handleHashScroll,
5
- trimPathRight,
6
- } from '@tanstack/router-core'
2
+ import { getLocationChangeInfo, trimPathRight } from '@tanstack/router-core'
7
3
  import { useRouter } from './useRouter'
8
- import { useRouterState } from './useRouterState'
9
- import { usePrevious } from './utils'
4
+ import type { ParsedLocation } from '@tanstack/router-core'
5
+
6
+ /**
7
+ * Inline version of handleHashScroll that accepts a pre-captured location
8
+ * to avoid reading router.stores.location.state inside an effect callback
9
+ * (which would trigger a Solid v2 reactive warning).
10
+ */
11
+ function handleHashScrollWithLocation(_router: any, location: ParsedLocation) {
12
+ if (typeof document !== 'undefined' && (document as any).querySelector) {
13
+ const hashScrollIntoViewOptions =
14
+ location.state.__hashScrollIntoViewOptions ?? true
15
+
16
+ if (hashScrollIntoViewOptions && location.hash !== '') {
17
+ const el = document.getElementById(location.hash)
18
+ if (el) {
19
+ el.scrollIntoView(hashScrollIntoViewOptions)
20
+ }
21
+ }
22
+ }
23
+ }
10
24
 
11
25
  export function Transitioner() {
12
26
  const router = useRouter()
13
27
  let mountLoadForRouter = { router, mounted: false }
14
- const isLoading = useRouterState({
15
- select: ({ isLoading }) => isLoading,
16
- })
28
+ const isLoading = Solid.createMemo(() => router.stores.isLoading.state)
17
29
 
18
30
  const [isSolidTransitioning] = [() => false]
19
31
 
20
32
  // Track pending state changes
21
- const hasPendingMatches = useRouterState({
22
- select: (s) => s.matches.some((d) => d.status === 'pending'),
23
- })
24
-
25
- const previousIsLoading = usePrevious(isLoading)
33
+ const hasPendingMatches = Solid.createMemo(
34
+ () => router.stores.hasPendingMatches.state,
35
+ )
26
36
 
27
- const isAnyPending = () =>
28
- isLoading() || isSolidTransitioning() || hasPendingMatches()
29
- const previousIsAnyPending = usePrevious(isAnyPending)
37
+ const isAnyPending = Solid.createMemo(
38
+ () => isLoading() || isSolidTransitioning() || hasPendingMatches(),
39
+ )
30
40
 
31
- const isPagePending = () => isLoading() || hasPendingMatches()
32
- const previousIsPagePending = usePrevious(isPagePending)
41
+ const isPagePending = Solid.createMemo(
42
+ () => isLoading() || hasPendingMatches(),
43
+ )
33
44
 
34
45
  router.startTransition = (fn: () => void | Promise<void>) => {
35
46
  Solid.runWithOwner(null, fn)
47
+ try {
48
+ Solid.flush()
49
+ } catch {
50
+ // flush() throws inside reactive contexts — Solid auto-flushes there
51
+ }
36
52
  }
37
53
 
38
54
  // Subscribe to location changes
39
55
  // and try to load the new location
40
56
  Solid.onSettled(() => {
41
- const unsub = router.history.subscribe(router.load)
57
+ const unsub = router.history.subscribe(() => {
58
+ queueMicrotask(() => router.load())
59
+ })
42
60
 
43
61
  // Refresh latestLocation from the current browser URL before comparing.
44
62
  // The URL may have been changed synchronously (e.g. via replaceState) after
@@ -65,22 +83,27 @@ export function Transitioner() {
65
83
  router.commitLocation({ ...nextLocation, replace: true })
66
84
  }
67
85
 
68
- Solid.onCleanup(() => {
86
+ return () => {
69
87
  unsub()
70
- })
88
+ }
71
89
  })
72
90
 
73
91
  // Try to load the initial location
74
- Solid.createTrackedEffect(() => {
75
- Solid.untrack(() => {
76
- if (
77
- // if we are hydrating from SSR, loading is triggered in ssr-client
78
- (typeof window !== 'undefined' && router.ssr) ||
79
- (mountLoadForRouter.router === router && mountLoadForRouter.mounted)
80
- ) {
81
- return
82
- }
83
- mountLoadForRouter = { router, mounted: true }
92
+ // In Solid v2, signal updates inside onSettled cannot be flushed
93
+ // synchronously (flush() throws). router.load() sets signals via batch(),
94
+ // and the code that runs immediately after needs those values committed.
95
+ // By deferring to queueMicrotask, the load runs outside the reactive
96
+ // scheduling frame so flush() works correctly.
97
+ Solid.onSettled(() => {
98
+ if (
99
+ // if we are hydrating from SSR, loading is triggered in ssr-client
100
+ (typeof window !== 'undefined' && router.ssr) ||
101
+ (mountLoadForRouter.router === router && mountLoadForRouter.mounted)
102
+ ) {
103
+ return
104
+ }
105
+ mountLoadForRouter = { router, mounted: true }
106
+ queueMicrotask(() => {
84
107
  const tryLoad = async () => {
85
108
  try {
86
109
  await router.load()
@@ -91,50 +114,70 @@ export function Transitioner() {
91
114
  tryLoad()
92
115
  })
93
116
  })
94
-
95
- Solid.createEffect(
96
- () => [previousIsLoading(), isLoading()] as const,
97
- ([previousIsLoading, isLoading]) => {
98
- if (previousIsLoading.previous && !isLoading) {
117
+ Solid.createRenderEffect(
118
+ () =>
119
+ [
120
+ isLoading(),
121
+ isPagePending(),
122
+ isAnyPending(),
123
+ router.stores.location.state,
124
+ router.stores.resolvedLocation.state,
125
+ ] as const,
126
+ (
127
+ [
128
+ currentIsLoading,
129
+ currentIsPagePending,
130
+ currentIsAnyPending,
131
+ loc,
132
+ resolvedLoc,
133
+ ],
134
+ prev,
135
+ ) => {
136
+ // Guard: if location state isn't available yet, skip all event emissions
137
+ if (!loc) return
138
+
139
+ const previousIsLoading = prev?.[0]
140
+ const previousIsPagePending = prev?.[1]
141
+ const previousIsAnyPending = prev?.[2]
142
+
143
+ // onLoad: when the router finishes loading
144
+ if (previousIsLoading && !currentIsLoading) {
99
145
  router.emit({
100
146
  type: 'onLoad',
101
- ...getLocationChangeInfo(router.state),
147
+ ...getLocationChangeInfo(loc, resolvedLoc),
102
148
  })
103
149
  }
104
- },
105
- )
106
150
 
107
- Solid.createEffect(
108
- () => [isPagePending(), previousIsPagePending()] as const,
109
- ([isPagePending, previousIsPagePending]) => {
110
- // emit onBeforeRouteMount
111
- if (previousIsPagePending.previous && !isPagePending) {
151
+ // onBeforeRouteMount: must fire before onResolved
152
+ if (previousIsPagePending && !currentIsPagePending) {
112
153
  router.emit({
113
154
  type: 'onBeforeRouteMount',
114
- ...getLocationChangeInfo(router.state),
155
+ ...getLocationChangeInfo(loc, resolvedLoc),
115
156
  })
116
157
  }
117
- },
118
- )
119
158
 
120
- Solid.createEffect(
121
- () => [isAnyPending(), previousIsAnyPending()] as const,
122
- ([isAnyPending, previousIsAnyPending]) => {
123
- if (previousIsAnyPending.previous && !isAnyPending) {
124
- const changeInfo = getLocationChangeInfo(router.state)
159
+ // onResolved: fires after onBeforeRouteMount
160
+ if (previousIsAnyPending && !currentIsAnyPending) {
161
+ const changeInfo = getLocationChangeInfo(loc, resolvedLoc)
125
162
  router.emit({
126
163
  type: 'onResolved',
127
164
  ...changeInfo,
128
165
  })
129
166
 
130
- router.__store.setState((s) => ({
131
- ...s,
132
- status: 'idle',
133
- resolvedLocation: s.location,
134
- }))
167
+ Solid.runWithOwner(null, () => {
168
+ router.batch(() => {
169
+ router.stores.status.setState(() => 'idle')
170
+ // Use `loc` from the source tuple to avoid reading
171
+ // router.stores.location.state inside the effect callback
172
+ router.stores.resolvedLocation.setState(() => loc)
173
+ })
174
+ })
135
175
 
136
176
  if (changeInfo.hrefChanged) {
137
- handleHashScroll(router)
177
+ // Pass the already-captured location to avoid a reactive read
178
+ // inside the effect callback (handleHashScroll would otherwise
179
+ // read router.stores.location.state which triggers a warning)
180
+ handleHashScrollWithLocation(router, loc)
138
181
  }
139
182
  }
140
183
  },