@tanstack/vue-router 1.141.2 → 1.141.6
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/esm/CatchBoundary.d.ts +4 -2
- package/dist/esm/CatchBoundary.js +29 -28
- package/dist/esm/CatchBoundary.js.map +1 -1
- package/dist/esm/ClientOnly.js +33 -0
- package/dist/esm/ClientOnly.js.map +1 -0
- package/dist/esm/Match.js +66 -39
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/ScriptOnce.js +1 -3
- package/dist/esm/ScriptOnce.js.map +1 -1
- package/dist/esm/Scripts.js +0 -9
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/Transitioner.js +20 -6
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +0 -6
- package/dist/esm/lazyRouteComponent.js +5 -24
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/esm/link.d.ts +4 -0
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/route.d.ts +6 -1
- package/dist/esm/route.js +25 -0
- package/dist/esm/route.js.map +1 -1
- package/dist/source/CatchBoundary.d.ts +4 -2
- package/dist/source/CatchBoundary.jsx +23 -22
- package/dist/source/CatchBoundary.jsx.map +1 -1
- package/dist/source/Match.jsx +90 -63
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/ScriptOnce.jsx +2 -2
- package/dist/source/ScriptOnce.jsx.map +1 -1
- package/dist/source/Scripts.jsx +0 -9
- package/dist/source/Scripts.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +23 -16
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/index.d.ts +2 -1
- package/dist/source/index.jsx +1 -0
- package/dist/source/index.jsx.map +1 -1
- package/dist/source/lazyRouteComponent.d.ts +0 -6
- package/dist/source/lazyRouteComponent.jsx +3 -23
- package/dist/source/lazyRouteComponent.jsx.map +1 -1
- package/dist/source/link.d.ts +4 -0
- package/dist/source/link.jsx.map +1 -1
- package/dist/source/route.d.ts +6 -1
- package/dist/source/route.js +13 -0
- package/dist/source/route.js.map +1 -1
- package/package.json +2 -2
- package/src/CatchBoundary.tsx +35 -32
- package/src/Match.tsx +115 -73
- package/src/ScriptOnce.tsx +1 -3
- package/src/Scripts.tsx +0 -11
- package/src/Transitioner.tsx +31 -17
- package/src/index.tsx +2 -0
- package/src/lazyRouteComponent.tsx +10 -32
- package/src/link.tsx +20 -0
- package/src/route.ts +33 -1
package/src/Scripts.tsx
CHANGED
|
@@ -4,10 +4,6 @@ import { useRouterState } from './useRouterState'
|
|
|
4
4
|
import { useRouter } from './useRouter'
|
|
5
5
|
import type { RouterManagedTag } from '@tanstack/router-core'
|
|
6
6
|
|
|
7
|
-
// Script that sets the defer flag for Vue - must run BEFORE TSR bootstrap script
|
|
8
|
-
// This prevents $_TSR.c() from removing scripts until Vue hydration is complete
|
|
9
|
-
const VUE_DEFER_SCRIPT = 'self.$_TSR_DEFER=true'
|
|
10
|
-
|
|
11
7
|
export const Scripts = Vue.defineComponent({
|
|
12
8
|
name: 'Scripts',
|
|
13
9
|
setup() {
|
|
@@ -68,12 +64,6 @@ export const Scripts = Vue.defineComponent({
|
|
|
68
64
|
const allScripts: Array<RouterManagedTag> = []
|
|
69
65
|
|
|
70
66
|
if (router.serverSsr) {
|
|
71
|
-
allScripts.push({
|
|
72
|
-
tag: 'script',
|
|
73
|
-
attrs: { nonce },
|
|
74
|
-
children: VUE_DEFER_SCRIPT,
|
|
75
|
-
} as RouterManagedTag)
|
|
76
|
-
|
|
77
67
|
const serverBufferedScript = router.serverSsr.takeBufferedScripts()
|
|
78
68
|
if (serverBufferedScript) {
|
|
79
69
|
allScripts.push(serverBufferedScript)
|
|
@@ -89,7 +79,6 @@ export const Scripts = Vue.defineComponent({
|
|
|
89
79
|
tag: 'script',
|
|
90
80
|
attrs: {
|
|
91
81
|
nonce,
|
|
92
|
-
class: '$tsr',
|
|
93
82
|
id: '$tsr-stream-barrier',
|
|
94
83
|
'data-allow-mismatch': true,
|
|
95
84
|
},
|
package/src/Transitioner.tsx
CHANGED
|
@@ -88,18 +88,23 @@ export function useTransitionerSetup() {
|
|
|
88
88
|
endTransition()
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
// Vue updates DOM asynchronously (next tick). The View Transitions API expects the
|
|
92
|
+
// update callback promise to resolve only after the DOM has been updated.
|
|
93
|
+
// Wrap the router-core implementation to await a Vue flush before resolving.
|
|
94
|
+
const originalStartViewTransition:
|
|
95
|
+
| undefined
|
|
96
|
+
| ((fn: () => Promise<void>) => void) =
|
|
97
|
+
(router as any).__tsrOriginalStartViewTransition ??
|
|
98
|
+
router.startViewTransition
|
|
99
|
+
|
|
100
|
+
;(router as any).__tsrOriginalStartViewTransition =
|
|
101
|
+
originalStartViewTransition
|
|
102
|
+
|
|
99
103
|
router.startViewTransition = (fn: () => Promise<void>) => {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
104
|
+
return originalStartViewTransition?.(async () => {
|
|
105
|
+
await fn()
|
|
106
|
+
await Vue.nextTick()
|
|
107
|
+
})
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
// Subscribe to location changes
|
|
@@ -131,6 +136,13 @@ export function useTransitionerSetup() {
|
|
|
131
136
|
|
|
132
137
|
Vue.onMounted(() => {
|
|
133
138
|
isMounted.value = true
|
|
139
|
+
if (!isAnyPending.value) {
|
|
140
|
+
router.__store.setState((s) =>
|
|
141
|
+
s.status === 'pending'
|
|
142
|
+
? { ...s, status: 'idle', resolvedLocation: s.location }
|
|
143
|
+
: s,
|
|
144
|
+
)
|
|
145
|
+
}
|
|
134
146
|
})
|
|
135
147
|
|
|
136
148
|
Vue.onUnmounted(() => {
|
|
@@ -196,6 +208,14 @@ export function useTransitionerSetup() {
|
|
|
196
208
|
Vue.watch(isAnyPending, (newValue) => {
|
|
197
209
|
if (!isMounted.value) return
|
|
198
210
|
try {
|
|
211
|
+
if (!newValue && router.__store.state.status === 'pending') {
|
|
212
|
+
router.__store.setState((s) => ({
|
|
213
|
+
...s,
|
|
214
|
+
status: 'idle',
|
|
215
|
+
resolvedLocation: s.location,
|
|
216
|
+
}))
|
|
217
|
+
}
|
|
218
|
+
|
|
199
219
|
// The router was pending and now it's not
|
|
200
220
|
if (previousIsAnyPending.value.previous && !newValue) {
|
|
201
221
|
const changeInfo = getLocationChangeInfo(router.state)
|
|
@@ -204,12 +224,6 @@ export function useTransitionerSetup() {
|
|
|
204
224
|
...changeInfo,
|
|
205
225
|
})
|
|
206
226
|
|
|
207
|
-
router.__store.setState((s) => ({
|
|
208
|
-
...s,
|
|
209
|
-
status: 'idle',
|
|
210
|
-
resolvedLocation: s.location,
|
|
211
|
-
}))
|
|
212
|
-
|
|
213
227
|
if (changeInfo.hrefChanged) {
|
|
214
228
|
handleHashScroll(router)
|
|
215
229
|
}
|
package/src/index.tsx
CHANGED
|
@@ -219,6 +219,7 @@ export type {
|
|
|
219
219
|
ActiveLinkOptions,
|
|
220
220
|
LinkProps,
|
|
221
221
|
LinkComponent,
|
|
222
|
+
LinkComponentRoute,
|
|
222
223
|
LinkComponentProps,
|
|
223
224
|
CreateLinkProps,
|
|
224
225
|
} from './link'
|
|
@@ -346,3 +347,4 @@ export type {
|
|
|
346
347
|
LocationRewrite,
|
|
347
348
|
LocationRewriteFunction,
|
|
348
349
|
} from '@tanstack/router-core'
|
|
350
|
+
export { ClientOnly } from './ClientOnly'
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Vue from 'vue'
|
|
2
2
|
import { Outlet } from './Match'
|
|
3
|
+
import { ClientOnly } from './ClientOnly'
|
|
3
4
|
import type { AsyncRouteComponent } from './route'
|
|
4
5
|
|
|
5
6
|
// If the load fails due to module not found, it may mean a new version of
|
|
@@ -19,34 +20,6 @@ function isModuleNotFoundError(error: any): boolean {
|
|
|
19
20
|
)
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
export function ClientOnly(props: { children?: any; fallback?: Vue.VNode }) {
|
|
23
|
-
const hydrated = useHydrated()
|
|
24
|
-
|
|
25
|
-
return () => {
|
|
26
|
-
if (hydrated.value) {
|
|
27
|
-
return props.children
|
|
28
|
-
}
|
|
29
|
-
return props.fallback || null
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function useHydrated() {
|
|
34
|
-
// Only hydrate on client-side, never on server
|
|
35
|
-
const hydrated = Vue.ref(false)
|
|
36
|
-
|
|
37
|
-
// If on server, return false
|
|
38
|
-
if (typeof window === 'undefined') {
|
|
39
|
-
return Vue.computed(() => false)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// On client, set to true once mounted
|
|
43
|
-
Vue.onMounted(() => {
|
|
44
|
-
hydrated.value = true
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
return hydrated
|
|
48
|
-
}
|
|
49
|
-
|
|
50
23
|
export function lazyRouteComponent<
|
|
51
24
|
T extends Record<string, any>,
|
|
52
25
|
TKey extends keyof T = 'default',
|
|
@@ -156,10 +129,15 @@ export function lazyRouteComponent<
|
|
|
156
129
|
|
|
157
130
|
// If SSR is disabled for this component
|
|
158
131
|
if (ssr?.() === false) {
|
|
159
|
-
return Vue.h(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
132
|
+
return Vue.h(
|
|
133
|
+
ClientOnly,
|
|
134
|
+
{
|
|
135
|
+
fallback: Vue.h(Outlet),
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
default: () => Vue.h(component.value, props),
|
|
139
|
+
},
|
|
140
|
+
)
|
|
163
141
|
}
|
|
164
142
|
|
|
165
143
|
// Regular render with the loaded component
|
package/src/link.tsx
CHANGED
|
@@ -630,6 +630,26 @@ export type LinkComponent<TComp> = <
|
|
|
630
630
|
props: LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
631
631
|
) => Vue.VNode
|
|
632
632
|
|
|
633
|
+
export interface LinkComponentRoute<
|
|
634
|
+
in out TDefaultFrom extends string = string,
|
|
635
|
+
> {
|
|
636
|
+
defaultFrom: TDefaultFrom
|
|
637
|
+
<
|
|
638
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
639
|
+
const TTo extends string | undefined = undefined,
|
|
640
|
+
const TMaskTo extends string = '',
|
|
641
|
+
>(
|
|
642
|
+
props: LinkComponentProps<
|
|
643
|
+
'a',
|
|
644
|
+
TRouter,
|
|
645
|
+
this['defaultFrom'],
|
|
646
|
+
TTo,
|
|
647
|
+
this['defaultFrom'],
|
|
648
|
+
TMaskTo
|
|
649
|
+
>,
|
|
650
|
+
): Vue.VNode
|
|
651
|
+
}
|
|
652
|
+
|
|
633
653
|
export function createLink<const TComp>(
|
|
634
654
|
Comp: Constrain<TComp, any, (props: CreateLinkProps) => Vue.VNode>,
|
|
635
655
|
): LinkComponent<TComp> {
|
package/src/route.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
BaseRouteApi,
|
|
5
5
|
notFound,
|
|
6
6
|
} from '@tanstack/router-core'
|
|
7
|
+
import * as Vue from 'vue'
|
|
8
|
+
import { Link } from './link'
|
|
7
9
|
import { useLoaderData } from './useLoaderData'
|
|
8
10
|
import { useLoaderDeps } from './useLoaderDeps'
|
|
9
11
|
import { useParams } from './useParams'
|
|
@@ -42,8 +44,8 @@ import type { UseMatchRoute } from './useMatch'
|
|
|
42
44
|
import type { UseLoaderDepsRoute } from './useLoaderDeps'
|
|
43
45
|
import type { UseParamsRoute } from './useParams'
|
|
44
46
|
import type { UseSearchRoute } from './useSearch'
|
|
45
|
-
import type * as Vue from 'vue'
|
|
46
47
|
import type { UseRouteContextRoute } from './useRouteContext'
|
|
48
|
+
import type { LinkComponentRoute } from './link'
|
|
47
49
|
|
|
48
50
|
// Structural type for Vue SFC components (.vue files)
|
|
49
51
|
// Uses structural matching to accept Vue components without breaking
|
|
@@ -73,6 +75,7 @@ declare module '@tanstack/router-core' {
|
|
|
73
75
|
useLoaderDeps: UseLoaderDepsRoute<TId>
|
|
74
76
|
useLoaderData: UseLoaderDataRoute<TId>
|
|
75
77
|
useNavigate: () => UseNavigateResult<TFullPath>
|
|
78
|
+
Link: LinkComponentRoute<TFullPath>
|
|
76
79
|
}
|
|
77
80
|
}
|
|
78
81
|
|
|
@@ -140,6 +143,19 @@ export class RouteApi<
|
|
|
140
143
|
notFound = (opts?: NotFoundError) => {
|
|
141
144
|
return notFound({ routeId: this.id as string, ...opts })
|
|
142
145
|
}
|
|
146
|
+
|
|
147
|
+
Link: LinkComponentRoute<RouteTypesById<TRouter, TId>['fullPath']> = ((
|
|
148
|
+
props,
|
|
149
|
+
ctx?: Vue.SetupContext,
|
|
150
|
+
) => {
|
|
151
|
+
const router = useRouter()
|
|
152
|
+
const fullPath = router.routesById[this.id as string].fullPath
|
|
153
|
+
return Vue.h(
|
|
154
|
+
Link as any,
|
|
155
|
+
{ from: fullPath as never, ...(props as any) },
|
|
156
|
+
ctx?.slots,
|
|
157
|
+
)
|
|
158
|
+
}) as LinkComponentRoute<RouteTypesById<TRouter, TId>['fullPath']>
|
|
143
159
|
}
|
|
144
160
|
|
|
145
161
|
export class Route<
|
|
@@ -277,6 +293,14 @@ export class Route<
|
|
|
277
293
|
useNavigate = (): UseNavigateResult<TFullPath> => {
|
|
278
294
|
return useNavigate({ from: this.fullPath })
|
|
279
295
|
}
|
|
296
|
+
|
|
297
|
+
Link: LinkComponentRoute<TFullPath> = ((props, ctx?: Vue.SetupContext) => {
|
|
298
|
+
return Vue.h(
|
|
299
|
+
Link as any,
|
|
300
|
+
{ from: this.fullPath as never, ...(props as any) },
|
|
301
|
+
ctx?.slots,
|
|
302
|
+
)
|
|
303
|
+
}) as LinkComponentRoute<TFullPath>
|
|
280
304
|
}
|
|
281
305
|
|
|
282
306
|
export function createRoute<
|
|
@@ -515,6 +539,14 @@ export class RootRoute<
|
|
|
515
539
|
useNavigate = (): UseNavigateResult<'/'> => {
|
|
516
540
|
return useNavigate({ from: this.fullPath })
|
|
517
541
|
}
|
|
542
|
+
|
|
543
|
+
Link: LinkComponentRoute<'/'> = ((props, ctx?: Vue.SetupContext) => {
|
|
544
|
+
return Vue.h(
|
|
545
|
+
Link as any,
|
|
546
|
+
{ from: this.fullPath as never, ...(props as any) },
|
|
547
|
+
ctx?.slots,
|
|
548
|
+
)
|
|
549
|
+
}) as LinkComponentRoute<'/'>
|
|
518
550
|
}
|
|
519
551
|
|
|
520
552
|
export function createRouteMask<
|