@tanstack/vue-router 1.140.5 → 1.141.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 (150) hide show
  1. package/dist/esm/Asset.js +122 -8
  2. package/dist/esm/Asset.js.map +1 -1
  3. package/dist/esm/Body.d.ts +4 -0
  4. package/dist/esm/Body.js +26 -0
  5. package/dist/esm/Body.js.map +1 -0
  6. package/dist/esm/CatchBoundary.d.ts +1 -1
  7. package/dist/esm/CatchBoundary.js +8 -8
  8. package/dist/esm/CatchBoundary.js.map +1 -1
  9. package/dist/esm/Html.d.ts +4 -0
  10. package/dist/esm/Html.js +63 -0
  11. package/dist/esm/Html.js.map +1 -0
  12. package/dist/esm/Match.js +87 -49
  13. package/dist/esm/Match.js.map +1 -1
  14. package/dist/esm/Matches.js +3 -2
  15. package/dist/esm/Matches.js.map +1 -1
  16. package/dist/esm/RouterProvider.js +3 -0
  17. package/dist/esm/RouterProvider.js.map +1 -1
  18. package/dist/esm/ScriptOnce.d.ts +12 -5
  19. package/dist/esm/ScriptOnce.js +35 -15
  20. package/dist/esm/ScriptOnce.js.map +1 -1
  21. package/dist/esm/Scripts.d.ts +2 -1
  22. package/dist/esm/Scripts.js +101 -35
  23. package/dist/esm/Scripts.js.map +1 -1
  24. package/dist/esm/Transitioner.d.ts +16 -0
  25. package/dist/esm/Transitioner.js +136 -133
  26. package/dist/esm/Transitioner.js.map +1 -1
  27. package/dist/esm/awaited.d.ts +20 -5
  28. package/dist/esm/awaited.js +17 -20
  29. package/dist/esm/awaited.js.map +1 -1
  30. package/dist/esm/index.d.ts +2 -0
  31. package/dist/esm/index.js +4 -0
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/lazyRouteComponent.js +2 -2
  34. package/dist/esm/lazyRouteComponent.js.map +1 -1
  35. package/dist/esm/link.js +27 -35
  36. package/dist/esm/link.js.map +1 -1
  37. package/dist/esm/scroll-restoration.d.ts +8 -1
  38. package/dist/esm/scroll-restoration.js +44 -12
  39. package/dist/esm/scroll-restoration.js.map +1 -1
  40. package/dist/esm/ssr/RouterClient.d.ts +15 -0
  41. package/dist/esm/ssr/RouterClient.js +46 -0
  42. package/dist/esm/ssr/RouterClient.js.map +1 -0
  43. package/dist/esm/ssr/RouterServer.d.ts +15 -0
  44. package/dist/esm/ssr/RouterServer.js +37 -0
  45. package/dist/esm/ssr/RouterServer.js.map +1 -0
  46. package/dist/esm/ssr/client.d.ts +1 -0
  47. package/dist/esm/ssr/client.js +5 -0
  48. package/dist/esm/ssr/client.js.map +1 -0
  49. package/dist/esm/ssr/defaultRenderHandler.d.ts +1 -0
  50. package/dist/esm/ssr/defaultRenderHandler.js +15 -0
  51. package/dist/esm/ssr/defaultRenderHandler.js.map +1 -0
  52. package/dist/esm/ssr/defaultStreamHandler.d.ts +1 -0
  53. package/dist/esm/ssr/defaultStreamHandler.js +17 -0
  54. package/dist/esm/ssr/defaultStreamHandler.js.map +1 -0
  55. package/dist/esm/ssr/renderRouterToStream.d.ts +8 -0
  56. package/dist/esm/ssr/renderRouterToStream.js +70 -0
  57. package/dist/esm/ssr/renderRouterToStream.js.map +1 -0
  58. package/dist/esm/ssr/renderRouterToString.d.ts +7 -0
  59. package/dist/esm/ssr/renderRouterToString.js +33 -0
  60. package/dist/esm/ssr/renderRouterToString.js.map +1 -0
  61. package/dist/esm/ssr/server.d.ts +6 -0
  62. package/dist/esm/ssr/server.js +14 -0
  63. package/dist/esm/ssr/server.js.map +1 -0
  64. package/dist/source/Asset.jsx +119 -7
  65. package/dist/source/Asset.jsx.map +1 -1
  66. package/dist/source/Body.d.ts +4 -0
  67. package/dist/source/Body.jsx +15 -0
  68. package/dist/source/Body.jsx.map +1 -0
  69. package/dist/source/CatchBoundary.d.ts +1 -1
  70. package/dist/source/CatchBoundary.jsx +10 -23
  71. package/dist/source/CatchBoundary.jsx.map +1 -1
  72. package/dist/source/Html.d.ts +4 -0
  73. package/dist/source/Html.jsx +56 -0
  74. package/dist/source/Html.jsx.map +1 -0
  75. package/dist/source/Match.jsx +119 -54
  76. package/dist/source/Match.jsx.map +1 -1
  77. package/dist/source/Matches.jsx +15 -3
  78. package/dist/source/Matches.jsx.map +1 -1
  79. package/dist/source/RouterProvider.jsx +5 -0
  80. package/dist/source/RouterProvider.jsx.map +1 -1
  81. package/dist/source/ScriptOnce.d.ts +12 -5
  82. package/dist/source/ScriptOnce.jsx +27 -16
  83. package/dist/source/ScriptOnce.jsx.map +1 -1
  84. package/dist/source/Scripts.d.ts +2 -1
  85. package/dist/source/Scripts.jsx +100 -42
  86. package/dist/source/Scripts.jsx.map +1 -1
  87. package/dist/source/Transitioner.d.ts +16 -0
  88. package/dist/source/Transitioner.jsx +180 -160
  89. package/dist/source/Transitioner.jsx.map +1 -1
  90. package/dist/source/awaited.d.ts +20 -5
  91. package/dist/source/awaited.jsx +18 -25
  92. package/dist/source/awaited.jsx.map +1 -1
  93. package/dist/source/index.d.ts +2 -0
  94. package/dist/source/index.jsx +2 -0
  95. package/dist/source/index.jsx.map +1 -1
  96. package/dist/source/lazyRouteComponent.jsx +4 -2
  97. package/dist/source/lazyRouteComponent.jsx.map +1 -1
  98. package/dist/source/link.jsx +37 -51
  99. package/dist/source/link.jsx.map +1 -1
  100. package/dist/source/scroll-restoration.d.ts +8 -1
  101. package/dist/source/scroll-restoration.jsx +55 -12
  102. package/dist/source/scroll-restoration.jsx.map +1 -1
  103. package/dist/source/ssr/RouterClient.d.ts +15 -0
  104. package/dist/source/ssr/RouterClient.jsx +48 -0
  105. package/dist/source/ssr/RouterClient.jsx.map +1 -0
  106. package/dist/source/ssr/RouterServer.d.ts +15 -0
  107. package/dist/source/ssr/RouterServer.jsx +40 -0
  108. package/dist/source/ssr/RouterServer.jsx.map +1 -0
  109. package/dist/source/ssr/client.d.ts +1 -0
  110. package/dist/source/ssr/client.js +2 -0
  111. package/dist/source/ssr/client.js.map +1 -0
  112. package/dist/source/ssr/defaultRenderHandler.d.ts +1 -0
  113. package/dist/source/ssr/defaultRenderHandler.jsx +9 -0
  114. package/dist/source/ssr/defaultRenderHandler.jsx.map +1 -0
  115. package/dist/source/ssr/defaultStreamHandler.d.ts +1 -0
  116. package/dist/source/ssr/defaultStreamHandler.jsx +10 -0
  117. package/dist/source/ssr/defaultStreamHandler.jsx.map +1 -0
  118. package/dist/source/ssr/renderRouterToStream.d.ts +8 -0
  119. package/dist/source/ssr/renderRouterToStream.jsx +55 -0
  120. package/dist/source/ssr/renderRouterToStream.jsx.map +1 -0
  121. package/dist/source/ssr/renderRouterToString.d.ts +7 -0
  122. package/dist/source/ssr/renderRouterToString.jsx +26 -0
  123. package/dist/source/ssr/renderRouterToString.jsx.map +1 -0
  124. package/dist/source/ssr/server.d.ts +6 -0
  125. package/dist/source/ssr/server.js +7 -0
  126. package/dist/source/ssr/server.js.map +1 -0
  127. package/package.json +16 -3
  128. package/src/Asset.tsx +157 -7
  129. package/src/Body.tsx +26 -0
  130. package/src/CatchBoundary.tsx +11 -25
  131. package/src/Html.tsx +65 -0
  132. package/src/Match.tsx +135 -58
  133. package/src/Matches.tsx +16 -4
  134. package/src/RouterProvider.tsx +6 -0
  135. package/src/ScriptOnce.tsx +43 -28
  136. package/src/Scripts.tsx +121 -56
  137. package/src/Transitioner.tsx +197 -176
  138. package/src/awaited.tsx +17 -28
  139. package/src/index.tsx +2 -0
  140. package/src/lazyRouteComponent.tsx +4 -2
  141. package/src/link.tsx +42 -47
  142. package/src/scroll-restoration.tsx +69 -21
  143. package/src/ssr/RouterClient.tsx +58 -0
  144. package/src/ssr/RouterServer.tsx +51 -0
  145. package/src/ssr/client.ts +1 -0
  146. package/src/ssr/defaultRenderHandler.tsx +12 -0
  147. package/src/ssr/defaultStreamHandler.tsx +13 -0
  148. package/src/ssr/renderRouterToStream.tsx +85 -0
  149. package/src/ssr/renderRouterToString.tsx +37 -0
  150. package/src/ssr/server.ts +6 -0
@@ -1,3 +1,4 @@
1
+ import * as Vue from 'vue'
1
2
  import {
2
3
  defaultGetScrollRestorationKey,
3
4
  restoreScroll,
@@ -6,24 +7,71 @@ import {
6
7
  import { useRouter } from './useRouter'
7
8
  import { ScriptOnce } from './ScriptOnce'
8
9
 
9
- export function ScrollRestoration() {
10
- const router = useRouter()
11
- const getKey =
12
- router.options.getScrollRestorationKey || defaultGetScrollRestorationKey
13
- const userKey = getKey(router.latestLocation)
14
- const resolvedKey =
15
- userKey !== defaultGetScrollRestorationKey(router.latestLocation)
16
- ? userKey
17
- : null
18
-
19
- if (!router.isScrollRestoring || !router.isServer) {
20
- return null
21
- }
22
-
23
- return (
24
- <ScriptOnce
25
- children={`(${restoreScroll.toString()})(${JSON.stringify(storageKey)},${JSON.stringify(resolvedKey)}, undefined, true)`}
26
- log={false}
27
- />
28
- )
29
- }
10
+ /**
11
+ * ScrollRestoration component for Vue.
12
+ * On server: renders a ScriptOnce with scroll restoration logic.
13
+ * On client during hydration: renders a matching ScriptOnce to avoid mismatch.
14
+ * After mount: renders nothing.
15
+ */
16
+ export const ScrollRestoration = Vue.defineComponent({
17
+ name: 'ScrollRestoration',
18
+ setup() {
19
+ const router = useRouter()
20
+
21
+ // Track mounted state for hydration handling
22
+ const mounted = Vue.ref(false)
23
+ Vue.onMounted(() => {
24
+ mounted.value = true
25
+ })
26
+
27
+ return () => {
28
+ // After mount, render nothing
29
+ if (mounted.value) {
30
+ return null
31
+ }
32
+
33
+ // Check if scroll restoration is enabled
34
+ if (!router.isScrollRestoring) {
35
+ return null
36
+ }
37
+
38
+ // Check custom scroll restoration function
39
+ if (typeof router.options.scrollRestoration === 'function') {
40
+ const shouldRestore = router.options.scrollRestoration({
41
+ location: router.latestLocation,
42
+ })
43
+ if (!shouldRestore) {
44
+ return null
45
+ }
46
+ }
47
+
48
+ const getKey =
49
+ router.options.getScrollRestorationKey || defaultGetScrollRestorationKey
50
+ const userKey = getKey(router.latestLocation)
51
+ const resolvedKey =
52
+ userKey !== defaultGetScrollRestorationKey(router.latestLocation)
53
+ ? userKey
54
+ : undefined
55
+
56
+ const restoreScrollOptions: Parameters<typeof restoreScroll>[0] = {
57
+ storageKey,
58
+ shouldScrollRestoration: true,
59
+ }
60
+ if (resolvedKey) {
61
+ restoreScrollOptions.key = resolvedKey
62
+ }
63
+
64
+ // Server-side: render the actual scroll restoration script
65
+ if (router.isServer) {
66
+ return (
67
+ <ScriptOnce
68
+ children={`(${restoreScroll.toString()})(${JSON.stringify(restoreScrollOptions)})`}
69
+ />
70
+ )
71
+ }
72
+
73
+ // Client-side during hydration: render empty ScriptOnce to match server structure
74
+ return <ScriptOnce children="" />
75
+ }
76
+ },
77
+ })
@@ -0,0 +1,58 @@
1
+ import * as Vue from 'vue'
2
+ import { hydrate } from '@tanstack/router-core/ssr/client'
3
+ import { HeadContent } from '../HeadContent'
4
+ import { RouterProvider } from '../RouterProvider'
5
+ import type { AnyRouter } from '@tanstack/router-core'
6
+
7
+ let hydrationPromise: Promise<void | Array<Array<void>>> | undefined
8
+
9
+ export const RouterClient = Vue.defineComponent({
10
+ name: 'RouterClient',
11
+ props: {
12
+ router: {
13
+ type: Object as () => AnyRouter,
14
+ required: true,
15
+ },
16
+ },
17
+ setup(props) {
18
+ const isHydrated = Vue.ref(false)
19
+
20
+ if (!hydrationPromise) {
21
+ if (!props.router.state.matches.length) {
22
+ hydrationPromise = hydrate(props.router)
23
+ } else {
24
+ hydrationPromise = Promise.resolve()
25
+ }
26
+ }
27
+
28
+ Vue.onMounted(() => {
29
+ hydrationPromise!.then(() => {
30
+ isHydrated.value = true
31
+ })
32
+ })
33
+
34
+ // For SSR, we're already hydrated
35
+ if (typeof window === 'undefined') {
36
+ isHydrated.value = true
37
+ }
38
+
39
+ return () => {
40
+ if (!isHydrated.value) {
41
+ return null
42
+ }
43
+
44
+ return Vue.h(
45
+ RouterProvider,
46
+ {
47
+ router: props.router,
48
+ },
49
+ {
50
+ innerWrap: (innerProps: { children: any }) => [
51
+ Vue.h(HeadContent),
52
+ innerProps.children,
53
+ ],
54
+ },
55
+ )
56
+ }
57
+ },
58
+ })
@@ -0,0 +1,51 @@
1
+ import * as Vue from 'vue'
2
+ import { Asset } from '../Asset'
3
+ import { useTags } from '../HeadContent'
4
+ import { RouterProvider } from '../RouterProvider'
5
+ import { Scripts } from '../Scripts'
6
+ import type { AnyRouter, RouterManagedTag } from '@tanstack/router-core'
7
+
8
+ const ServerHeadContent = Vue.defineComponent({
9
+ name: 'ServerHeadContent',
10
+ setup() {
11
+ const getTags = useTags()
12
+
13
+ return () =>
14
+ getTags().map((tag: RouterManagedTag) =>
15
+ Vue.h(Asset, { key: tag.tag + (tag as any).id, ...tag }),
16
+ )
17
+ },
18
+ })
19
+
20
+ export const RouterServer = Vue.defineComponent({
21
+ name: 'RouterServer',
22
+ props: {
23
+ router: {
24
+ type: Object as () => AnyRouter,
25
+ required: true,
26
+ },
27
+ },
28
+ setup(props) {
29
+ return () =>
30
+ Vue.h('html', null, [
31
+ Vue.h('head', null, [Vue.h(ServerHeadContent)]),
32
+ Vue.h('body', null, [
33
+ Vue.h('div', { id: '__app' }, [
34
+ Vue.h(
35
+ RouterProvider,
36
+ {
37
+ router: props.router,
38
+ },
39
+ {
40
+ innerWrap: (innerProps: { children: any }) => [
41
+ Vue.h(ServerHeadContent),
42
+ innerProps.children,
43
+ Vue.h(Scripts),
44
+ ],
45
+ },
46
+ ),
47
+ ]),
48
+ ]),
49
+ ])
50
+ },
51
+ })
@@ -0,0 +1 @@
1
+ export { RouterClient } from './RouterClient'
@@ -0,0 +1,12 @@
1
+ import { defineHandlerCallback } from '@tanstack/router-core/ssr/server'
2
+ import { renderRouterToString } from './renderRouterToString'
3
+ import { RouterServer } from './RouterServer'
4
+
5
+ export const defaultRenderHandler = defineHandlerCallback(
6
+ ({ router, responseHeaders }) =>
7
+ renderRouterToString({
8
+ router,
9
+ responseHeaders,
10
+ App: RouterServer,
11
+ }),
12
+ )
@@ -0,0 +1,13 @@
1
+ import { defineHandlerCallback } from '@tanstack/router-core/ssr/server'
2
+ import { renderRouterToStream } from './renderRouterToStream'
3
+ import { RouterServer } from './RouterServer'
4
+
5
+ export const defaultStreamHandler = defineHandlerCallback(
6
+ async ({ request, router, responseHeaders }) =>
7
+ await renderRouterToStream({
8
+ request,
9
+ router,
10
+ responseHeaders,
11
+ App: RouterServer,
12
+ }),
13
+ )
@@ -0,0 +1,85 @@
1
+ import { ReadableStream as NodeReadableStream } from 'node:stream/web'
2
+ import * as Vue from 'vue'
3
+ import { pipeToWebWritable, renderToString } from 'vue/server-renderer'
4
+ import { isbot } from 'isbot'
5
+ import { transformReadableStreamWithRouter } from '@tanstack/router-core/ssr/server'
6
+ import type { AnyRouter } from '@tanstack/router-core'
7
+ import type { Component } from 'vue'
8
+ import type { ReadableStream } from 'node:stream/web'
9
+
10
+ function prependDoctype(
11
+ readable: globalThis.ReadableStream,
12
+ ): NodeReadableStream<Uint8Array> {
13
+ const encoder = new TextEncoder()
14
+ let sentDoctype = false
15
+
16
+ return new NodeReadableStream<Uint8Array>({
17
+ async start(controller) {
18
+ const reader = readable.getReader()
19
+
20
+ async function pump(): Promise<void> {
21
+ const { done, value } = await reader.read()
22
+ if (done) {
23
+ controller.close()
24
+ return
25
+ }
26
+
27
+ if (!sentDoctype) {
28
+ sentDoctype = true
29
+ controller.enqueue(encoder.encode('<!DOCTYPE html>'))
30
+ }
31
+ controller.enqueue(value)
32
+ return pump()
33
+ }
34
+
35
+ pump().catch((err) => controller.error(err))
36
+ },
37
+ })
38
+ }
39
+
40
+ export const renderRouterToStream = async ({
41
+ request,
42
+ router,
43
+ responseHeaders,
44
+ App,
45
+ }: {
46
+ request: Request
47
+ router: AnyRouter
48
+ responseHeaders: Headers
49
+ App: Component
50
+ }) => {
51
+ const app = Vue.createSSRApp(App, { router })
52
+
53
+ if (isbot(request.headers.get('User-Agent'))) {
54
+ let fullHtml = await renderToString(app)
55
+
56
+ const htmlOpenIndex = fullHtml.indexOf('<html')
57
+ const htmlCloseIndex = fullHtml.indexOf('</html>')
58
+
59
+ if (htmlOpenIndex !== -1 && htmlCloseIndex !== -1) {
60
+ fullHtml = fullHtml.slice(htmlOpenIndex, htmlCloseIndex + 7)
61
+ } else if (htmlOpenIndex !== -1) {
62
+ fullHtml = fullHtml.slice(htmlOpenIndex)
63
+ }
64
+
65
+ return new Response(`<!DOCTYPE html>${fullHtml}`, {
66
+ status: router.state.statusCode,
67
+ headers: responseHeaders,
68
+ })
69
+ }
70
+
71
+ const { writable, readable } = new TransformStream()
72
+
73
+ pipeToWebWritable(app, {}, writable)
74
+
75
+ const doctypedStream = prependDoctype(readable)
76
+ const responseStream = transformReadableStreamWithRouter(
77
+ router,
78
+ doctypedStream as unknown as ReadableStream,
79
+ )
80
+
81
+ return new Response(responseStream as any, {
82
+ status: router.state.statusCode,
83
+ headers: responseHeaders,
84
+ })
85
+ }
@@ -0,0 +1,37 @@
1
+ import * as Vue from 'vue'
2
+ import { renderToString as vueRenderToString } from 'vue/server-renderer'
3
+ import type { AnyRouter } from '@tanstack/router-core'
4
+ import type { Component } from 'vue'
5
+
6
+ export const renderRouterToString = async ({
7
+ router,
8
+ responseHeaders,
9
+ App,
10
+ }: {
11
+ router: AnyRouter
12
+ responseHeaders: Headers
13
+ App: Component
14
+ }) => {
15
+ try {
16
+ const app = Vue.createSSRApp(App, { router })
17
+
18
+ let html = await vueRenderToString(app)
19
+ router.serverSsr!.setRenderFinished()
20
+ const injectedHtml = await Promise.all(router.serverSsr!.injectedHtml).then(
21
+ (htmls) => htmls.join(''),
22
+ )
23
+ html = html.replace(`</body>`, () => `${injectedHtml}</body>`)
24
+ return new Response(`<!DOCTYPE html>${html}`, {
25
+ status: router.state.statusCode,
26
+ headers: responseHeaders,
27
+ })
28
+ } catch (error) {
29
+ console.error('Render to string error:', error)
30
+ return new Response('Internal Server Error', {
31
+ status: 500,
32
+ headers: responseHeaders,
33
+ })
34
+ } finally {
35
+ router.serverSsr?.cleanup()
36
+ }
37
+ }
@@ -0,0 +1,6 @@
1
+ export { RouterServer } from './RouterServer'
2
+ export { defaultRenderHandler } from './defaultRenderHandler'
3
+ export { defaultStreamHandler } from './defaultStreamHandler'
4
+ export { renderRouterToStream } from './renderRouterToStream'
5
+ export { renderRouterToString } from './renderRouterToString'
6
+ export * from '@tanstack/router-core/ssr/server'