wcz-test 1.6.0 → 1.8.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/.prettierignore +4 -0
- package/.vs/VSWorkspaceState.json +6 -0
- package/.vs/slnx.sqlite +0 -0
- package/.vs/wcz-test/CopilotIndices/17.13.441.19478/CodeChunks.db +0 -0
- package/.vs/wcz-test/CopilotIndices/17.13.441.19478/SemanticSymbols.db +0 -0
- package/.vs/wcz-test/v17/.wsuo +0 -0
- package/.vs/wcz-test/v17/DocumentLayout.backup.json +31 -0
- package/.vs/wcz-test/v17/DocumentLayout.json +31 -0
- package/.vscode/settings.json +11 -0
- package/app.config.ts +15 -0
- package/dist/index.cjs +36 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +5 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/package.json +35 -45
- package/postcss.config.mjs +6 -0
- package/public/android-chrome-192x192.png +0 -0
- package/public/android-chrome-512x512.png +0 -0
- package/public/apple-touch-icon.png +0 -0
- package/public/favicon-16x16.png +0 -0
- package/public/favicon-32x32.png +0 -0
- package/public/favicon.ico +0 -0
- package/public/favicon.png +0 -0
- package/public/site.webmanifest +19 -0
- package/src/api.ts +6 -0
- package/src/client.tsx +8 -0
- package/src/components/DefaultCatchBoundary.tsx +53 -0
- package/src/components/MyComponent.tsx +8 -0
- package/src/components/NotFound.tsx +25 -0
- package/src/components/PostError.tsx +5 -0
- package/src/components/UserError.tsx +5 -0
- package/src/global-middleware.ts +6 -0
- package/src/index.ts +1 -0
- package/src/routeTree.gen.ts +483 -0
- package/src/router.tsx +22 -0
- package/src/routes/__root.tsx +139 -0
- package/src/routes/_pathlessLayout/_nested-layout/route-a.tsx +11 -0
- package/src/routes/_pathlessLayout/_nested-layout/route-b.tsx +11 -0
- package/src/routes/_pathlessLayout/_nested-layout.tsx +34 -0
- package/src/routes/_pathlessLayout.tsx +16 -0
- package/src/routes/api/users.$id.ts +24 -0
- package/src/routes/api/users.ts +17 -0
- package/src/routes/deferred.tsx +62 -0
- package/src/routes/index.tsx +13 -0
- package/src/routes/posts.$postId.tsx +34 -0
- package/src/routes/posts.index.tsx +9 -0
- package/src/routes/posts.route.tsx +38 -0
- package/src/routes/posts_.$postId.deep.tsx +29 -0
- package/src/routes/redirect.tsx +9 -0
- package/src/routes/users.$userId.tsx +33 -0
- package/src/routes/users.index.tsx +9 -0
- package/src/routes/users.route.tsx +48 -0
- package/src/ssr.tsx +13 -0
- package/src/styles/app.css +22 -0
- package/src/utils/loggingMiddleware.tsx +41 -0
- package/src/utils/posts.tsx +36 -0
- package/src/utils/seo.ts +33 -0
- package/src/utils/users.tsx +7 -0
- package/tailwind.config.mjs +4 -0
- package/tsconfig.json +22 -0
- package/tsup.config.ts +10 -0
- package/dist/api.d.ts +0 -3
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js +0 -3
- package/dist/api.js.map +0 -1
- package/dist/client.d.ts +0 -2
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js +0 -8
- package/dist/client.js.map +0 -1
- package/dist/components/DefaultCatchBoundary.d.ts +0 -3
- package/dist/components/DefaultCatchBoundary.d.ts.map +0 -1
- package/dist/components/DefaultCatchBoundary.js +0 -17
- package/dist/components/DefaultCatchBoundary.js.map +0 -1
- package/dist/components/MyComponent.d.ts +0 -3
- package/dist/components/MyComponent.d.ts.map +0 -1
- package/dist/components/MyComponent.js +0 -5
- package/dist/components/MyComponent.js.map +0 -1
- package/dist/components/NotFound.d.ts +0 -4
- package/dist/components/NotFound.d.ts.map +0 -1
- package/dist/components/NotFound.js +0 -6
- package/dist/components/NotFound.js.map +0 -1
- package/dist/components/PostError.d.ts +0 -3
- package/dist/components/PostError.d.ts.map +0 -1
- package/dist/components/PostError.js +0 -6
- package/dist/components/PostError.js.map +0 -1
- package/dist/components/UserError.d.ts +0 -3
- package/dist/components/UserError.d.ts.map +0 -1
- package/dist/components/UserError.js +0 -6
- package/dist/components/UserError.js.map +0 -1
- package/dist/global-middleware.d.ts +0 -2
- package/dist/global-middleware.d.ts.map +0 -1
- package/dist/global-middleware.js +0 -6
- package/dist/global-middleware.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/routeTree.gen.d.ts +0 -249
- package/dist/routeTree.gen.d.ts.map +0 -1
- package/dist/routeTree.gen.js +0 -205
- package/dist/routeTree.gen.js.map +0 -1
- package/dist/router.d.ts +0 -7
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -16
- package/dist/router.js.map +0 -1
- package/dist/routes/__root.d.ts +0 -2
- package/dist/routes/__root.d.ts.map +0 -1
- package/dist/routes/__root.js +0 -74
- package/dist/routes/__root.js.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout/route-a.d.ts +0 -2
- package/dist/routes/_pathlessLayout/_nested-layout/route-a.d.ts.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout/route-a.js +0 -9
- package/dist/routes/_pathlessLayout/_nested-layout/route-a.js.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout/route-b.d.ts +0 -2
- package/dist/routes/_pathlessLayout/_nested-layout/route-b.d.ts.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout/route-b.js +0 -9
- package/dist/routes/_pathlessLayout/_nested-layout/route-b.js.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout.d.ts +0 -2
- package/dist/routes/_pathlessLayout/_nested-layout.d.ts.map +0 -1
- package/dist/routes/_pathlessLayout/_nested-layout.js +0 -13
- package/dist/routes/_pathlessLayout/_nested-layout.js.map +0 -1
- package/dist/routes/_pathlessLayout.d.ts +0 -2
- package/dist/routes/_pathlessLayout.d.ts.map +0 -1
- package/dist/routes/_pathlessLayout.js +0 -9
- package/dist/routes/_pathlessLayout.js.map +0 -1
- package/dist/routes/api/users.$id.d.ts +0 -5
- package/dist/routes/api/users.$id.d.ts.map +0 -1
- package/dist/routes/api/users.$id.js +0 -21
- package/dist/routes/api/users.$id.js.map +0 -1
- package/dist/routes/api/users.d.ts +0 -5
- package/dist/routes/api/users.d.ts.map +0 -1
- package/dist/routes/api/users.js +0 -12
- package/dist/routes/api/users.js.map +0 -1
- package/dist/routes/deferred.d.ts +0 -12
- package/dist/routes/deferred.d.ts.map +0 -1
- package/dist/routes/deferred.js +0 -31
- package/dist/routes/deferred.js.map +0 -1
- package/dist/routes/index.d.ts +0 -2
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/index.js +0 -9
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/posts.$postId.d.ts +0 -14
- package/dist/routes/posts.$postId.d.ts.map +0 -1
- package/dist/routes/posts.$postId.js +0 -20
- package/dist/routes/posts.$postId.js.map +0 -1
- package/dist/routes/posts.index.d.ts +0 -6
- package/dist/routes/posts.index.d.ts.map +0 -1
- package/dist/routes/posts.index.js +0 -9
- package/dist/routes/posts.index.js.map +0 -1
- package/dist/routes/posts.route.d.ts +0 -6
- package/dist/routes/posts.route.d.ts.map +0 -1
- package/dist/routes/posts.route.js +0 -16
- package/dist/routes/posts.route.js.map +0 -1
- package/dist/routes/posts_.$postId.deep.d.ts +0 -6
- package/dist/routes/posts_.$postId.deep.d.ts.map +0 -1
- package/dist/routes/posts_.$postId.deep.js +0 -16
- package/dist/routes/posts_.$postId.deep.js.map +0 -1
- package/dist/routes/redirect.d.ts +0 -2
- package/dist/routes/redirect.d.ts.map +0 -1
- package/dist/routes/redirect.js +0 -9
- package/dist/routes/redirect.js.map +0 -1
- package/dist/routes/users.$userId.d.ts +0 -3
- package/dist/routes/users.$userId.d.ts.map +0 -1
- package/dist/routes/users.$userId.js +0 -26
- package/dist/routes/users.$userId.js.map +0 -1
- package/dist/routes/users.index.d.ts +0 -2
- package/dist/routes/users.index.d.ts.map +0 -1
- package/dist/routes/users.index.js +0 -10
- package/dist/routes/users.index.js.map +0 -1
- package/dist/routes/users.route.d.ts +0 -3
- package/dist/routes/users.route.d.ts.map +0 -1
- package/dist/routes/users.route.js +0 -27
- package/dist/routes/users.route.js.map +0 -1
- package/dist/ssr.d.ts +0 -3
- package/dist/ssr.d.ts.map +0 -1
- package/dist/ssr.js +0 -9
- package/dist/ssr.js.map +0 -1
- package/dist/utils/loggingMiddleware.d.ts +0 -9
- package/dist/utils/loggingMiddleware.d.ts.map +0 -1
- package/dist/utils/loggingMiddleware.js +0 -35
- package/dist/utils/loggingMiddleware.js.map +0 -1
- package/dist/utils/posts.d.ts +0 -8
- package/dist/utils/posts.d.ts.map +0 -1
- package/dist/utils/posts.js +0 -26
- package/dist/utils/posts.js.map +0 -1
- package/dist/utils/seo.d.ts +0 -15
- package/dist/utils/seo.d.ts.map +0 -1
- package/dist/utils/seo.js +0 -23
- package/dist/utils/seo.js.map +0 -1
- package/dist/utils/users.d.ts +0 -7
- package/dist/utils/users.d.ts.map +0 -1
- package/dist/utils/users.js +0 -2
- package/dist/utils/users.js.map +0 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Outlet, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
|
|
3
|
+
export const Route = createFileRoute('/_pathlessLayout')({
|
|
4
|
+
component: LayoutComponent,
|
|
5
|
+
})
|
|
6
|
+
|
|
7
|
+
function LayoutComponent() {
|
|
8
|
+
return (
|
|
9
|
+
<div className="p-2">
|
|
10
|
+
<div className="border-b">I'm a layout</div>
|
|
11
|
+
<div>
|
|
12
|
+
<Outlet />
|
|
13
|
+
</div>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { json } from '@tanstack/react-start'
|
|
2
|
+
import { createAPIFileRoute } from '@tanstack/react-start/api'
|
|
3
|
+
import axios from 'redaxios'
|
|
4
|
+
import type { User } from '../../utils/users'
|
|
5
|
+
|
|
6
|
+
export const APIRoute = createAPIFileRoute('/api/users/$id')({
|
|
7
|
+
GET: async ({ request, params }) => {
|
|
8
|
+
console.info(`Fetching users by id=${params.id}... @`, request.url)
|
|
9
|
+
try {
|
|
10
|
+
const res = await axios.get<User>(
|
|
11
|
+
'https://jsonplaceholder.typicode.com/users/' + params.id,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
return json({
|
|
15
|
+
id: res.data.id,
|
|
16
|
+
name: res.data.name,
|
|
17
|
+
email: res.data.email,
|
|
18
|
+
})
|
|
19
|
+
} catch (e) {
|
|
20
|
+
console.error(e)
|
|
21
|
+
return json({ error: 'User not found' }, { status: 404 })
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { json } from '@tanstack/react-start'
|
|
2
|
+
import { createAPIFileRoute } from '@tanstack/react-start/api'
|
|
3
|
+
import axios from 'redaxios'
|
|
4
|
+
import type { User } from '../../utils/users'
|
|
5
|
+
|
|
6
|
+
export const APIRoute = createAPIFileRoute('/api/users')({
|
|
7
|
+
GET: async ({ request }) => {
|
|
8
|
+
console.info('Fetching users... @', request.url)
|
|
9
|
+
const res = await axios.get<Array<User>>(
|
|
10
|
+
'https://jsonplaceholder.typicode.com/users',
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const list = res.data.slice(0, 10)
|
|
14
|
+
|
|
15
|
+
return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email })))
|
|
16
|
+
},
|
|
17
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Await, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
3
|
+
import { Suspense, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
const personServerFn = createServerFn({ method: 'GET' })
|
|
6
|
+
.validator((d: string) => d)
|
|
7
|
+
.handler(({ data: name }) => {
|
|
8
|
+
return { name, randomNumber: Math.floor(Math.random() * 100) }
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
const slowServerFn = createServerFn({ method: 'GET' })
|
|
12
|
+
.validator((d: string) => d)
|
|
13
|
+
.handler(async ({ data: name }) => {
|
|
14
|
+
await new Promise((r) => setTimeout(r, 1000))
|
|
15
|
+
return { name, randomNumber: Math.floor(Math.random() * 100) }
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export const Route = createFileRoute('/deferred')({
|
|
19
|
+
loader: async () => {
|
|
20
|
+
return {
|
|
21
|
+
deferredStuff: new Promise<string>((r) =>
|
|
22
|
+
setTimeout(() => r('Hello deferred!'), 2000),
|
|
23
|
+
),
|
|
24
|
+
deferredPerson: slowServerFn({ data: 'Tanner Linsley' }),
|
|
25
|
+
person: await personServerFn({ data: 'John Doe' }),
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
component: Deferred,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
function Deferred() {
|
|
32
|
+
const [count, setCount] = useState(0)
|
|
33
|
+
const { deferredStuff, deferredPerson, person } = Route.useLoaderData()
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<div className="p-2">
|
|
37
|
+
<div data-testid="regular-person">
|
|
38
|
+
{person.name} - {person.randomNumber}
|
|
39
|
+
</div>
|
|
40
|
+
<Suspense fallback={<div>Loading person...</div>}>
|
|
41
|
+
<Await
|
|
42
|
+
promise={deferredPerson}
|
|
43
|
+
children={(data) => (
|
|
44
|
+
<div data-testid="deferred-person">
|
|
45
|
+
{data.name} - {data.randomNumber}
|
|
46
|
+
</div>
|
|
47
|
+
)}
|
|
48
|
+
/>
|
|
49
|
+
</Suspense>
|
|
50
|
+
<Suspense fallback={<div>Loading stuff...</div>}>
|
|
51
|
+
<Await
|
|
52
|
+
promise={deferredStuff}
|
|
53
|
+
children={(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
|
|
54
|
+
/>
|
|
55
|
+
</Suspense>
|
|
56
|
+
<div>Count: {count}</div>
|
|
57
|
+
<div>
|
|
58
|
+
<button onClick={() => setCount(count + 1)}>Increment</button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
)
|
|
62
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Link, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { fetchPost } from '../utils/posts'
|
|
3
|
+
import { NotFound } from '~/components/NotFound'
|
|
4
|
+
import { PostErrorComponent } from '~/components/PostError'
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute('/posts/$postId')({
|
|
7
|
+
loader: ({ params: { postId } }) => fetchPost({ data: postId }),
|
|
8
|
+
errorComponent: PostErrorComponent,
|
|
9
|
+
component: PostComponent,
|
|
10
|
+
notFoundComponent: () => {
|
|
11
|
+
return <NotFound>Post not found</NotFound>
|
|
12
|
+
},
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
function PostComponent() {
|
|
16
|
+
const post = Route.useLoaderData()
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className="space-y-2">
|
|
20
|
+
<h4 className="text-xl font-bold underline">{post.title}</h4>
|
|
21
|
+
<div className="text-sm">{post.body}</div>
|
|
22
|
+
<Link
|
|
23
|
+
to="/posts/$postId/deep"
|
|
24
|
+
params={{
|
|
25
|
+
postId: post.id,
|
|
26
|
+
}}
|
|
27
|
+
activeProps={{ className: 'text-black font-bold' }}
|
|
28
|
+
className="block py-1 text-blue-800 hover:text-blue-600"
|
|
29
|
+
>
|
|
30
|
+
Deep View
|
|
31
|
+
</Link>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { fetchPosts } from '../utils/posts'
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute('/posts')({
|
|
5
|
+
loader: async () => fetchPosts(),
|
|
6
|
+
component: PostsLayoutComponent,
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
function PostsLayoutComponent() {
|
|
10
|
+
const posts = Route.useLoaderData()
|
|
11
|
+
|
|
12
|
+
return (
|
|
13
|
+
<div className="p-2 flex gap-2">
|
|
14
|
+
<ul className="list-disc pl-4">
|
|
15
|
+
{[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map(
|
|
16
|
+
(post) => {
|
|
17
|
+
return (
|
|
18
|
+
<li key={post.id} className="whitespace-nowrap">
|
|
19
|
+
<Link
|
|
20
|
+
to="/posts/$postId"
|
|
21
|
+
params={{
|
|
22
|
+
postId: post.id,
|
|
23
|
+
}}
|
|
24
|
+
className="block py-1 text-blue-800 hover:text-blue-600"
|
|
25
|
+
activeProps={{ className: 'text-black font-bold' }}
|
|
26
|
+
>
|
|
27
|
+
<div>{post.title.substring(0, 20)}</div>
|
|
28
|
+
</Link>
|
|
29
|
+
</li>
|
|
30
|
+
)
|
|
31
|
+
},
|
|
32
|
+
)}
|
|
33
|
+
</ul>
|
|
34
|
+
<hr />
|
|
35
|
+
<Outlet />
|
|
36
|
+
</div>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Link, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import { fetchPost } from '../utils/posts'
|
|
3
|
+
import { PostErrorComponent } from '~/components/PostError'
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute('/posts_/$postId/deep')({
|
|
6
|
+
loader: async ({ params: { postId } }) =>
|
|
7
|
+
fetchPost({
|
|
8
|
+
data: postId,
|
|
9
|
+
}),
|
|
10
|
+
errorComponent: PostErrorComponent,
|
|
11
|
+
component: PostDeepComponent,
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
function PostDeepComponent() {
|
|
15
|
+
const post = Route.useLoaderData()
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<div className="p-2 space-y-2">
|
|
19
|
+
<Link
|
|
20
|
+
to="/posts"
|
|
21
|
+
className="block py-1 text-blue-800 hover:text-blue-600"
|
|
22
|
+
>
|
|
23
|
+
← All Posts
|
|
24
|
+
</Link>
|
|
25
|
+
<h4 className="text-xl font-bold underline">{post.title}</h4>
|
|
26
|
+
<div className="text-sm">{post.body}</div>
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import axios from 'redaxios'
|
|
3
|
+
import type { User } from '~/utils/users'
|
|
4
|
+
import { DEPLOY_URL } from '~/utils/users'
|
|
5
|
+
import { NotFound } from '~/components/NotFound'
|
|
6
|
+
import { UserErrorComponent } from '~/components/UserError'
|
|
7
|
+
|
|
8
|
+
export const Route = createFileRoute('/users/$userId')({
|
|
9
|
+
loader: async ({ params: { userId } }) => {
|
|
10
|
+
return await axios
|
|
11
|
+
.get<User>(DEPLOY_URL + '/api/users/' + userId)
|
|
12
|
+
.then((r) => r.data)
|
|
13
|
+
.catch(() => {
|
|
14
|
+
throw new Error('Failed to fetch user')
|
|
15
|
+
})
|
|
16
|
+
},
|
|
17
|
+
errorComponent: UserErrorComponent,
|
|
18
|
+
component: UserComponent,
|
|
19
|
+
notFoundComponent: () => {
|
|
20
|
+
return <NotFound>User not found</NotFound>
|
|
21
|
+
},
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
function UserComponent() {
|
|
25
|
+
const user = Route.useLoaderData()
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="space-y-2">
|
|
29
|
+
<h4 className="text-xl font-bold underline">{user.name}</h4>
|
|
30
|
+
<div className="text-sm">{user.email}</div>
|
|
31
|
+
</div>
|
|
32
|
+
)
|
|
33
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Link, Outlet, createFileRoute } from '@tanstack/react-router'
|
|
2
|
+
import axios from 'redaxios'
|
|
3
|
+
import { DEPLOY_URL } from '../utils/users'
|
|
4
|
+
import type { User } from '../utils/users'
|
|
5
|
+
|
|
6
|
+
export const Route = createFileRoute('/users')({
|
|
7
|
+
loader: async () => {
|
|
8
|
+
return await axios
|
|
9
|
+
.get<Array<User>>(DEPLOY_URL + '/api/users')
|
|
10
|
+
.then((r) => r.data)
|
|
11
|
+
.catch(() => {
|
|
12
|
+
throw new Error('Failed to fetch users')
|
|
13
|
+
})
|
|
14
|
+
},
|
|
15
|
+
component: UsersLayoutComponent,
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
function UsersLayoutComponent() {
|
|
19
|
+
const users = Route.useLoaderData()
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<div className="p-2 flex gap-2">
|
|
23
|
+
<ul className="list-disc pl-4">
|
|
24
|
+
{[
|
|
25
|
+
...users,
|
|
26
|
+
{ id: 'i-do-not-exist', name: 'Non-existent User', email: '' },
|
|
27
|
+
].map((user) => {
|
|
28
|
+
return (
|
|
29
|
+
<li key={user.id} className="whitespace-nowrap">
|
|
30
|
+
<Link
|
|
31
|
+
to="/users/$userId"
|
|
32
|
+
params={{
|
|
33
|
+
userId: String(user.id),
|
|
34
|
+
}}
|
|
35
|
+
className="block py-1 text-blue-800 hover:text-blue-600"
|
|
36
|
+
activeProps={{ className: 'text-black font-bold' }}
|
|
37
|
+
>
|
|
38
|
+
<div>{user.name}</div>
|
|
39
|
+
</Link>
|
|
40
|
+
</li>
|
|
41
|
+
)
|
|
42
|
+
})}
|
|
43
|
+
</ul>
|
|
44
|
+
<hr />
|
|
45
|
+
<Outlet />
|
|
46
|
+
</div>
|
|
47
|
+
)
|
|
48
|
+
}
|
package/src/ssr.tsx
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/// <reference types="vinxi/types/server" />
|
|
2
|
+
import {
|
|
3
|
+
createStartHandler,
|
|
4
|
+
defaultStreamHandler,
|
|
5
|
+
} from '@tanstack/react-start/server'
|
|
6
|
+
import { getRouterManifest } from '@tanstack/react-start/router-manifest'
|
|
7
|
+
|
|
8
|
+
import { createRouter } from './router'
|
|
9
|
+
|
|
10
|
+
export default createStartHandler({
|
|
11
|
+
createRouter,
|
|
12
|
+
getRouterManifest,
|
|
13
|
+
})(defaultStreamHandler)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
@layer base {
|
|
6
|
+
html {
|
|
7
|
+
color-scheme: light dark;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
* {
|
|
11
|
+
@apply border-gray-200 dark:border-gray-800;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
html,
|
|
15
|
+
body {
|
|
16
|
+
@apply text-gray-900 bg-gray-50 dark:bg-gray-950 dark:text-gray-200;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.using-mouse * {
|
|
20
|
+
outline: none !important;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createMiddleware } from '@tanstack/react-start'
|
|
2
|
+
|
|
3
|
+
const preLogMiddleware = createMiddleware()
|
|
4
|
+
.client(async (ctx) => {
|
|
5
|
+
const clientTime = new Date()
|
|
6
|
+
|
|
7
|
+
return ctx.next({
|
|
8
|
+
context: {
|
|
9
|
+
clientTime,
|
|
10
|
+
},
|
|
11
|
+
sendContext: {
|
|
12
|
+
clientTime,
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
})
|
|
16
|
+
.server(async (ctx) => {
|
|
17
|
+
const serverTime = new Date()
|
|
18
|
+
|
|
19
|
+
return ctx.next({
|
|
20
|
+
sendContext: {
|
|
21
|
+
serverTime,
|
|
22
|
+
durationToServer:
|
|
23
|
+
serverTime.getTime() - ctx.context.clientTime.getTime(),
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
export const logMiddleware = createMiddleware()
|
|
29
|
+
.middleware([preLogMiddleware])
|
|
30
|
+
.client(async (ctx) => {
|
|
31
|
+
const res = await ctx.next()
|
|
32
|
+
|
|
33
|
+
const now = new Date()
|
|
34
|
+
console.log('Client Req/Res:', {
|
|
35
|
+
duration: res.context.clientTime.getTime() - now.getTime(),
|
|
36
|
+
durationToServer: res.context.durationToServer,
|
|
37
|
+
durationFromServer: now.getTime() - res.context.serverTime.getTime(),
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
return res
|
|
41
|
+
})
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { notFound } from '@tanstack/react-router'
|
|
2
|
+
import { createServerFn } from '@tanstack/react-start'
|
|
3
|
+
import axios from 'redaxios'
|
|
4
|
+
|
|
5
|
+
export type PostType = {
|
|
6
|
+
id: string
|
|
7
|
+
title: string
|
|
8
|
+
body: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const fetchPost = createServerFn({ method: 'GET' })
|
|
12
|
+
.validator((d: string) => d)
|
|
13
|
+
.handler(async ({ data }) => {
|
|
14
|
+
console.info(`Fetching post with id ${data}...`)
|
|
15
|
+
const post = await axios
|
|
16
|
+
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${data}`)
|
|
17
|
+
.then((r) => r.data)
|
|
18
|
+
.catch((err) => {
|
|
19
|
+
console.error(err)
|
|
20
|
+
if (err.status === 404) {
|
|
21
|
+
throw notFound()
|
|
22
|
+
}
|
|
23
|
+
throw err
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
return post
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
export const fetchPosts = createServerFn({ method: 'GET' }).handler(
|
|
30
|
+
async () => {
|
|
31
|
+
console.info('Fetching posts...')
|
|
32
|
+
return axios
|
|
33
|
+
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
|
|
34
|
+
.then((r) => r.data.slice(0, 10))
|
|
35
|
+
},
|
|
36
|
+
)
|
package/src/utils/seo.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const seo = ({
|
|
2
|
+
title,
|
|
3
|
+
description,
|
|
4
|
+
keywords,
|
|
5
|
+
image,
|
|
6
|
+
}: {
|
|
7
|
+
title: string
|
|
8
|
+
description?: string
|
|
9
|
+
image?: string
|
|
10
|
+
keywords?: string
|
|
11
|
+
}) => {
|
|
12
|
+
const tags = [
|
|
13
|
+
{ title },
|
|
14
|
+
{ name: 'description', content: description },
|
|
15
|
+
{ name: 'keywords', content: keywords },
|
|
16
|
+
{ name: 'twitter:title', content: title },
|
|
17
|
+
{ name: 'twitter:description', content: description },
|
|
18
|
+
{ name: 'twitter:creator', content: '@tannerlinsley' },
|
|
19
|
+
{ name: 'twitter:site', content: '@tannerlinsley' },
|
|
20
|
+
{ name: 'og:type', content: 'website' },
|
|
21
|
+
{ name: 'og:title', content: title },
|
|
22
|
+
{ name: 'og:description', content: description },
|
|
23
|
+
...(image
|
|
24
|
+
? [
|
|
25
|
+
{ name: 'twitter:image', content: image },
|
|
26
|
+
{ name: 'twitter:card', content: 'summary_large_image' },
|
|
27
|
+
{ name: 'og:image', content: image },
|
|
28
|
+
]
|
|
29
|
+
: []),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
return tags
|
|
33
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"include": ["**/*.ts", "**/*.tsx"],
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"strict": true,
|
|
5
|
+
"esModuleInterop": true,
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"lib": ["DOM", "DOM.Iterable", "ES2022"],
|
|
10
|
+
"isolatedModules": true,
|
|
11
|
+
"resolveJsonModule": true,
|
|
12
|
+
"skipLibCheck": true,
|
|
13
|
+
"target": "ES2022",
|
|
14
|
+
"allowJs": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"baseUrl": ".",
|
|
17
|
+
"paths": {
|
|
18
|
+
"~/*": ["./src/*"]
|
|
19
|
+
},
|
|
20
|
+
"noEmit": true
|
|
21
|
+
}
|
|
22
|
+
}
|
package/tsup.config.ts
ADDED
package/dist/api.d.ts
DELETED
package/dist/api.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":";AAKA,wBAAgE"}
|
package/dist/api.js
DELETED
package/dist/api.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,2BAA2B,CAAA;AAElC,eAAe,qBAAqB,CAAC,0BAA0B,CAAC,CAAA"}
|
package/dist/client.d.ts
DELETED
package/dist/client.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":""}
|
package/dist/client.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/// <reference types="vinxi/types/client" />
|
|
3
|
-
import { hydrateRoot } from 'react-dom/client';
|
|
4
|
-
import { StartClient } from '@tanstack/react-start';
|
|
5
|
-
import { createRouter } from './router';
|
|
6
|
-
const router = createRouter();
|
|
7
|
-
hydrateRoot(document, _jsx(StartClient, { router: router }));
|
|
8
|
-
//# sourceMappingURL=client.js.map
|
package/dist/client.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":";AAAA,4CAA4C;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAEvC,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;AAE7B,WAAW,CAAC,QAAQ,EAAE,KAAC,WAAW,IAAC,MAAM,EAAE,MAAM,GAAI,CAAC,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultCatchBoundary.d.ts","sourceRoot":"","sources":["../../src/components/DefaultCatchBoundary.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAEjE,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,EAAE,mBAAmB,2CA2ClE"}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ErrorComponent, Link, rootRouteId, useMatch, useRouter, } from '@tanstack/react-router';
|
|
3
|
-
export function DefaultCatchBoundary({ error }) {
|
|
4
|
-
const router = useRouter();
|
|
5
|
-
const isRoot = useMatch({
|
|
6
|
-
strict: false,
|
|
7
|
-
select: (state) => state.id === rootRouteId,
|
|
8
|
-
});
|
|
9
|
-
console.error('DefaultCatchBoundary Error:', error);
|
|
10
|
-
return (_jsxs("div", { className: "min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6", children: [_jsx(ErrorComponent, { error: error }), _jsxs("div", { className: "flex gap-2 items-center flex-wrap", children: [_jsx("button", { onClick: () => {
|
|
11
|
-
router.invalidate();
|
|
12
|
-
}, className: `px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`, children: "Try Again" }), isRoot ? (_jsx(Link, { to: "/", className: `px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`, children: "Home" })) : (_jsx(Link, { to: "/", className: `px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`, onClick: (e) => {
|
|
13
|
-
e.preventDefault();
|
|
14
|
-
window.history.back();
|
|
15
|
-
}, children: "Go Back" }))] })] }));
|
|
16
|
-
}
|
|
17
|
-
//# sourceMappingURL=DefaultCatchBoundary.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultCatchBoundary.js","sourceRoot":"","sources":["../../src/components/DefaultCatchBoundary.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,cAAc,EACd,IAAI,EACJ,WAAW,EACX,QAAQ,EACR,SAAS,GACV,MAAM,wBAAwB,CAAA;AAG/B,MAAM,UAAU,oBAAoB,CAAC,EAAE,KAAK,EAAuB;IACjE,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC;QACtB,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,WAAW;KAC5C,CAAC,CAAA;IAEF,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,KAAK,CAAC,CAAA;IAEnD,OAAO,CACL,eAAK,SAAS,EAAC,oEAAoE,aACjF,KAAC,cAAc,IAAC,KAAK,EAAE,KAAK,GAAI,EAChC,eAAK,SAAS,EAAC,mCAAmC,aAChD,iBACE,OAAO,EAAE,GAAG,EAAE;4BACZ,MAAM,CAAC,UAAU,EAAE,CAAA;wBACrB,CAAC,EACD,SAAS,EAAE,oFAAoF,0BAGxF,EACR,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,IAAI,IACH,EAAE,EAAC,GAAG,EACN,SAAS,EAAE,oFAAoF,qBAG1F,CACR,CAAC,CAAC,CAAC,CACF,KAAC,IAAI,IACH,EAAE,EAAC,GAAG,EACN,SAAS,EAAE,oFAAoF,EAC/F,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE;4BACb,CAAC,CAAC,cAAc,EAAE,CAAA;4BAClB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAA;wBACvB,CAAC,wBAGI,CACR,IACG,IACF,CACP,CAAA;AACH,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MyComponent.d.ts","sourceRoot":"","sources":["../../src/components/MyComponent.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAE3B,eAAO,MAAM,WAAW,EAAE,EAGzB,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"MyComponent.js","sourceRoot":"","sources":["../../src/components/MyComponent.tsx"],"names":[],"mappings":";AAEA,MAAM,CAAC,MAAM,WAAW,GAAO,GAAG,EAAE;IAEhC,OAAO,4CAA0B,CAAA;AACrC,CAAC,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"NotFound.d.ts","sourceRoot":"","sources":["../../src/components/NotFound.tsx"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,CAAC,EAAE,GAAG,CAAA;CAAE,2CAsBxD"}
|