@tanstack/vue-router 1.167.5 → 1.168.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/Match.js +55 -61
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.js +8 -15
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/Scripts.js +7 -6
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/Transitioner.js +18 -24
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/headContentUtils.js +13 -15
- package/dist/esm/headContentUtils.js.map +1 -1
- package/dist/esm/index.dev.js +6 -6
- package/dist/esm/index.js +6 -6
- package/dist/esm/link.js +242 -178
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/matchContext.d.ts +8 -14
- package/dist/esm/matchContext.js +11 -9
- package/dist/esm/matchContext.js.map +1 -1
- package/dist/esm/not-found.js +6 -3
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/router.js +2 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerStores.d.ts +13 -0
- package/dist/esm/routerStores.js +33 -0
- package/dist/esm/routerStores.js.map +1 -0
- package/dist/esm/ssr/RouterClient.js +1 -1
- package/dist/esm/ssr/RouterClient.js.map +1 -1
- package/dist/esm/ssr/renderRouterToStream.js +2 -2
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -1
- package/dist/esm/ssr/renderRouterToString.js +1 -1
- package/dist/esm/ssr/renderRouterToString.js.map +1 -1
- package/dist/esm/useCanGoBack.d.ts +1 -1
- package/dist/esm/useCanGoBack.js +3 -2
- package/dist/esm/useCanGoBack.js.map +1 -1
- package/dist/esm/useLocation.js +3 -2
- package/dist/esm/useLocation.js.map +1 -1
- package/dist/esm/useMatch.js +29 -19
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/useRouterState.js +4 -4
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/source/Match.jsx +121 -159
- package/dist/source/Match.jsx.map +1 -1
- package/dist/source/Matches.jsx +11 -28
- package/dist/source/Matches.jsx.map +1 -1
- package/dist/source/Scripts.jsx +32 -35
- package/dist/source/Scripts.jsx.map +1 -1
- package/dist/source/Transitioner.jsx +19 -21
- package/dist/source/Transitioner.jsx.map +1 -1
- package/dist/source/headContentUtils.jsx +51 -61
- package/dist/source/headContentUtils.jsx.map +1 -1
- package/dist/source/link.jsx +298 -249
- package/dist/source/link.jsx.map +1 -1
- package/dist/source/matchContext.d.ts +8 -14
- package/dist/source/matchContext.jsx +17 -23
- package/dist/source/matchContext.jsx.map +1 -1
- package/dist/source/not-found.jsx +6 -5
- package/dist/source/not-found.jsx.map +1 -1
- package/dist/source/router.js +2 -1
- package/dist/source/router.js.map +1 -1
- package/dist/source/routerStores.d.ts +13 -0
- package/dist/source/routerStores.js +37 -0
- package/dist/source/routerStores.js.map +1 -0
- package/dist/source/ssr/RouterClient.jsx +1 -1
- package/dist/source/ssr/RouterClient.jsx.map +1 -1
- package/dist/source/ssr/renderRouterToStream.jsx +2 -2
- package/dist/source/ssr/renderRouterToStream.jsx.map +1 -1
- package/dist/source/ssr/renderRouterToString.jsx +1 -1
- package/dist/source/ssr/renderRouterToString.jsx.map +1 -1
- package/dist/source/useCanGoBack.d.ts +1 -1
- package/dist/source/useCanGoBack.js +4 -2
- package/dist/source/useCanGoBack.js.map +1 -1
- package/dist/source/useLocation.jsx +4 -4
- package/dist/source/useLocation.jsx.map +1 -1
- package/dist/source/useMatch.jsx +60 -38
- package/dist/source/useMatch.jsx.map +1 -1
- package/dist/source/useRouterState.jsx +4 -4
- package/dist/source/useRouterState.jsx.map +1 -1
- package/package.json +4 -4
- package/skills/vue-router/SKILL.md +3 -0
- package/src/Match.tsx +168 -180
- package/src/Matches.tsx +18 -31
- package/src/Scripts.tsx +40 -40
- package/src/Transitioner.tsx +35 -23
- package/src/headContentUtils.tsx +101 -107
- package/src/link.tsx +445 -300
- package/src/matchContext.tsx +23 -25
- package/src/not-found.tsx +9 -5
- package/src/router.ts +2 -1
- package/src/routerStores.ts +54 -0
- package/src/ssr/RouterClient.tsx +1 -1
- package/src/ssr/renderRouterToStream.tsx +2 -2
- package/src/ssr/renderRouterToString.tsx +1 -1
- package/src/useCanGoBack.ts +7 -2
- package/src/useLocation.tsx +8 -5
- package/src/useMatch.tsx +95 -49
- package/src/useRouterState.tsx +6 -4
package/src/Transitioner.tsx
CHANGED
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
trimPathRight,
|
|
6
6
|
} from '@tanstack/router-core'
|
|
7
7
|
import { isServer } from '@tanstack/router-core/isServer'
|
|
8
|
+
import { batch, useStore } from '@tanstack/vue-store'
|
|
8
9
|
import { useRouter } from './useRouter'
|
|
9
|
-
import { useRouterState } from './useRouterState'
|
|
10
10
|
import { usePrevious } from './utils'
|
|
11
11
|
|
|
12
12
|
// Track mount state per router to avoid double-loading
|
|
@@ -30,17 +30,16 @@ export function useTransitionerSetup() {
|
|
|
30
30
|
return
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const isLoading =
|
|
34
|
-
select: ({ isLoading }) => isLoading,
|
|
35
|
-
})
|
|
33
|
+
const isLoading = useStore(router.stores.isLoading, (value) => value)
|
|
36
34
|
|
|
37
35
|
// Track if we're in a transition - using a ref to track async transitions
|
|
38
36
|
const isTransitioning = Vue.ref(false)
|
|
39
37
|
|
|
40
38
|
// Track pending state changes
|
|
41
|
-
const hasPendingMatches =
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
const hasPendingMatches = useStore(
|
|
40
|
+
router.stores.hasPendingMatches,
|
|
41
|
+
(value) => value,
|
|
42
|
+
)
|
|
44
43
|
|
|
45
44
|
const previousIsLoading = usePrevious(() => isLoading.value)
|
|
46
45
|
|
|
@@ -61,7 +60,7 @@ export function useTransitionerSetup() {
|
|
|
61
60
|
isTransitioning.value = true
|
|
62
61
|
// Also update the router state so useMatch knows we're transitioning
|
|
63
62
|
try {
|
|
64
|
-
router.
|
|
63
|
+
router.stores.isTransitioning.setState(() => true)
|
|
65
64
|
} catch {
|
|
66
65
|
// Ignore errors if component is unmounted
|
|
67
66
|
}
|
|
@@ -72,7 +71,7 @@ export function useTransitionerSetup() {
|
|
|
72
71
|
Vue.nextTick(() => {
|
|
73
72
|
try {
|
|
74
73
|
isTransitioning.value = false
|
|
75
|
-
router.
|
|
74
|
+
router.stores.isTransitioning.setState(() => false)
|
|
76
75
|
} catch {
|
|
77
76
|
// Ignore errors if component is unmounted
|
|
78
77
|
}
|
|
@@ -141,11 +140,14 @@ export function useTransitionerSetup() {
|
|
|
141
140
|
Vue.onMounted(() => {
|
|
142
141
|
isMounted.value = true
|
|
143
142
|
if (!isAnyPending.value) {
|
|
144
|
-
router.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
if (router.stores.status.state === 'pending') {
|
|
144
|
+
batch(() => {
|
|
145
|
+
router.stores.status.setState(() => 'idle')
|
|
146
|
+
router.stores.resolvedLocation.setState(
|
|
147
|
+
() => router.stores.location.state,
|
|
148
|
+
)
|
|
149
|
+
})
|
|
150
|
+
}
|
|
149
151
|
}
|
|
150
152
|
})
|
|
151
153
|
|
|
@@ -185,7 +187,10 @@ export function useTransitionerSetup() {
|
|
|
185
187
|
if (previousIsLoading.value.previous && !newValue) {
|
|
186
188
|
router.emit({
|
|
187
189
|
type: 'onLoad',
|
|
188
|
-
...getLocationChangeInfo(
|
|
190
|
+
...getLocationChangeInfo(
|
|
191
|
+
router.stores.location.state,
|
|
192
|
+
router.stores.resolvedLocation.state,
|
|
193
|
+
),
|
|
189
194
|
})
|
|
190
195
|
}
|
|
191
196
|
} catch {
|
|
@@ -201,7 +206,10 @@ export function useTransitionerSetup() {
|
|
|
201
206
|
if (previousIsPagePending.value.previous && !newValue) {
|
|
202
207
|
router.emit({
|
|
203
208
|
type: 'onBeforeRouteMount',
|
|
204
|
-
...getLocationChangeInfo(
|
|
209
|
+
...getLocationChangeInfo(
|
|
210
|
+
router.stores.location.state,
|
|
211
|
+
router.stores.resolvedLocation.state,
|
|
212
|
+
),
|
|
205
213
|
})
|
|
206
214
|
}
|
|
207
215
|
} catch {
|
|
@@ -212,17 +220,21 @@ export function useTransitionerSetup() {
|
|
|
212
220
|
Vue.watch(isAnyPending, (newValue) => {
|
|
213
221
|
if (!isMounted.value) return
|
|
214
222
|
try {
|
|
215
|
-
if (!newValue && router.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
if (!newValue && router.stores.status.state === 'pending') {
|
|
224
|
+
batch(() => {
|
|
225
|
+
router.stores.status.setState(() => 'idle')
|
|
226
|
+
router.stores.resolvedLocation.setState(
|
|
227
|
+
() => router.stores.location.state,
|
|
228
|
+
)
|
|
229
|
+
})
|
|
221
230
|
}
|
|
222
231
|
|
|
223
232
|
// The router was pending and now it's not
|
|
224
233
|
if (previousIsAnyPending.value.previous && !newValue) {
|
|
225
|
-
const changeInfo = getLocationChangeInfo(
|
|
234
|
+
const changeInfo = getLocationChangeInfo(
|
|
235
|
+
router.stores.location.state,
|
|
236
|
+
router.stores.resolvedLocation.state,
|
|
237
|
+
)
|
|
226
238
|
router.emit({
|
|
227
239
|
type: 'onResolved',
|
|
228
240
|
...changeInfo,
|
package/src/headContentUtils.tsx
CHANGED
|
@@ -1,68 +1,67 @@
|
|
|
1
1
|
import * as Vue from 'vue'
|
|
2
|
-
|
|
3
2
|
import { escapeHtml } from '@tanstack/router-core'
|
|
3
|
+
import { useStore } from '@tanstack/vue-store'
|
|
4
4
|
import { useRouter } from './useRouter'
|
|
5
|
-
import { useRouterState } from './useRouterState'
|
|
6
5
|
import type { RouterManagedTag } from '@tanstack/router-core'
|
|
7
6
|
|
|
8
7
|
export const useTags = () => {
|
|
9
8
|
const router = useRouter()
|
|
9
|
+
const matches = useStore(
|
|
10
|
+
router.stores.activeMatchesSnapshot,
|
|
11
|
+
(value) => value,
|
|
12
|
+
)
|
|
10
13
|
|
|
11
|
-
const
|
|
12
|
-
select: (state) => {
|
|
13
|
-
return state.matches.map((match) => match.meta!).filter(Boolean)
|
|
14
|
-
},
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const meta: Vue.Ref<Array<RouterManagedTag>> = Vue.computed(() => {
|
|
14
|
+
const meta = Vue.computed<Array<RouterManagedTag>>(() => {
|
|
18
15
|
const resultMeta: Array<RouterManagedTag> = []
|
|
19
16
|
const metaByAttribute: Record<string, true> = {}
|
|
20
17
|
let title: RouterManagedTag | undefined
|
|
21
|
-
;[...
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
;[...matches.value.map((match) => match.meta!).filter(Boolean)]
|
|
19
|
+
.reverse()
|
|
20
|
+
.forEach((metas) => {
|
|
21
|
+
;[...metas].reverse().forEach((m) => {
|
|
22
|
+
if (!m) return
|
|
23
|
+
|
|
24
|
+
if (m.title) {
|
|
25
|
+
if (!title) {
|
|
26
|
+
title = {
|
|
27
|
+
tag: 'title',
|
|
28
|
+
children: m.title,
|
|
29
|
+
}
|
|
30
30
|
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
} else if ('script:ld+json' in m) {
|
|
32
|
+
// Handle JSON-LD structured data
|
|
33
|
+
// Content is HTML-escaped to prevent XSS when injected via innerHTML
|
|
34
|
+
try {
|
|
35
|
+
const json = JSON.stringify(m['script:ld+json'])
|
|
36
|
+
resultMeta.push({
|
|
37
|
+
tag: 'script',
|
|
38
|
+
attrs: {
|
|
39
|
+
type: 'application/ld+json',
|
|
40
|
+
},
|
|
41
|
+
children: escapeHtml(json),
|
|
42
|
+
})
|
|
43
|
+
} catch {
|
|
44
|
+
// Skip invalid JSON-LD objects
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
const attribute = m.name ?? m.property
|
|
48
|
+
if (attribute) {
|
|
49
|
+
if (metaByAttribute[attribute]) {
|
|
50
|
+
return
|
|
51
|
+
} else {
|
|
52
|
+
metaByAttribute[attribute] = true
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
resultMeta.push({
|
|
38
|
-
tag: '
|
|
57
|
+
tag: 'meta',
|
|
39
58
|
attrs: {
|
|
40
|
-
|
|
59
|
+
...m,
|
|
41
60
|
},
|
|
42
|
-
children: escapeHtml(json),
|
|
43
61
|
})
|
|
44
|
-
} catch {
|
|
45
|
-
// Skip invalid JSON-LD objects
|
|
46
|
-
}
|
|
47
|
-
} else {
|
|
48
|
-
const attribute = m.name ?? m.property
|
|
49
|
-
if (attribute) {
|
|
50
|
-
if (metaByAttribute[attribute]) {
|
|
51
|
-
return
|
|
52
|
-
} else {
|
|
53
|
-
metaByAttribute[attribute] = true
|
|
54
|
-
}
|
|
55
62
|
}
|
|
56
|
-
|
|
57
|
-
resultMeta.push({
|
|
58
|
-
tag: 'meta',
|
|
59
|
-
attrs: {
|
|
60
|
-
...m,
|
|
61
|
-
},
|
|
62
|
-
})
|
|
63
|
-
}
|
|
63
|
+
})
|
|
64
64
|
})
|
|
65
|
-
})
|
|
66
65
|
|
|
67
66
|
if (title) {
|
|
68
67
|
resultMeta.push(title)
|
|
@@ -73,9 +72,9 @@ export const useTags = () => {
|
|
|
73
72
|
return resultMeta
|
|
74
73
|
})
|
|
75
74
|
|
|
76
|
-
const links =
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const links = Vue.computed<Array<RouterManagedTag>>(
|
|
76
|
+
() =>
|
|
77
|
+
matches.value
|
|
79
78
|
.map((match) => match.links!)
|
|
80
79
|
.filter(Boolean)
|
|
81
80
|
.flat(1)
|
|
@@ -85,67 +84,62 @@ export const useTags = () => {
|
|
|
85
84
|
...link,
|
|
86
85
|
},
|
|
87
86
|
})) as Array<RouterManagedTag>,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const preloadMeta =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
|
|
110
|
-
return preloadMeta
|
|
111
|
-
},
|
|
112
|
-
})
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
const preloadMeta = Vue.computed<Array<RouterManagedTag>>(() => {
|
|
90
|
+
const preloadMeta: Array<RouterManagedTag> = []
|
|
91
|
+
|
|
92
|
+
matches.value
|
|
93
|
+
.map((match) => router.looseRoutesById[match.routeId]!)
|
|
94
|
+
.forEach((route) =>
|
|
95
|
+
router.ssr?.manifest?.routes[route.id]?.preloads
|
|
96
|
+
?.filter(Boolean)
|
|
97
|
+
.forEach((preload) => {
|
|
98
|
+
preloadMeta.push({
|
|
99
|
+
tag: 'link',
|
|
100
|
+
attrs: {
|
|
101
|
+
rel: 'modulepreload',
|
|
102
|
+
href: preload,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}),
|
|
106
|
+
)
|
|
113
107
|
|
|
114
|
-
|
|
115
|
-
select: (state) =>
|
|
116
|
-
(
|
|
117
|
-
state.matches
|
|
118
|
-
.map((match) => match.headScripts!)
|
|
119
|
-
.flat(1)
|
|
120
|
-
.filter(Boolean) as Array<RouterManagedTag>
|
|
121
|
-
).map(({ children, ...script }) => ({
|
|
122
|
-
tag: 'script',
|
|
123
|
-
attrs: {
|
|
124
|
-
...script,
|
|
125
|
-
},
|
|
126
|
-
children,
|
|
127
|
-
})),
|
|
108
|
+
return preloadMeta
|
|
128
109
|
})
|
|
129
110
|
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const assets = state.matches
|
|
135
|
-
.map((match) => manifest?.routes[match.routeId]?.assets ?? [])
|
|
136
|
-
.filter(Boolean)
|
|
111
|
+
const headScripts = Vue.computed<Array<RouterManagedTag>>(() =>
|
|
112
|
+
(
|
|
113
|
+
matches.value
|
|
114
|
+
.map((match) => match.headScripts!)
|
|
137
115
|
.flat(1)
|
|
138
|
-
.filter(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
116
|
+
.filter(Boolean) as Array<RouterManagedTag>
|
|
117
|
+
).map(({ children, ...script }) => ({
|
|
118
|
+
tag: 'script',
|
|
119
|
+
attrs: {
|
|
120
|
+
...script,
|
|
121
|
+
},
|
|
122
|
+
children,
|
|
123
|
+
})),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const manifestAssets = Vue.computed<Array<RouterManagedTag>>(() => {
|
|
127
|
+
const manifest = router.ssr?.manifest
|
|
128
|
+
|
|
129
|
+
const assets = matches.value
|
|
130
|
+
.map((match) => manifest?.routes[match.routeId]?.assets ?? [])
|
|
131
|
+
.filter(Boolean)
|
|
132
|
+
.flat(1)
|
|
133
|
+
.filter((asset) => asset.tag === 'link')
|
|
134
|
+
.map(
|
|
135
|
+
(asset) =>
|
|
136
|
+
({
|
|
137
|
+
tag: 'link',
|
|
138
|
+
attrs: { ...asset.attrs },
|
|
139
|
+
}) satisfies RouterManagedTag,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return assets
|
|
149
143
|
})
|
|
150
144
|
|
|
151
145
|
return () =>
|