@tanstack/react-router 1.121.0-alpha.22 → 1.121.0-alpha.28
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/Asset.cjs +83 -16
- package/dist/cjs/Asset.cjs.map +1 -1
- package/dist/cjs/Asset.d.cts +2 -1
- package/dist/cjs/CatchBoundary.cjs.map +1 -1
- package/dist/cjs/ClientOnly.cjs.map +1 -1
- package/dist/cjs/ClientOnly.d.cts +1 -1
- package/dist/cjs/HeadContent.cjs +19 -17
- package/dist/cjs/HeadContent.cjs.map +1 -1
- package/dist/cjs/Match.cjs +61 -57
- package/dist/cjs/Match.cjs.map +1 -1
- package/dist/cjs/Matches.cjs +14 -16
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +2 -2
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/SafeFragment.cjs.map +1 -1
- package/dist/cjs/ScriptOnce.cjs +3 -10
- package/dist/cjs/ScriptOnce.cjs.map +1 -1
- package/dist/cjs/ScriptOnce.d.cts +1 -1
- package/dist/cjs/Scripts.cjs +7 -11
- package/dist/cjs/Scripts.cjs.map +1 -1
- package/dist/cjs/ScrollRestoration.cjs +3 -4
- package/dist/cjs/ScrollRestoration.cjs.map +1 -1
- package/dist/cjs/Transitioner.cjs +16 -15
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/awaited.cjs.map +1 -1
- package/dist/cjs/fileRoute.cjs +8 -8
- package/dist/cjs/fileRoute.cjs.map +1 -1
- package/dist/cjs/index.cjs +0 -12
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +4 -8
- package/dist/cjs/lazyRouteComponent.cjs +3 -16
- package/dist/cjs/lazyRouteComponent.cjs.map +1 -1
- package/dist/cjs/lazyRouteComponent.d.cts +1 -1
- package/dist/cjs/link.cjs +106 -74
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +1 -5
- package/dist/cjs/matchContext.cjs.map +1 -1
- package/dist/cjs/not-found.cjs +2 -4
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/renderRouteNotFound.cjs.map +1 -1
- package/dist/cjs/route.cjs +21 -21
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +14 -6
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/routerContext.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs +9 -3
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/ssr/RouterClient.cjs +25 -0
- package/dist/cjs/ssr/RouterClient.cjs.map +1 -0
- package/dist/cjs/ssr/RouterClient.d.cts +4 -0
- package/dist/cjs/ssr/RouterServer.cjs +9 -0
- package/dist/cjs/ssr/RouterServer.cjs.map +1 -0
- package/dist/cjs/ssr/RouterServer.d.cts +4 -0
- package/dist/cjs/ssr/client.cjs +12 -0
- package/dist/cjs/ssr/client.cjs.map +1 -0
- package/dist/cjs/ssr/client.d.cts +2 -0
- package/dist/cjs/ssr/defaultRenderHandler.cjs +15 -0
- package/dist/cjs/ssr/defaultRenderHandler.cjs.map +1 -0
- package/dist/cjs/ssr/defaultRenderHandler.d.cts +1 -0
- package/dist/cjs/ssr/defaultStreamHandler.cjs +16 -0
- package/dist/cjs/ssr/defaultStreamHandler.cjs.map +1 -0
- package/dist/cjs/ssr/defaultStreamHandler.d.cts +1 -0
- package/dist/cjs/ssr/renderRouterToStream.cjs +63 -0
- package/dist/cjs/ssr/renderRouterToStream.cjs.map +1 -0
- package/dist/cjs/ssr/renderRouterToStream.d.cts +8 -0
- package/dist/cjs/ssr/renderRouterToString.cjs +28 -0
- package/dist/cjs/ssr/renderRouterToString.cjs.map +1 -0
- package/dist/cjs/ssr/renderRouterToString.d.cts +7 -0
- package/dist/cjs/ssr/server.cjs +20 -0
- package/dist/cjs/ssr/server.cjs.map +1 -0
- package/dist/cjs/ssr/server.d.cts +6 -0
- package/dist/cjs/useBlocker.cjs.map +1 -1
- package/dist/cjs/useCanGoBack.cjs.map +1 -1
- package/dist/cjs/useLoaderData.cjs.map +1 -1
- package/dist/cjs/useLoaderDeps.cjs.map +1 -1
- package/dist/cjs/useLocation.cjs +1 -1
- package/dist/cjs/useLocation.cjs.map +1 -1
- package/dist/cjs/useMatch.cjs.map +1 -1
- package/dist/cjs/useNavigate.cjs +2 -2
- package/dist/cjs/useNavigate.cjs.map +1 -1
- package/dist/cjs/useParams.cjs.map +1 -1
- package/dist/cjs/useRouter.cjs +1 -1
- package/dist/cjs/useRouter.cjs.map +1 -1
- package/dist/cjs/useRouterState.cjs +3 -3
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/cjs/useSearch.cjs.map +1 -1
- package/dist/cjs/utils.cjs +4 -10
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -1
- package/dist/esm/Asset.d.ts +2 -1
- package/dist/esm/Asset.js +66 -16
- package/dist/esm/Asset.js.map +1 -1
- package/dist/esm/CatchBoundary.js.map +1 -1
- package/dist/esm/ClientOnly.d.ts +1 -1
- package/dist/esm/ClientOnly.js.map +1 -1
- package/dist/esm/HeadContent.js +19 -17
- package/dist/esm/HeadContent.js.map +1 -1
- package/dist/esm/Match.js +63 -59
- package/dist/esm/Match.js.map +1 -1
- package/dist/esm/Matches.d.ts +2 -2
- package/dist/esm/Matches.js +14 -16
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/SafeFragment.js.map +1 -1
- package/dist/esm/ScriptOnce.d.ts +1 -1
- package/dist/esm/ScriptOnce.js +3 -10
- package/dist/esm/ScriptOnce.js.map +1 -1
- package/dist/esm/Scripts.js +7 -11
- package/dist/esm/Scripts.js.map +1 -1
- package/dist/esm/ScrollRestoration.js +3 -4
- package/dist/esm/ScrollRestoration.js.map +1 -1
- package/dist/esm/Transitioner.js +16 -15
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/awaited.js.map +1 -1
- package/dist/esm/fileRoute.js +8 -8
- package/dist/esm/fileRoute.js.map +1 -1
- package/dist/esm/index.d.ts +4 -8
- package/dist/esm/index.js +2 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lazyRouteComponent.d.ts +1 -1
- package/dist/esm/lazyRouteComponent.js +2 -15
- package/dist/esm/lazyRouteComponent.js.map +1 -1
- package/dist/esm/link.d.ts +1 -5
- package/dist/esm/link.js +107 -75
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/matchContext.js.map +1 -1
- package/dist/esm/not-found.js +2 -4
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/renderRouteNotFound.js.map +1 -1
- package/dist/esm/route.d.ts +14 -6
- package/dist/esm/route.js +21 -21
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerContext.js.map +1 -1
- package/dist/esm/scroll-restoration.js +9 -3
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/RouterClient.d.ts +4 -0
- package/dist/esm/ssr/RouterClient.js +25 -0
- package/dist/esm/ssr/RouterClient.js.map +1 -0
- package/dist/esm/ssr/RouterServer.d.ts +4 -0
- package/dist/esm/ssr/RouterServer.js +9 -0
- package/dist/esm/ssr/RouterServer.js.map +1 -0
- package/dist/esm/ssr/client.d.ts +2 -0
- package/dist/esm/ssr/client.js +6 -0
- package/dist/esm/ssr/client.js.map +1 -0
- package/dist/esm/ssr/defaultRenderHandler.d.ts +1 -0
- package/dist/esm/ssr/defaultRenderHandler.js +15 -0
- package/dist/esm/ssr/defaultRenderHandler.js.map +1 -0
- package/dist/esm/ssr/defaultStreamHandler.d.ts +1 -0
- package/dist/esm/ssr/defaultStreamHandler.js +16 -0
- package/dist/esm/ssr/defaultStreamHandler.js.map +1 -0
- package/dist/esm/ssr/renderRouterToStream.d.ts +8 -0
- package/dist/esm/ssr/renderRouterToStream.js +63 -0
- package/dist/esm/ssr/renderRouterToStream.js.map +1 -0
- package/dist/esm/ssr/renderRouterToString.d.ts +7 -0
- package/dist/esm/ssr/renderRouterToString.js +28 -0
- package/dist/esm/ssr/renderRouterToString.js.map +1 -0
- package/dist/esm/ssr/server.d.ts +6 -0
- package/dist/esm/ssr/server.js +14 -0
- package/dist/esm/ssr/server.js.map +1 -0
- package/dist/esm/useBlocker.js.map +1 -1
- package/dist/esm/useCanGoBack.js.map +1 -1
- package/dist/esm/useLoaderData.js.map +1 -1
- package/dist/esm/useLoaderDeps.js.map +1 -1
- package/dist/esm/useLocation.js +1 -1
- package/dist/esm/useLocation.js.map +1 -1
- package/dist/esm/useMatch.js.map +1 -1
- package/dist/esm/useNavigate.js +2 -2
- package/dist/esm/useNavigate.js.map +1 -1
- package/dist/esm/useParams.js.map +1 -1
- package/dist/esm/useRouter.js +1 -1
- package/dist/esm/useRouter.js.map +1 -1
- package/dist/esm/useRouterState.js +3 -3
- package/dist/esm/useRouterState.js.map +1 -1
- package/dist/esm/useSearch.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -1
- package/dist/esm/utils.js +4 -10
- package/dist/esm/utils.js.map +1 -1
- package/dist/llms/index.d.ts +3 -0
- package/dist/llms/index.js +35 -0
- package/dist/llms/rules/api.d.ts +2 -0
- package/dist/llms/rules/api.js +4326 -0
- package/dist/llms/rules/guide.d.ts +2 -0
- package/dist/llms/rules/guide.js +7096 -0
- package/dist/llms/rules/routing.d.ts +2 -0
- package/dist/llms/rules/routing.js +1981 -0
- package/dist/llms/rules/setup-and-architecture.d.ts +2 -0
- package/dist/llms/rules/setup-and-architecture.js +945 -0
- package/package.json +32 -6
- package/src/Asset.tsx +95 -16
- package/src/ClientOnly.tsx +1 -1
- package/src/HeadContent.tsx +16 -0
- package/src/Match.tsx +86 -63
- package/src/Matches.tsx +24 -17
- package/src/ScriptOnce.tsx +2 -14
- package/src/Transitioner.tsx +13 -14
- package/src/index.tsx +3 -21
- package/src/lazyRouteComponent.tsx +6 -31
- package/src/link.tsx +130 -99
- package/src/not-found.tsx +1 -1
- package/src/route.tsx +18 -9
- package/src/scroll-restoration.tsx +10 -3
- package/src/ssr/RouterClient.tsx +22 -0
- package/src/ssr/RouterServer.tsx +9 -0
- package/src/ssr/client.ts +2 -0
- package/src/ssr/defaultRenderHandler.tsx +12 -0
- package/src/ssr/defaultStreamHandler.tsx +13 -0
- package/src/ssr/renderRouterToStream.tsx +79 -0
- package/src/ssr/renderRouterToString.tsx +31 -0
- package/src/ssr/server.ts +6 -0
- package/src/utils.ts +6 -14
- package/dist/cjs/serializer.d.cts +0 -6
- package/dist/esm/serializer.d.ts +0 -6
- package/src/serializer.ts +0 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/react-router",
|
|
3
|
-
"version": "1.121.0-alpha.
|
|
3
|
+
"version": "1.121.0-alpha.28",
|
|
4
4
|
"description": "Modern and scalable routing for React applications",
|
|
5
5
|
"author": "Tanner Linsley",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,7 +38,33 @@
|
|
|
38
38
|
"default": "./dist/cjs/index.cjs"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
|
-
"./
|
|
41
|
+
"./ssr/server": {
|
|
42
|
+
"import": {
|
|
43
|
+
"types": "./dist/esm/ssr/server.d.ts",
|
|
44
|
+
"default": "./dist/esm/ssr/server.js"
|
|
45
|
+
},
|
|
46
|
+
"require": {
|
|
47
|
+
"types": "./dist/cjs/ssr/server.d.cts",
|
|
48
|
+
"default": "./dist/cjs/ssr/server.cjs"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"./ssr/client": {
|
|
52
|
+
"import": {
|
|
53
|
+
"types": "./dist/esm/ssr/client.d.ts",
|
|
54
|
+
"default": "./dist/esm/ssr/client.js"
|
|
55
|
+
},
|
|
56
|
+
"require": {
|
|
57
|
+
"types": "./dist/cjs/ssr/client.d.cts",
|
|
58
|
+
"default": "./dist/cjs/ssr/client.cjs"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"./package.json": "./package.json",
|
|
62
|
+
"./llms": {
|
|
63
|
+
"import": {
|
|
64
|
+
"types": "./dist/llms/index.d.ts",
|
|
65
|
+
"default": "./dist/llms/index.js"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
42
68
|
},
|
|
43
69
|
"sideEffects": false,
|
|
44
70
|
"files": [
|
|
@@ -50,20 +76,20 @@
|
|
|
50
76
|
},
|
|
51
77
|
"dependencies": {
|
|
52
78
|
"@tanstack/react-store": "^0.7.0",
|
|
53
|
-
"
|
|
79
|
+
"isbot": "^5.1.22",
|
|
54
80
|
"tiny-invariant": "^1.3.3",
|
|
55
81
|
"tiny-warning": "^1.0.3",
|
|
56
|
-
"@tanstack/history": "1.121.0-alpha.
|
|
57
|
-
"@tanstack/router-core": "1.121.0-alpha.
|
|
82
|
+
"@tanstack/history": "1.121.0-alpha.28",
|
|
83
|
+
"@tanstack/router-core": "1.121.0-alpha.28"
|
|
58
84
|
},
|
|
59
85
|
"devDependencies": {
|
|
60
86
|
"@testing-library/jest-dom": "^6.6.3",
|
|
61
87
|
"@testing-library/react": "^16.2.0",
|
|
62
|
-
"@types/jsesc": "^3.0.3",
|
|
63
88
|
"@vitejs/plugin-react": "^4.3.4",
|
|
64
89
|
"combinate": "^1.1.11",
|
|
65
90
|
"react": "^19.0.0",
|
|
66
91
|
"react-dom": "^19.0.0",
|
|
92
|
+
"vibe-rules": "^0.2.57",
|
|
67
93
|
"zod": "^3.24.2"
|
|
68
94
|
},
|
|
69
95
|
"peerDependencies": {
|
package/src/Asset.tsx
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
1
2
|
import type { RouterManagedTag } from '@tanstack/router-core'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
interface ScriptAttrs {
|
|
5
|
+
[key: string]: string | boolean | undefined
|
|
6
|
+
src?: string
|
|
7
|
+
suppressHydrationWarning?: boolean
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Asset({
|
|
11
|
+
tag,
|
|
12
|
+
attrs,
|
|
13
|
+
children,
|
|
14
|
+
}: RouterManagedTag): React.ReactElement | null {
|
|
4
15
|
switch (tag) {
|
|
5
16
|
case 'title':
|
|
6
17
|
return (
|
|
@@ -16,25 +27,93 @@ export function Asset({ tag, attrs, children }: RouterManagedTag): any {
|
|
|
16
27
|
return (
|
|
17
28
|
<style
|
|
18
29
|
{...attrs}
|
|
19
|
-
dangerouslySetInnerHTML={{ __html: children as
|
|
30
|
+
dangerouslySetInnerHTML={{ __html: children as string }}
|
|
20
31
|
/>
|
|
21
32
|
)
|
|
22
33
|
case 'script':
|
|
23
|
-
|
|
24
|
-
return <script {...attrs} suppressHydrationWarning />
|
|
25
|
-
}
|
|
26
|
-
if (typeof children === 'string')
|
|
27
|
-
return (
|
|
28
|
-
<script
|
|
29
|
-
{...attrs}
|
|
30
|
-
dangerouslySetInnerHTML={{
|
|
31
|
-
__html: children,
|
|
32
|
-
}}
|
|
33
|
-
suppressHydrationWarning
|
|
34
|
-
/>
|
|
35
|
-
)
|
|
36
|
-
return null
|
|
34
|
+
return <Script attrs={attrs}>{children}</Script>
|
|
37
35
|
default:
|
|
38
36
|
return null
|
|
39
37
|
}
|
|
40
38
|
}
|
|
39
|
+
|
|
40
|
+
function Script({
|
|
41
|
+
attrs,
|
|
42
|
+
children,
|
|
43
|
+
}: {
|
|
44
|
+
attrs?: ScriptAttrs
|
|
45
|
+
children?: string
|
|
46
|
+
}) {
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
if (attrs?.src) {
|
|
49
|
+
const script = document.createElement('script')
|
|
50
|
+
|
|
51
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
52
|
+
if (
|
|
53
|
+
key !== 'suppressHydrationWarning' &&
|
|
54
|
+
value !== undefined &&
|
|
55
|
+
value !== false
|
|
56
|
+
) {
|
|
57
|
+
script.setAttribute(
|
|
58
|
+
key,
|
|
59
|
+
typeof value === 'boolean' ? '' : String(value),
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
document.head.appendChild(script)
|
|
65
|
+
|
|
66
|
+
return () => {
|
|
67
|
+
if (script.parentNode) {
|
|
68
|
+
script.parentNode.removeChild(script)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeof children === 'string') {
|
|
74
|
+
const script = document.createElement('script')
|
|
75
|
+
script.textContent = children
|
|
76
|
+
|
|
77
|
+
if (attrs) {
|
|
78
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
79
|
+
if (
|
|
80
|
+
key !== 'suppressHydrationWarning' &&
|
|
81
|
+
value !== undefined &&
|
|
82
|
+
value !== false
|
|
83
|
+
) {
|
|
84
|
+
script.setAttribute(
|
|
85
|
+
key,
|
|
86
|
+
typeof value === 'boolean' ? '' : String(value),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
document.head.appendChild(script)
|
|
93
|
+
|
|
94
|
+
return () => {
|
|
95
|
+
if (script.parentNode) {
|
|
96
|
+
script.parentNode.removeChild(script)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return undefined
|
|
102
|
+
}, [attrs, children])
|
|
103
|
+
|
|
104
|
+
if (attrs?.src && typeof attrs.src === 'string') {
|
|
105
|
+
return <script {...attrs} suppressHydrationWarning />
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (typeof children === 'string') {
|
|
109
|
+
return (
|
|
110
|
+
<script
|
|
111
|
+
{...attrs}
|
|
112
|
+
dangerouslySetInnerHTML={{ __html: children }}
|
|
113
|
+
suppressHydrationWarning
|
|
114
|
+
/>
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return null
|
|
119
|
+
}
|
package/src/ClientOnly.tsx
CHANGED
package/src/HeadContent.tsx
CHANGED
|
@@ -120,6 +120,21 @@ export const useTags = () => {
|
|
|
120
120
|
structuralSharing: true as any,
|
|
121
121
|
})
|
|
122
122
|
|
|
123
|
+
const styles = useRouterState({
|
|
124
|
+
select: (state) =>
|
|
125
|
+
(
|
|
126
|
+
state.matches
|
|
127
|
+
.map((match) => match.styles!)
|
|
128
|
+
.flat(1)
|
|
129
|
+
.filter(Boolean) as Array<RouterManagedTag>
|
|
130
|
+
).map(({ children, ...attrs }) => ({
|
|
131
|
+
tag: 'style',
|
|
132
|
+
attrs,
|
|
133
|
+
children,
|
|
134
|
+
})),
|
|
135
|
+
structuralSharing: true as any,
|
|
136
|
+
})
|
|
137
|
+
|
|
123
138
|
const headScripts = useRouterState({
|
|
124
139
|
select: (state) =>
|
|
125
140
|
(
|
|
@@ -142,6 +157,7 @@ export const useTags = () => {
|
|
|
142
157
|
...meta,
|
|
143
158
|
...preloadMeta,
|
|
144
159
|
...links,
|
|
160
|
+
...styles,
|
|
145
161
|
...headScripts,
|
|
146
162
|
] as Array<RouterManagedTag>,
|
|
147
163
|
(d) => {
|
package/src/Match.tsx
CHANGED
|
@@ -17,7 +17,12 @@ import { matchContext } from './matchContext'
|
|
|
17
17
|
import { SafeFragment } from './SafeFragment'
|
|
18
18
|
import { renderRouteNotFound } from './renderRouteNotFound'
|
|
19
19
|
import { ScrollRestoration } from './scroll-restoration'
|
|
20
|
-
import
|
|
20
|
+
import { ClientOnly } from './ClientOnly'
|
|
21
|
+
import type {
|
|
22
|
+
AnyRoute,
|
|
23
|
+
ParsedLocation,
|
|
24
|
+
RootRouteOptions,
|
|
25
|
+
} from '@tanstack/router-core'
|
|
21
26
|
|
|
22
27
|
export const Match = React.memo(function MatchImpl({
|
|
23
28
|
matchId,
|
|
@@ -25,16 +30,19 @@ export const Match = React.memo(function MatchImpl({
|
|
|
25
30
|
matchId: string
|
|
26
31
|
}) {
|
|
27
32
|
const router = useRouter()
|
|
28
|
-
const
|
|
29
|
-
select: (s) =>
|
|
33
|
+
const matchState = useRouterState({
|
|
34
|
+
select: (s) => {
|
|
35
|
+
const match = s.matches.find((d) => d.id === matchId)
|
|
36
|
+
invariant(
|
|
37
|
+
match,
|
|
38
|
+
`Could not find match for matchId "${matchId}". Please file an issue!`,
|
|
39
|
+
)
|
|
40
|
+
return pick(match, ['routeId', 'ssr', '_displayPending'])
|
|
41
|
+
},
|
|
42
|
+
structuralSharing: true as any,
|
|
30
43
|
})
|
|
31
44
|
|
|
32
|
-
|
|
33
|
-
routeId,
|
|
34
|
-
`Could not find routeId for matchId "${matchId}". Please file an issue!`,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
const route: AnyRoute = router.routesById[routeId]
|
|
45
|
+
const route: AnyRoute = router.routesById[matchState.routeId]
|
|
38
46
|
|
|
39
47
|
const PendingComponent =
|
|
40
48
|
route.options.pendingComponent ?? router.options.defaultPendingComponent
|
|
@@ -52,12 +60,14 @@ export const Match = React.memo(function MatchImpl({
|
|
|
52
60
|
router.options.notFoundRoute?.options.component)
|
|
53
61
|
: route.options.notFoundComponent
|
|
54
62
|
|
|
63
|
+
const resolvedNoSsr =
|
|
64
|
+
matchState.ssr === false || matchState.ssr === 'data-only'
|
|
55
65
|
const ResolvedSuspenseBoundary =
|
|
56
66
|
// If we're on the root route, allow forcefully wrapping in suspense
|
|
57
|
-
(!route.isRoot || route.options.wrapInSuspense) &&
|
|
67
|
+
(!route.isRoot || route.options.wrapInSuspense || resolvedNoSsr) &&
|
|
58
68
|
(route.options.wrapInSuspense ??
|
|
59
69
|
PendingComponent ??
|
|
60
|
-
(route.options.errorComponent as any)?.preload)
|
|
70
|
+
((route.options.errorComponent as any)?.preload || resolvedNoSsr))
|
|
61
71
|
? React.Suspense
|
|
62
72
|
: SafeFragment
|
|
63
73
|
|
|
@@ -80,8 +90,11 @@ export const Match = React.memo(function MatchImpl({
|
|
|
80
90
|
},
|
|
81
91
|
})
|
|
82
92
|
|
|
93
|
+
const ShellComponent = route.isRoot
|
|
94
|
+
? ((route.options as RootRouteOptions).shellComponent ?? SafeFragment)
|
|
95
|
+
: SafeFragment
|
|
83
96
|
return (
|
|
84
|
-
|
|
97
|
+
<ShellComponent>
|
|
85
98
|
<matchContext.Provider value={matchId}>
|
|
86
99
|
<ResolvedSuspenseBoundary fallback={pendingElement}>
|
|
87
100
|
<ResolvedCatchBoundary
|
|
@@ -100,7 +113,7 @@ export const Match = React.memo(function MatchImpl({
|
|
|
100
113
|
// route ID which doesn't match the current route, rethrow the error
|
|
101
114
|
if (
|
|
102
115
|
!routeNotFoundComponent ||
|
|
103
|
-
(error.routeId && error.routeId !== routeId) ||
|
|
116
|
+
(error.routeId && error.routeId !== matchState.routeId) ||
|
|
104
117
|
(!error.routeId && !route.isRoot)
|
|
105
118
|
)
|
|
106
119
|
throw error
|
|
@@ -108,7 +121,13 @@ export const Match = React.memo(function MatchImpl({
|
|
|
108
121
|
return React.createElement(routeNotFoundComponent, error as any)
|
|
109
122
|
}}
|
|
110
123
|
>
|
|
111
|
-
|
|
124
|
+
{resolvedNoSsr || matchState._displayPending ? (
|
|
125
|
+
<ClientOnly fallback={pendingElement}>
|
|
126
|
+
<MatchInner matchId={matchId} />
|
|
127
|
+
</ClientOnly>
|
|
128
|
+
) : (
|
|
129
|
+
<MatchInner matchId={matchId} />
|
|
130
|
+
)}
|
|
112
131
|
</ResolvedNotFoundBoundary>
|
|
113
132
|
</ResolvedCatchBoundary>
|
|
114
133
|
</ResolvedSuspenseBoundary>
|
|
@@ -119,14 +138,14 @@ export const Match = React.memo(function MatchImpl({
|
|
|
119
138
|
<ScrollRestoration />
|
|
120
139
|
</>
|
|
121
140
|
) : null}
|
|
122
|
-
|
|
141
|
+
</ShellComponent>
|
|
123
142
|
)
|
|
124
143
|
})
|
|
125
144
|
|
|
126
145
|
// On Rendered can't happen above the root layout because it actually
|
|
127
146
|
// renders a dummy dom element to track the rendered state of the app.
|
|
128
147
|
// We render a script tag with a key that changes based on the current
|
|
129
|
-
// location state.
|
|
148
|
+
// location state.__TSR_key. Also, because it's below the root layout, it
|
|
130
149
|
// allows us to fire onRendered events even after a hydration mismatch
|
|
131
150
|
// error that occurred above the root layout (like bad head/link tags,
|
|
132
151
|
// which is common).
|
|
@@ -139,7 +158,7 @@ function OnRendered() {
|
|
|
139
158
|
|
|
140
159
|
return (
|
|
141
160
|
<script
|
|
142
|
-
key={router.latestLocation.state.
|
|
161
|
+
key={router.latestLocation.state.__TSR_key}
|
|
143
162
|
suppressHydrationWarning
|
|
144
163
|
ref={(el) => {
|
|
145
164
|
if (
|
|
@@ -185,7 +204,13 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
185
204
|
return {
|
|
186
205
|
key,
|
|
187
206
|
routeId,
|
|
188
|
-
match: pick(match, [
|
|
207
|
+
match: pick(match, [
|
|
208
|
+
'id',
|
|
209
|
+
'status',
|
|
210
|
+
'error',
|
|
211
|
+
'_forcePending',
|
|
212
|
+
'_displayPending',
|
|
213
|
+
]),
|
|
189
214
|
}
|
|
190
215
|
},
|
|
191
216
|
structuralSharing: true as any,
|
|
@@ -201,9 +226,45 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
201
226
|
return <Outlet />
|
|
202
227
|
}, [key, route.options.component, router.options.defaultComponent])
|
|
203
228
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
229
|
+
if (match._displayPending) {
|
|
230
|
+
throw router.getMatch(match.id)?.displayPendingPromise
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (match._forcePending) {
|
|
234
|
+
throw router.getMatch(match.id)?.minPendingPromise
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// see also hydrate() in packages/router-core/src/ssr/ssr-client.ts
|
|
238
|
+
if (match.status === 'pending') {
|
|
239
|
+
// We're pending, and if we have a minPendingMs, we need to wait for it
|
|
240
|
+
const pendingMinMs =
|
|
241
|
+
route.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
242
|
+
|
|
243
|
+
if (pendingMinMs && !router.getMatch(match.id)?.minPendingPromise) {
|
|
244
|
+
// Create a promise that will resolve after the minPendingMs
|
|
245
|
+
if (!router.isServer) {
|
|
246
|
+
const minPendingPromise = createControlledPromise<void>()
|
|
247
|
+
|
|
248
|
+
Promise.resolve().then(() => {
|
|
249
|
+
router.updateMatch(match.id, (prev) => ({
|
|
250
|
+
...prev,
|
|
251
|
+
minPendingPromise,
|
|
252
|
+
}))
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
setTimeout(() => {
|
|
256
|
+
minPendingPromise.resolve()
|
|
257
|
+
|
|
258
|
+
// We've handled the minPendingPromise, so we can delete it
|
|
259
|
+
router.updateMatch(match.id, (prev) => ({
|
|
260
|
+
...prev,
|
|
261
|
+
minPendingPromise: undefined,
|
|
262
|
+
}))
|
|
263
|
+
}, pendingMinMs)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
throw router.getMatch(match.id)?.loadPromise
|
|
267
|
+
}
|
|
207
268
|
|
|
208
269
|
if (match.status === 'notFound') {
|
|
209
270
|
invariant(isNotFound(match.error), 'Expected a notFound error')
|
|
@@ -229,6 +290,10 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
229
290
|
// renderToPipeableStream to not hang indefinitely.
|
|
230
291
|
// We'll serialize the error and rethrow it on the client.
|
|
231
292
|
if (router.isServer) {
|
|
293
|
+
const RouteErrorComponent =
|
|
294
|
+
(route.options.errorComponent ??
|
|
295
|
+
router.options.defaultErrorComponent) ||
|
|
296
|
+
ErrorComponent
|
|
232
297
|
return (
|
|
233
298
|
<RouteErrorComponent
|
|
234
299
|
error={match.error as any}
|
|
@@ -243,37 +308,6 @@ export const MatchInner = React.memo(function MatchInnerImpl({
|
|
|
243
308
|
throw match.error
|
|
244
309
|
}
|
|
245
310
|
|
|
246
|
-
if (match.status === 'pending') {
|
|
247
|
-
// We're pending, and if we have a minPendingMs, we need to wait for it
|
|
248
|
-
const pendingMinMs =
|
|
249
|
-
route.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
250
|
-
|
|
251
|
-
if (pendingMinMs && !router.getMatch(match.id)?.minPendingPromise) {
|
|
252
|
-
// Create a promise that will resolve after the minPendingMs
|
|
253
|
-
if (!router.isServer) {
|
|
254
|
-
const minPendingPromise = createControlledPromise<void>()
|
|
255
|
-
|
|
256
|
-
Promise.resolve().then(() => {
|
|
257
|
-
router.updateMatch(match.id, (prev) => ({
|
|
258
|
-
...prev,
|
|
259
|
-
minPendingPromise,
|
|
260
|
-
}))
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
setTimeout(() => {
|
|
264
|
-
minPendingPromise.resolve()
|
|
265
|
-
|
|
266
|
-
// We've handled the minPendingPromise, so we can delete it
|
|
267
|
-
router.updateMatch(match.id, (prev) => ({
|
|
268
|
-
...prev,
|
|
269
|
-
minPendingPromise: undefined,
|
|
270
|
-
}))
|
|
271
|
-
}, pendingMinMs)
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
throw router.getMatch(match.id)?.loadPromise
|
|
275
|
-
}
|
|
276
|
-
|
|
277
311
|
return out
|
|
278
312
|
})
|
|
279
313
|
|
|
@@ -310,13 +344,6 @@ export const Outlet = React.memo(function OutletImpl() {
|
|
|
310
344
|
<router.options.defaultPendingComponent />
|
|
311
345
|
) : null
|
|
312
346
|
|
|
313
|
-
if (router.isShell)
|
|
314
|
-
return (
|
|
315
|
-
<React.Suspense fallback={pendingElement}>
|
|
316
|
-
<ShellInner />
|
|
317
|
-
</React.Suspense>
|
|
318
|
-
)
|
|
319
|
-
|
|
320
347
|
if (parentGlobalNotFound) {
|
|
321
348
|
return renderRouteNotFound(router, route, undefined)
|
|
322
349
|
}
|
|
@@ -335,7 +362,3 @@ export const Outlet = React.memo(function OutletImpl() {
|
|
|
335
362
|
|
|
336
363
|
return nextMatch
|
|
337
364
|
})
|
|
338
|
-
|
|
339
|
-
function ShellInner(): React.ReactElement {
|
|
340
|
-
throw new Error('ShellBoundaryError')
|
|
341
|
-
}
|
package/src/Matches.tsx
CHANGED
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
StructuralSharingOption,
|
|
12
12
|
ValidateSelected,
|
|
13
13
|
} from './structuralSharing'
|
|
14
|
-
import type { ReactNode } from './route'
|
|
15
14
|
import type {
|
|
16
15
|
AnyRouter,
|
|
17
16
|
DeepPartial,
|
|
@@ -35,6 +34,7 @@ declare module '@tanstack/router-core' {
|
|
|
35
34
|
meta?: Array<React.JSX.IntrinsicElements['meta'] | undefined>
|
|
36
35
|
links?: Array<React.JSX.IntrinsicElements['link'] | undefined>
|
|
37
36
|
scripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>
|
|
37
|
+
styles?: Array<React.JSX.IntrinsicElements['style'] | undefined>
|
|
38
38
|
headScripts?: Array<React.JSX.IntrinsicElements['script'] | undefined>
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -48,13 +48,13 @@ export function Matches() {
|
|
|
48
48
|
|
|
49
49
|
// Do not render a root Suspense during SSR or hydrating from SSR
|
|
50
50
|
const ResolvedSuspense =
|
|
51
|
-
router.isServer || (typeof document !== 'undefined' && router.
|
|
51
|
+
router.isServer || (typeof document !== 'undefined' && router.ssr)
|
|
52
52
|
? SafeFragment
|
|
53
53
|
: React.Suspense
|
|
54
54
|
|
|
55
55
|
const inner = (
|
|
56
56
|
<ResolvedSuspense fallback={pendingElement}>
|
|
57
|
-
<Transitioner />
|
|
57
|
+
{!router.isServer && <Transitioner />}
|
|
58
58
|
<MatchesInner />
|
|
59
59
|
</ResolvedSuspense>
|
|
60
60
|
)
|
|
@@ -67,6 +67,7 @@ export function Matches() {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
function MatchesInner() {
|
|
70
|
+
const router = useRouter()
|
|
70
71
|
const matchId = useRouterState({
|
|
71
72
|
select: (s) => {
|
|
72
73
|
return s.matches[0]?.id
|
|
@@ -77,21 +78,27 @@ function MatchesInner() {
|
|
|
77
78
|
select: (s) => s.loadedAt,
|
|
78
79
|
})
|
|
79
80
|
|
|
81
|
+
const matchComponent = matchId ? <Match matchId={matchId} /> : null
|
|
82
|
+
|
|
80
83
|
return (
|
|
81
84
|
<matchContext.Provider value={matchId}>
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
85
|
+
{router.options.disableGlobalCatchBoundary ? (
|
|
86
|
+
matchComponent
|
|
87
|
+
) : (
|
|
88
|
+
<CatchBoundary
|
|
89
|
+
getResetKey={() => resetKey}
|
|
90
|
+
errorComponent={ErrorComponent}
|
|
91
|
+
onCatch={(error) => {
|
|
92
|
+
warning(
|
|
93
|
+
false,
|
|
94
|
+
`The following error wasn't caught by any route! At the very least, consider setting an 'errorComponent' in your RootRoute!`,
|
|
95
|
+
)
|
|
96
|
+
warning(false, error.message || error.toString())
|
|
97
|
+
}}
|
|
98
|
+
>
|
|
99
|
+
{matchComponent}
|
|
100
|
+
</CatchBoundary>
|
|
101
|
+
)}
|
|
95
102
|
</matchContext.Provider>
|
|
96
103
|
)
|
|
97
104
|
}
|
|
@@ -154,7 +161,7 @@ export type MakeMatchRouteOptions<
|
|
|
154
161
|
TRouter['routeTree'],
|
|
155
162
|
ResolveRelativePath<TFrom, NoInfer<TTo>>
|
|
156
163
|
>['types']['allParams'],
|
|
157
|
-
) => ReactNode)
|
|
164
|
+
) => React.ReactNode)
|
|
158
165
|
| React.ReactNode
|
|
159
166
|
}
|
|
160
167
|
|
package/src/ScriptOnce.tsx
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import jsesc from 'jsesc'
|
|
2
|
-
|
|
3
1
|
export function ScriptOnce({
|
|
4
2
|
children,
|
|
5
|
-
log,
|
|
6
3
|
}: {
|
|
7
4
|
children: string
|
|
8
5
|
log?: boolean
|
|
@@ -14,18 +11,9 @@ export function ScriptOnce({
|
|
|
14
11
|
|
|
15
12
|
return (
|
|
16
13
|
<script
|
|
17
|
-
className="tsr
|
|
14
|
+
className="$tsr"
|
|
18
15
|
dangerouslySetInnerHTML={{
|
|
19
|
-
__html: [
|
|
20
|
-
children,
|
|
21
|
-
(log ?? true) && process.env.NODE_ENV === 'development'
|
|
22
|
-
? `console.info(\`Injected From Server:
|
|
23
|
-
${jsesc(children.toString(), { quotes: 'backtick' })}\`)`
|
|
24
|
-
: '',
|
|
25
|
-
'if (typeof __TSR_SSR__ !== "undefined") __TSR_SSR__.cleanScripts()',
|
|
26
|
-
]
|
|
27
|
-
.filter(Boolean)
|
|
28
|
-
.join('\n'),
|
|
16
|
+
__html: [children].filter(Boolean).join('\n'),
|
|
29
17
|
}}
|
|
30
18
|
/>
|
|
31
19
|
)
|
package/src/Transitioner.tsx
CHANGED
|
@@ -11,14 +11,14 @@ import { useRouterState } from './useRouterState'
|
|
|
11
11
|
export function Transitioner() {
|
|
12
12
|
const router = useRouter()
|
|
13
13
|
const mountLoadForRouter = React.useRef({ router, mounted: false })
|
|
14
|
-
const isLoading = useRouterState({
|
|
15
|
-
select: ({ isLoading }) => isLoading,
|
|
16
|
-
})
|
|
17
14
|
|
|
18
15
|
const [isTransitioning, setIsTransitioning] = React.useState(false)
|
|
19
16
|
// Track pending state changes
|
|
20
|
-
const hasPendingMatches = useRouterState({
|
|
21
|
-
select: (s) =>
|
|
17
|
+
const { hasPendingMatches, isLoading } = useRouterState({
|
|
18
|
+
select: (s) => ({
|
|
19
|
+
isLoading: s.isLoading,
|
|
20
|
+
hasPendingMatches: s.matches.some((d) => d.status === 'pending'),
|
|
21
|
+
}),
|
|
22
22
|
structuralSharing: true,
|
|
23
23
|
})
|
|
24
24
|
|
|
@@ -30,14 +30,12 @@ export function Transitioner() {
|
|
|
30
30
|
const isPagePending = isLoading || hasPendingMatches
|
|
31
31
|
const previousIsPagePending = usePrevious(isPagePending)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
})
|
|
40
|
-
}
|
|
33
|
+
router.startTransition = (fn: () => void) => {
|
|
34
|
+
setIsTransitioning(true)
|
|
35
|
+
React.startTransition(() => {
|
|
36
|
+
fn()
|
|
37
|
+
setIsTransitioning(false)
|
|
38
|
+
})
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
// Subscribe to location changes
|
|
@@ -69,7 +67,8 @@ export function Transitioner() {
|
|
|
69
67
|
// Try to load the initial location
|
|
70
68
|
useLayoutEffect(() => {
|
|
71
69
|
if (
|
|
72
|
-
|
|
70
|
+
// if we are hydrating from SSR, loading is triggered in ssr-client
|
|
71
|
+
(typeof window !== 'undefined' && router.ssr) ||
|
|
73
72
|
(mountLoadForRouter.current.router === router &&
|
|
74
73
|
mountLoadForRouter.current.mounted)
|
|
75
74
|
) {
|