@tanstack/solid-router 1.124.0 → 1.125.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.
- package/dist/cjs/Match.cjs +89 -63
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/lazyRouteComponent.cjs +3 -23
- package/dist/cjs/lazyRouteComponent.cjs.map +1 -1
- package/dist/cjs/lazyRouteComponent.d.cts +1 -1
- package/dist/esm/Match.js +90 -64
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +1 -1
- package/dist/esm/lazyRouteComponent.js +2 -22
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/source/Match.jsx +62 -60
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/lazyRouteComponent.d.ts +1 -1
- package/dist/source/lazyRouteComponent.jsx +7 -21
- package/dist/source/lazyRouteComponent.jsx.map +1 -1
- package/package.json +2 -2
- package/src/Match.tsx +85 -80
- package/src/lazyRouteComponent.tsx +6 -26
package/src/Match.tsx
CHANGED
|
@@ -22,18 +22,19 @@ import type { AnyRoute, RootRouteOptions } from '@tanstack/router-core'
|
|
|
22
22
|
|
|
23
23
|
export const Match = (props: { matchId: string }) => {
|
|
24
24
|
const router = useRouter()
|
|
25
|
-
const
|
|
25
|
+
const matchState = useRouterState({
|
|
26
26
|
select: (s) => {
|
|
27
|
-
|
|
27
|
+
const match = s.matches.find((d) => d.id === props.matchId)
|
|
28
|
+
|
|
29
|
+
invariant(
|
|
30
|
+
match,
|
|
31
|
+
`Could not find match for matchId "${props.matchId}". Please file an issue!`,
|
|
32
|
+
)
|
|
33
|
+
return pick(match, ['routeId', 'ssr', '_displayPending'])
|
|
28
34
|
},
|
|
29
35
|
})
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
routeId,
|
|
33
|
-
`Could not find routeId for matchId "${props.matchId}". Please file an issue!`,
|
|
34
|
-
)
|
|
35
|
-
|
|
36
|
-
const route: () => AnyRoute = () => router.routesById[routeId()]
|
|
37
|
+
const route: () => AnyRoute = () => router.routesById[matchState().routeId]
|
|
37
38
|
|
|
38
39
|
const PendingComponent = () =>
|
|
39
40
|
route().options.pendingComponent ?? router.options.defaultPendingComponent
|
|
@@ -51,12 +52,18 @@ export const Match = (props: { matchId: string }) => {
|
|
|
51
52
|
router.options.notFoundRoute?.options.component)
|
|
52
53
|
: route().options.notFoundComponent
|
|
53
54
|
|
|
55
|
+
const resolvedNoSsr =
|
|
56
|
+
matchState().ssr === false || matchState().ssr === 'data-only'
|
|
57
|
+
|
|
54
58
|
const ResolvedSuspenseBoundary = () =>
|
|
55
59
|
// If we're on the root route, allow forcefully wrapping in suspense
|
|
56
|
-
(!route().isRoot ||
|
|
60
|
+
(!route().isRoot ||
|
|
61
|
+
route().options.wrapInSuspense ||
|
|
62
|
+
resolvedNoSsr ||
|
|
63
|
+
matchState()._displayPending) &&
|
|
57
64
|
(route().options.wrapInSuspense ??
|
|
58
65
|
PendingComponent() ??
|
|
59
|
-
(route().options.errorComponent as any)?.preload)
|
|
66
|
+
((route().options.errorComponent as any)?.preload || resolvedNoSsr))
|
|
60
67
|
? Solid.Suspense
|
|
61
68
|
: SafeFragment
|
|
62
69
|
|
|
@@ -106,7 +113,7 @@ export const Match = (props: { matchId: string }) => {
|
|
|
106
113
|
// route ID which doesn't match the current route, rethrow the error
|
|
107
114
|
if (
|
|
108
115
|
!routeNotFoundComponent() ||
|
|
109
|
-
(error.routeId && error.routeId !== routeId) ||
|
|
116
|
+
(error.routeId && error.routeId !== matchState().routeId) ||
|
|
110
117
|
(!error.routeId && !route().isRoot)
|
|
111
118
|
)
|
|
112
119
|
throw error
|
|
@@ -116,7 +123,19 @@ export const Match = (props: { matchId: string }) => {
|
|
|
116
123
|
)
|
|
117
124
|
}}
|
|
118
125
|
>
|
|
119
|
-
<
|
|
126
|
+
<Solid.Switch>
|
|
127
|
+
<Solid.Match when={resolvedNoSsr || router.isShell}>
|
|
128
|
+
<Solid.Show
|
|
129
|
+
when={!router.isServer}
|
|
130
|
+
fallback={<Dynamic component={PendingComponent()} />}
|
|
131
|
+
>
|
|
132
|
+
<MatchInner matchId={props.matchId} />
|
|
133
|
+
</Solid.Show>
|
|
134
|
+
</Solid.Match>
|
|
135
|
+
<Solid.Match when={!resolvedNoSsr}>
|
|
136
|
+
<MatchInner matchId={props.matchId} />
|
|
137
|
+
</Solid.Match>
|
|
138
|
+
</Solid.Switch>
|
|
120
139
|
</Dynamic>
|
|
121
140
|
</Dynamic>
|
|
122
141
|
</Dynamic>
|
|
@@ -161,8 +180,7 @@ function OnRendered() {
|
|
|
161
180
|
export const MatchInner = (props: { matchId: string }): any => {
|
|
162
181
|
const router = useRouter()
|
|
163
182
|
|
|
164
|
-
|
|
165
|
-
const matchState: Solid.Accessor<any> = useRouterState({
|
|
183
|
+
const matchState = useRouterState({
|
|
166
184
|
select: (s) => {
|
|
167
185
|
const matchIndex = s.matches.findIndex((d) => d.id === props.matchId)
|
|
168
186
|
const match = s.matches[matchIndex]!
|
|
@@ -182,31 +200,19 @@ export const MatchInner = (props: { matchId: string }): any => {
|
|
|
182
200
|
return {
|
|
183
201
|
key,
|
|
184
202
|
routeId,
|
|
185
|
-
match: pick(match, [
|
|
203
|
+
match: pick(match, [
|
|
204
|
+
'id',
|
|
205
|
+
'status',
|
|
206
|
+
'error',
|
|
207
|
+
'_forcePending',
|
|
208
|
+
'_displayPending',
|
|
209
|
+
]),
|
|
186
210
|
}
|
|
187
211
|
},
|
|
188
212
|
})
|
|
189
213
|
|
|
190
214
|
const route = () => router.routesById[matchState().routeId]!
|
|
191
215
|
|
|
192
|
-
// function useChangedDiff(value: any) {
|
|
193
|
-
// const ref = Solid.useRef(value)
|
|
194
|
-
// const changed = ref.current !== value
|
|
195
|
-
// if (changed) {
|
|
196
|
-
// console.log(
|
|
197
|
-
// 'Changed:',
|
|
198
|
-
// value,
|
|
199
|
-
// Object.fromEntries(
|
|
200
|
-
// Object.entries(value).filter(
|
|
201
|
-
// ([key, val]) => val !== ref.current[key],
|
|
202
|
-
// ),
|
|
203
|
-
// ),
|
|
204
|
-
// )
|
|
205
|
-
// }
|
|
206
|
-
// ref.current = value
|
|
207
|
-
// }
|
|
208
|
-
|
|
209
|
-
// useChangedDiff(match)
|
|
210
216
|
const match = () => matchState().match
|
|
211
217
|
|
|
212
218
|
const out = () => {
|
|
@@ -219,47 +225,16 @@ export const MatchInner = (props: { matchId: string }): any => {
|
|
|
219
225
|
|
|
220
226
|
return (
|
|
221
227
|
<Solid.Switch>
|
|
222
|
-
<Solid.Match when={match().
|
|
223
|
-
{(_) => {
|
|
224
|
-
invariant(isNotFound(match().error), 'Expected a notFound error')
|
|
225
|
-
|
|
226
|
-
return renderRouteNotFound(router, route(), match().error)
|
|
227
|
-
}}
|
|
228
|
-
</Solid.Match>
|
|
229
|
-
<Solid.Match when={match().status === 'redirected'}>
|
|
230
|
-
{(_) => {
|
|
231
|
-
invariant(isRedirect(match().error), 'Expected a redirect error')
|
|
232
|
-
|
|
233
|
-
const [loaderResult] = Solid.createResource(async () => {
|
|
234
|
-
await new Promise((r) => setTimeout(r, 0))
|
|
235
|
-
return router.getMatch(match().id)?.loadPromise
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
return <>{loaderResult()}</>
|
|
239
|
-
}}
|
|
240
|
-
</Solid.Match>
|
|
241
|
-
<Solid.Match when={match().status === 'error'}>
|
|
228
|
+
<Solid.Match when={match()._displayPending}>
|
|
242
229
|
{(_) => {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
router.options.defaultErrorComponent) ||
|
|
247
|
-
ErrorComponent
|
|
248
|
-
|
|
249
|
-
return (
|
|
250
|
-
<RouteErrorComponent
|
|
251
|
-
error={match().error}
|
|
252
|
-
info={{
|
|
253
|
-
componentStack: '',
|
|
254
|
-
}}
|
|
255
|
-
/>
|
|
256
|
-
)
|
|
257
|
-
}
|
|
230
|
+
const [displayPendingResult] = Solid.createResource(
|
|
231
|
+
() => router.getMatch(match().id)?.displayPendingPromise,
|
|
232
|
+
)
|
|
258
233
|
|
|
259
|
-
|
|
234
|
+
return <>{displayPendingResult()}</>
|
|
260
235
|
}}
|
|
261
236
|
</Solid.Match>
|
|
262
|
-
<Solid.Match when={match().status === 'pending'}>
|
|
237
|
+
<Solid.Match when={match().status === 'pending' || match()._forcePending}>
|
|
263
238
|
{(_) => {
|
|
264
239
|
const pendingMinMs =
|
|
265
240
|
route().options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
@@ -296,6 +271,46 @@ export const MatchInner = (props: { matchId: string }): any => {
|
|
|
296
271
|
return <>{loaderResult()}</>
|
|
297
272
|
}}
|
|
298
273
|
</Solid.Match>
|
|
274
|
+
<Solid.Match when={match().status === 'notFound'}>
|
|
275
|
+
{(_) => {
|
|
276
|
+
invariant(isNotFound(match().error), 'Expected a notFound error')
|
|
277
|
+
|
|
278
|
+
return renderRouteNotFound(router, route(), match().error)
|
|
279
|
+
}}
|
|
280
|
+
</Solid.Match>
|
|
281
|
+
<Solid.Match when={match().status === 'redirected'}>
|
|
282
|
+
{(_) => {
|
|
283
|
+
invariant(isRedirect(match().error), 'Expected a redirect error')
|
|
284
|
+
|
|
285
|
+
const [loaderResult] = Solid.createResource(async () => {
|
|
286
|
+
await new Promise((r) => setTimeout(r, 0))
|
|
287
|
+
return router.getMatch(match().id)?.loadPromise
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
return <>{loaderResult()}</>
|
|
291
|
+
}}
|
|
292
|
+
</Solid.Match>
|
|
293
|
+
<Solid.Match when={match().status === 'error'}>
|
|
294
|
+
{(_) => {
|
|
295
|
+
if (router.isServer) {
|
|
296
|
+
const RouteErrorComponent =
|
|
297
|
+
(route().options.errorComponent ??
|
|
298
|
+
router.options.defaultErrorComponent) ||
|
|
299
|
+
ErrorComponent
|
|
300
|
+
|
|
301
|
+
return (
|
|
302
|
+
<RouteErrorComponent
|
|
303
|
+
error={match().error}
|
|
304
|
+
info={{
|
|
305
|
+
componentStack: '',
|
|
306
|
+
}}
|
|
307
|
+
/>
|
|
308
|
+
)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
throw match().error
|
|
312
|
+
}}
|
|
313
|
+
</Solid.Match>
|
|
299
314
|
<Solid.Match when={match().status === 'success'}>{out()}</Solid.Match>
|
|
300
315
|
</Solid.Switch>
|
|
301
316
|
)
|
|
@@ -333,16 +348,6 @@ export const Outlet = () => {
|
|
|
333
348
|
|
|
334
349
|
return (
|
|
335
350
|
<Solid.Switch>
|
|
336
|
-
<Solid.Match when={router.isShell}>
|
|
337
|
-
<Solid.Suspense
|
|
338
|
-
fallback={
|
|
339
|
-
<Dynamic component={router.options.defaultPendingComponent} />
|
|
340
|
-
}
|
|
341
|
-
>
|
|
342
|
-
<ErrorComponent error={new Error('ShellBoundaryError')} />
|
|
343
|
-
</Solid.Suspense>
|
|
344
|
-
</Solid.Match>
|
|
345
|
-
|
|
346
351
|
<Solid.Match when={parentGlobalNotFound()}>
|
|
347
352
|
{renderRouteNotFound(router, route(), undefined)}
|
|
348
353
|
</Solid.Match>
|
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
import { Dynamic } from 'solid-js/web'
|
|
2
2
|
import { createResource } from 'solid-js'
|
|
3
|
-
import {
|
|
4
|
-
import { ClientOnly } from './ClientOnly'
|
|
3
|
+
import { isModuleNotFoundError } from '@tanstack/router-core'
|
|
5
4
|
import type { AsyncRouteComponent } from './route'
|
|
6
5
|
|
|
7
|
-
// If the load fails due to module not found, it may mean a new version of
|
|
8
|
-
// the build was deployed and the user's browser is still using an old version.
|
|
9
|
-
// If this happens, the old version in the user's browser would have an outdated
|
|
10
|
-
// URL to the lazy module.
|
|
11
|
-
// In that case, we want to attempt one window refresh to get the latest.
|
|
12
|
-
function isModuleNotFoundError(error: any): boolean {
|
|
13
|
-
return (
|
|
14
|
-
typeof error?.message === 'string' &&
|
|
15
|
-
/Failed to fetch dynamically imported module/.test(error.message)
|
|
16
|
-
)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
6
|
export function lazyRouteComponent<
|
|
20
7
|
T extends Record<string, any>,
|
|
21
8
|
TKey extends keyof T = 'default',
|
|
22
9
|
>(
|
|
23
10
|
importer: () => Promise<T>,
|
|
24
11
|
exportName?: TKey,
|
|
25
|
-
ssr?: () => boolean,
|
|
26
12
|
): T[TKey] extends (props: infer TProps) => any
|
|
27
13
|
? AsyncRouteComponent<TProps>
|
|
28
14
|
: never {
|
|
@@ -31,10 +17,6 @@ export function lazyRouteComponent<
|
|
|
31
17
|
let error: any
|
|
32
18
|
|
|
33
19
|
const load = () => {
|
|
34
|
-
if (typeof document === 'undefined' && ssr?.() === false) {
|
|
35
|
-
comp = (() => null) as any
|
|
36
|
-
return Promise.resolve(comp)
|
|
37
|
-
}
|
|
38
20
|
if (!loadPromise) {
|
|
39
21
|
loadPromise = importer()
|
|
40
22
|
.then((res) => {
|
|
@@ -54,6 +36,11 @@ export function lazyRouteComponent<
|
|
|
54
36
|
// Now that we're out of preload and into actual render path,
|
|
55
37
|
// throw the error if it was a module not found error during preload
|
|
56
38
|
if (error) {
|
|
39
|
+
// If the load fails due to module not found, it may mean a new version of
|
|
40
|
+
// the build was deployed and the user's browser is still using an old version.
|
|
41
|
+
// If this happens, the old version in the user's browser would have an outdated
|
|
42
|
+
// URL to the lazy module.
|
|
43
|
+
// In that case, we want to attempt one window refresh to get the latest.
|
|
57
44
|
if (isModuleNotFoundError(error)) {
|
|
58
45
|
// We don't want an error thrown from preload in this case, because
|
|
59
46
|
// there's nothing we want to do about module not found during preload.
|
|
@@ -94,13 +81,6 @@ export function lazyRouteComponent<
|
|
|
94
81
|
return <>{compResource()}</>
|
|
95
82
|
}
|
|
96
83
|
|
|
97
|
-
if (ssr?.() === false) {
|
|
98
|
-
return (
|
|
99
|
-
<ClientOnly fallback={<Outlet />}>
|
|
100
|
-
<Dynamic component={comp} {...props} />
|
|
101
|
-
</ClientOnly>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
84
|
return <Dynamic component={comp} {...props} />
|
|
105
85
|
}
|
|
106
86
|
|