create-better-t-stack 2.2.3 → 2.3.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/dist/index.js +74 -128
- package/package.json +4 -4
- package/templates/addons/turborepo/{turbo.json → turbo.json.hbs} +7 -1
- package/templates/api/orpc/server/base/src/lib/context.ts.hbs +0 -2
- package/templates/auth/web/nuxt/app/components/SignInForm.vue +0 -1
- package/templates/auth/web/nuxt/app/components/SignUpForm.vue +0 -1
- package/templates/auth/web/nuxt/app/components/UserMenu.vue +0 -1
- package/templates/backend/convex/packages/backend/_gitignore +2 -0
- package/templates/backend/convex/packages/backend/convex/README.md +90 -0
- package/templates/backend/convex/packages/backend/convex/healthCheck.ts +7 -0
- package/templates/backend/convex/packages/backend/convex/schema.ts +9 -0
- package/templates/backend/convex/packages/backend/convex/todos.ts +42 -0
- package/templates/backend/convex/packages/backend/convex/tsconfig.json +25 -0
- package/templates/backend/convex/packages/backend/package.json.hbs +21 -0
- package/templates/base/package.json +2 -1
- package/templates/examples/todo/web/react/react-router/src/routes/todos.tsx.hbs +178 -93
- package/templates/examples/todo/web/react/tanstack-router/src/routes/todos.tsx.hbs +178 -92
- package/templates/examples/todo/web/react/tanstack-start/src/routes/todos.tsx.hbs +119 -18
- package/templates/extras/pnpm-workspace.yaml +1 -0
- package/templates/frontend/native/app/(drawer)/index.tsx.hbs +35 -7
- package/templates/frontend/native/app/_layout.tsx.hbs +27 -0
- package/templates/frontend/react/next/package.json +0 -2
- package/templates/frontend/react/next/src/app/page.tsx.hbs +26 -44
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +15 -3
- package/templates/frontend/react/react-router/package.json +0 -2
- package/templates/frontend/react/react-router/src/root.tsx.hbs +31 -11
- package/templates/frontend/react/react-router/src/routes/_index.tsx.hbs +28 -47
- package/templates/frontend/react/tanstack-router/package.json +0 -2
- package/templates/frontend/react/tanstack-router/src/main.tsx.hbs +18 -2
- package/templates/frontend/react/tanstack-router/src/routes/__root.tsx.hbs +24 -1
- package/templates/frontend/react/tanstack-router/src/routes/index.tsx.hbs +24 -39
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +57 -13
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +12 -10
- package/templates/frontend/react/tanstack-start/src/routes/index.tsx.hbs +31 -45
- package/dist/index.d.ts +0 -2
- /package/templates/backend/{elysia → server/elysia}/src/index.ts.hbs +0 -0
- /package/templates/backend/{express → server/express}/src/index.ts.hbs +0 -0
- /package/templates/backend/{hono → server/hono}/src/index.ts.hbs +0 -0
- /package/templates/backend/{next → server/next}/next-env.d.ts +0 -0
- /package/templates/backend/{next → server/next}/next.config.ts +0 -0
- /package/templates/backend/{next → server/next}/package.json +0 -0
- /package/templates/backend/{next → server/next}/src/app/route.ts +0 -0
- /package/templates/backend/{next → server/next}/src/middleware.ts +0 -0
- /package/templates/backend/{next → server/next}/tsconfig.json +0 -0
- /package/templates/backend/{server-base → server/server-base}/_gitignore +0 -0
- /package/templates/backend/{server-base → server/server-base}/package.json +0 -0
- /package/templates/backend/{server-base → server/server-base}/src/routers/index.ts.hbs +0 -0
- /package/templates/backend/{server-base → server/server-base}/tsconfig.json.hbs +0 -0
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use client"
|
|
2
|
-
{{#if (eq
|
|
2
|
+
{{#if (eq backend "convex")}}
|
|
3
|
+
import { useQuery } from "convex/react";
|
|
4
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
|
|
5
|
+
{{else}}
|
|
6
|
+
{{#if (eq api "orpc")}}
|
|
3
7
|
import { orpc } from "@/utils/orpc";
|
|
4
|
-
{{/if}}
|
|
5
|
-
{{#if (eq api "trpc")}}
|
|
8
|
+
{{/if}}
|
|
9
|
+
{{#if (eq api "trpc")}}
|
|
6
10
|
import { trpc } from "@/utils/trpc";
|
|
7
|
-
{{/if}}
|
|
11
|
+
{{/if}}
|
|
8
12
|
import { useQuery } from "@tanstack/react-query";
|
|
13
|
+
{{/if}}
|
|
9
14
|
|
|
10
15
|
const TITLE_TEXT = `
|
|
11
16
|
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
@@ -24,10 +29,11 @@ const TITLE_TEXT = `
|
|
|
24
29
|
`;
|
|
25
30
|
|
|
26
31
|
export default function Home() {
|
|
27
|
-
{{#if (eq
|
|
32
|
+
{{#if (eq backend "convex")}}
|
|
33
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
34
|
+
{{else if (eq api "orpc")}}
|
|
28
35
|
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
|
|
29
|
-
{{
|
|
30
|
-
{{#if (eq api "trpc")}}
|
|
36
|
+
{{else if (eq api "trpc")}}
|
|
31
37
|
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
32
38
|
{{/if}}
|
|
33
39
|
|
|
@@ -38,6 +44,18 @@ export default function Home() {
|
|
|
38
44
|
<section className="rounded-lg border p-4">
|
|
39
45
|
<h2 className="mb-2 font-medium">API Status</h2>
|
|
40
46
|
<div className="flex items-center gap-2">
|
|
47
|
+
{{#if (eq backend "convex")}}
|
|
48
|
+
<div
|
|
49
|
+
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
|
|
50
|
+
/>
|
|
51
|
+
<span className="text-sm text-muted-foreground">
|
|
52
|
+
{healthCheck === undefined
|
|
53
|
+
? "Checking..."
|
|
54
|
+
: healthCheck === "OK"
|
|
55
|
+
? "Connected"
|
|
56
|
+
: "Error"}
|
|
57
|
+
</span>
|
|
58
|
+
{{else}}
|
|
41
59
|
<div
|
|
42
60
|
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
|
|
43
61
|
/>
|
|
@@ -48,46 +66,10 @@ export default function Home() {
|
|
|
48
66
|
? "Connected"
|
|
49
67
|
: "Disconnected"}
|
|
50
68
|
</span>
|
|
69
|
+
{{/if}}
|
|
51
70
|
</div>
|
|
52
71
|
</section>
|
|
53
|
-
|
|
54
|
-
<section>
|
|
55
|
-
<h2 className="mb-3 font-medium">Core Features</h2>
|
|
56
|
-
<ul className="grid grid-cols-2 gap-3">
|
|
57
|
-
<FeatureItem
|
|
58
|
-
title="Type-Safe API"
|
|
59
|
-
description="End-to-end type safety with tRPC"
|
|
60
|
-
/>
|
|
61
|
-
<FeatureItem
|
|
62
|
-
title="Modern React"
|
|
63
|
-
description="TanStack Router + TanStack Query"
|
|
64
|
-
/>
|
|
65
|
-
<FeatureItem
|
|
66
|
-
title="Fast Backend"
|
|
67
|
-
description="Lightweight Hono server"
|
|
68
|
-
/>
|
|
69
|
-
<FeatureItem
|
|
70
|
-
title="Beautiful UI"
|
|
71
|
-
description="TailwindCSS + shadcn/ui components"
|
|
72
|
-
/>
|
|
73
|
-
</ul>
|
|
74
|
-
</section>
|
|
75
72
|
</div>
|
|
76
73
|
</div>
|
|
77
74
|
);
|
|
78
75
|
}
|
|
79
|
-
|
|
80
|
-
function FeatureItem({
|
|
81
|
-
title,
|
|
82
|
-
description,
|
|
83
|
-
}: {
|
|
84
|
-
title: string;
|
|
85
|
-
description: string;
|
|
86
|
-
}) {
|
|
87
|
-
return (
|
|
88
|
-
<li className="border-l-2 border-primary py-1 pl-3">
|
|
89
|
-
<h3 className="font-medium">{title}</h3>
|
|
90
|
-
<p className="text-sm text-muted-foreground">{description}</p>
|
|
91
|
-
</li>
|
|
92
|
-
);
|
|
93
|
-
}
|
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
"use client"
|
|
2
|
+
{{#if (eq backend "convex")}}
|
|
3
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
4
|
+
{{else}}
|
|
2
5
|
import { QueryClientProvider } from "@tanstack/react-query";
|
|
3
|
-
{{#if (eq api "orpc")}}
|
|
6
|
+
{{#if (eq api "orpc")}}
|
|
4
7
|
import { orpc, ORPCContext, queryClient } from "@/utils/orpc";
|
|
5
|
-
{{/if}}
|
|
6
|
-
{{#if (eq api "trpc")}}
|
|
8
|
+
{{/if}}
|
|
9
|
+
{{#if (eq api "trpc")}}
|
|
7
10
|
import { queryClient } from "@/utils/trpc";
|
|
11
|
+
{{/if}}
|
|
8
12
|
{{/if}}
|
|
9
13
|
import { ThemeProvider } from "./theme-provider";
|
|
10
14
|
import { Toaster } from "./ui/sonner";
|
|
11
15
|
|
|
16
|
+
{{#if (eq backend "convex")}}
|
|
17
|
+
const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!);
|
|
18
|
+
{{/if}}
|
|
19
|
+
|
|
12
20
|
export default function Providers({
|
|
13
21
|
children,
|
|
14
22
|
}: {
|
|
@@ -21,6 +29,9 @@ export default function Providers({
|
|
|
21
29
|
enableSystem
|
|
22
30
|
disableTransitionOnChange
|
|
23
31
|
>
|
|
32
|
+
{{#if (eq backend "convex")}}
|
|
33
|
+
<ConvexProvider client={convex}>{children}</ConvexProvider>
|
|
34
|
+
{{else}}
|
|
24
35
|
<QueryClientProvider client={queryClient}>
|
|
25
36
|
{{#if (eq api "orpc")}}
|
|
26
37
|
<ORPCContext.Provider value={orpc}>
|
|
@@ -31,6 +42,7 @@ export default function Providers({
|
|
|
31
42
|
{children}
|
|
32
43
|
{{/if}}
|
|
33
44
|
</QueryClientProvider>
|
|
45
|
+
{{/if}}
|
|
34
46
|
<Toaster richColors />
|
|
35
47
|
</ThemeProvider>
|
|
36
48
|
)
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"@react-router/node": "^7.4.1",
|
|
18
18
|
"@react-router/serve": "^7.4.1",
|
|
19
19
|
"@tanstack/react-form": "^1.2.3",
|
|
20
|
-
"@tanstack/react-query": "^5.71.3",
|
|
21
20
|
"class-variance-authority": "^0.7.1",
|
|
22
21
|
"clsx": "^2.1.1",
|
|
23
22
|
"isbot": "^5.1.17",
|
|
@@ -34,7 +33,6 @@
|
|
|
34
33
|
"devDependencies": {
|
|
35
34
|
"@react-router/dev": "^7.4.1",
|
|
36
35
|
"@tailwindcss/vite": "^4.0.0",
|
|
37
|
-
"@tanstack/react-query-devtools": "^5.71.3",
|
|
38
36
|
"@types/node": "^20",
|
|
39
37
|
"@types/react": "^19.0.1",
|
|
40
38
|
"@types/react-dom": "^19.0.1",
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
-
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
3
1
|
import {
|
|
4
2
|
isRouteErrorResponse,
|
|
5
3
|
Links,
|
|
@@ -14,11 +12,17 @@ import Header from "./components/header";
|
|
|
14
12
|
import { ThemeProvider } from "./components/theme-provider";
|
|
15
13
|
import { Toaster } from "./components/ui/sonner";
|
|
16
14
|
|
|
17
|
-
{{#if (eq
|
|
18
|
-
import {
|
|
19
|
-
{{
|
|
20
|
-
{
|
|
21
|
-
import {
|
|
15
|
+
{{#if (eq backend "convex")}}
|
|
16
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
17
|
+
{{else}}
|
|
18
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
19
|
+
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
|
20
|
+
{{#if (eq api "orpc")}}
|
|
21
|
+
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
|
|
22
|
+
{{/if}}
|
|
23
|
+
{{#if (eq api "trpc")}}
|
|
24
|
+
import { queryClient } from "./utils/trpc";
|
|
25
|
+
{{/if}}
|
|
22
26
|
{{/if}}
|
|
23
27
|
|
|
24
28
|
export const links: Route.LinksFunction = () => [
|
|
@@ -52,7 +56,25 @@ export function Layout({ children }: { children: React.ReactNode }) {
|
|
|
52
56
|
);
|
|
53
57
|
}
|
|
54
58
|
|
|
55
|
-
{{#if (eq
|
|
59
|
+
{{#if (eq backend "convex")}}
|
|
60
|
+
export default function App() {
|
|
61
|
+
const convex = new ConvexReactClient(
|
|
62
|
+
import.meta.env.VITE_CONVEX_URL as string,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<ConvexProvider client={convex}>
|
|
67
|
+
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
|
68
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
69
|
+
<Header />
|
|
70
|
+
<Outlet />
|
|
71
|
+
</div>
|
|
72
|
+
<Toaster richColors />
|
|
73
|
+
</ThemeProvider>
|
|
74
|
+
</ConvexProvider>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
{{else if (eq api "orpc")}}
|
|
56
78
|
export default function App() {
|
|
57
79
|
return (
|
|
58
80
|
<QueryClientProvider client={queryClient}>
|
|
@@ -69,9 +91,7 @@ export default function App() {
|
|
|
69
91
|
</QueryClientProvider>
|
|
70
92
|
);
|
|
71
93
|
}
|
|
72
|
-
{{
|
|
73
|
-
|
|
74
|
-
{{#if (eq api "trpc")}}
|
|
94
|
+
{{else if (eq api "trpc")}}
|
|
75
95
|
export default function App() {
|
|
76
96
|
return (
|
|
77
97
|
<QueryClientProvider client={queryClient}>
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { Route } from "./+types/_index";
|
|
2
|
-
{{#if (eq
|
|
3
|
-
import {
|
|
4
|
-
{{
|
|
5
|
-
{{
|
|
6
|
-
|
|
7
|
-
{
|
|
2
|
+
{{#if (eq backend "convex")}}
|
|
3
|
+
import { useQuery } from "convex/react";
|
|
4
|
+
import { api } from "@{{projectName}}/backend/convex/_generated/api.js";
|
|
5
|
+
{{else}}
|
|
6
|
+
{{#if (eq api "orpc")}}
|
|
7
|
+
import { orpc } from "@/utils/orpc";
|
|
8
|
+
{{/if}}
|
|
9
|
+
{{#if (eq api "trpc")}}
|
|
10
|
+
import { trpc } from "@/utils/trpc";
|
|
11
|
+
{{/if}}
|
|
8
12
|
import { useQuery } from "@tanstack/react-query";
|
|
13
|
+
{{/if}}
|
|
9
14
|
|
|
10
15
|
const TITLE_TEXT = `
|
|
11
16
|
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
@@ -28,11 +33,11 @@ export function meta({}: Route.MetaArgs) {
|
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export default function Home() {
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
{{#if (eq backend "convex")}}
|
|
37
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
38
|
+
{{else if (eq api "orpc")}}
|
|
33
39
|
const healthCheck = useQuery(orpc.healthCheck.queryOptions());
|
|
34
|
-
{{
|
|
35
|
-
{{#if (eq api "trpc")}}
|
|
40
|
+
{{else if (eq api "trpc")}}
|
|
36
41
|
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
37
42
|
{{/if}}
|
|
38
43
|
|
|
@@ -43,6 +48,18 @@ export default function Home() {
|
|
|
43
48
|
<section className="rounded-lg border p-4">
|
|
44
49
|
<h2 className="mb-2 font-medium">API Status</h2>
|
|
45
50
|
<div className="flex items-center gap-2">
|
|
51
|
+
{{#if (eq backend "convex")}}
|
|
52
|
+
<div
|
|
53
|
+
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
|
|
54
|
+
/>
|
|
55
|
+
<span className="text-sm text-muted-foreground">
|
|
56
|
+
{healthCheck === undefined
|
|
57
|
+
? "Checking..."
|
|
58
|
+
: healthCheck === "OK"
|
|
59
|
+
? "Connected"
|
|
60
|
+
: "Error"}
|
|
61
|
+
</span>
|
|
62
|
+
{{else}}
|
|
46
63
|
<div
|
|
47
64
|
className={`h-2 w-2 rounded-full ${
|
|
48
65
|
healthCheck.data ? "bg-green-500" : "bg-red-500"
|
|
@@ -55,46 +72,10 @@ export default function Home() {
|
|
|
55
72
|
? "Connected"
|
|
56
73
|
: "Disconnected"}
|
|
57
74
|
</span>
|
|
75
|
+
{{/if}}
|
|
58
76
|
</div>
|
|
59
77
|
</section>
|
|
60
|
-
|
|
61
|
-
<section>
|
|
62
|
-
<h2 className="mb-3 font-medium">Core Features</h2>
|
|
63
|
-
<ul className="grid grid-cols-2 gap-3">
|
|
64
|
-
<FeatureItem
|
|
65
|
-
title="Type-Safe API"
|
|
66
|
-
description="End-to-end type safety with tRPC"
|
|
67
|
-
/>
|
|
68
|
-
<FeatureItem
|
|
69
|
-
title="Modern React"
|
|
70
|
-
description="TanStack Router + TanStack Query"
|
|
71
|
-
/>
|
|
72
|
-
<FeatureItem
|
|
73
|
-
title="Fast Backend"
|
|
74
|
-
description="Lightweight Hono server"
|
|
75
|
-
/>
|
|
76
|
-
<FeatureItem
|
|
77
|
-
title="Beautiful UI"
|
|
78
|
-
description="TailwindCSS + shadcn/ui components"
|
|
79
|
-
/>
|
|
80
|
-
</ul>
|
|
81
|
-
</section>
|
|
82
78
|
</div>
|
|
83
79
|
</div>
|
|
84
80
|
);
|
|
85
81
|
}
|
|
86
|
-
|
|
87
|
-
function FeatureItem({
|
|
88
|
-
title,
|
|
89
|
-
description,
|
|
90
|
-
}: {
|
|
91
|
-
title: string;
|
|
92
|
-
description: string;
|
|
93
|
-
}) {
|
|
94
|
-
return (
|
|
95
|
-
<li className="border-l-2 border-primary py-1 pl-3">
|
|
96
|
-
<h3 className="font-medium">{title}</h3>
|
|
97
|
-
<p className="text-sm text-muted-foreground">{description}</p>
|
|
98
|
-
</li>
|
|
99
|
-
);
|
|
100
|
-
}
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
"check-types": "tsc --noEmit"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@tanstack/react-query-devtools": "^5.69.0",
|
|
15
14
|
"@tanstack/react-router-devtools": "^1.114.27",
|
|
16
15
|
"@tanstack/router-plugin": "^1.114.27",
|
|
17
16
|
"@types/node": "^22.13.13",
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
"@radix-ui/react-slot": "^1.1.2",
|
|
31
30
|
"@tanstack/react-form": "^1.0.5",
|
|
32
31
|
"@tailwindcss/vite": "^4.0.15",
|
|
33
|
-
"@tanstack/react-query": "^5.69.0",
|
|
34
32
|
"@tanstack/react-router": "^1.114.25",
|
|
35
33
|
"class-variance-authority": "^0.7.1",
|
|
36
34
|
"clsx": "^2.1.1",
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
import { QueryClientProvider } from "@tanstack/react-query";
|
|
2
1
|
import { RouterProvider, createRouter } from "@tanstack/react-router";
|
|
3
2
|
import ReactDOM from "react-dom/client";
|
|
4
3
|
import Loader from "./components/loader";
|
|
5
4
|
import { routeTree } from "./routeTree.gen";
|
|
6
5
|
{{#if (eq api "orpc")}}
|
|
6
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
7
7
|
import { orpc, queryClient } from "./utils/orpc";
|
|
8
8
|
{{/if}}
|
|
9
9
|
{{#if (eq api "trpc")}}
|
|
10
|
+
import { QueryClientProvider } from "@tanstack/react-query";
|
|
10
11
|
import { queryClient, trpc } from "./utils/trpc";
|
|
11
12
|
{{/if}}
|
|
13
|
+
{{#if (eq backend "convex")}}
|
|
14
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
15
|
+
|
|
16
|
+
const convex = new ConvexReactClient(import.meta.env.VITE_CONVEX_URL as string);
|
|
17
|
+
{{/if}}
|
|
12
18
|
|
|
13
19
|
{{#if (eq api "orpc")}}
|
|
14
20
|
const router = createRouter({
|
|
@@ -36,8 +42,18 @@ const router = createRouter({
|
|
|
36
42
|
},
|
|
37
43
|
});
|
|
38
44
|
{{/if}}
|
|
45
|
+
{{#if (eq backend "convex")}}
|
|
46
|
+
const router = createRouter({
|
|
47
|
+
routeTree,
|
|
48
|
+
defaultPreload: "intent",
|
|
49
|
+
defaultPendingComponent: () => <Loader />,
|
|
50
|
+
context: {},
|
|
51
|
+
Wrap: function WrapComponent({ children }) {
|
|
52
|
+
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
{{/if}}
|
|
39
56
|
|
|
40
|
-
// Register things for typesafety
|
|
41
57
|
declare module "@tanstack/react-router" {
|
|
42
58
|
interface Register {
|
|
43
59
|
router: typeof router;
|
|
@@ -38,6 +38,9 @@ export interface RouterAppContext {
|
|
|
38
38
|
queryClient: QueryClient;
|
|
39
39
|
}
|
|
40
40
|
{{/if}}
|
|
41
|
+
{{#if (eq backend "convex")}}
|
|
42
|
+
export interface RouterAppContext {}
|
|
43
|
+
{{/if}}
|
|
41
44
|
|
|
42
45
|
export const Route = createRootRouteWithContext<RouterAppContext>()({
|
|
43
46
|
component: RootComponent,
|
|
@@ -106,4 +109,24 @@ function RootComponent() {
|
|
|
106
109
|
</>
|
|
107
110
|
);
|
|
108
111
|
}
|
|
109
|
-
{{/if}}
|
|
112
|
+
{{/if}}
|
|
113
|
+
{{#if (eq backend "convex")}}
|
|
114
|
+
function RootComponent() {
|
|
115
|
+
const isFetching = useRouterState({
|
|
116
|
+
select: (s) => s.isLoading,
|
|
117
|
+
});
|
|
118
|
+
return (
|
|
119
|
+
<>
|
|
120
|
+
<HeadContent />
|
|
121
|
+
<ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
|
|
122
|
+
<div className="grid grid-rows-[auto_1fr] h-svh">
|
|
123
|
+
<Header />
|
|
124
|
+
{isFetching ? <Loader /> : <Outlet />}
|
|
125
|
+
</div>
|
|
126
|
+
<Toaster richColors />
|
|
127
|
+
</ThemeProvider>
|
|
128
|
+
<TanStackRouterDevtools position="bottom-left" />
|
|
129
|
+
</>
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
{{/if}}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
1
2
|
{{#if (eq api "orpc")}}
|
|
2
3
|
import { orpc } from "@/utils/orpc";
|
|
4
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
5
|
{{/if}}
|
|
4
6
|
{{#if (eq api "trpc")}}
|
|
5
7
|
import { trpc } from "@/utils/trpc";
|
|
6
|
-
{{/if}}
|
|
7
8
|
import { useQuery } from "@tanstack/react-query";
|
|
8
|
-
|
|
9
|
+
{{/if}}
|
|
10
|
+
{{#if (eq backend "convex")}}
|
|
11
|
+
import { useQuery } from "convex/react";
|
|
12
|
+
import { api } from "@{{ projectName }}/backend/convex/_generated/api.js";
|
|
13
|
+
{{/if}}
|
|
9
14
|
|
|
10
15
|
export const Route = createFileRoute("/")({
|
|
11
16
|
component: HomeComponent,
|
|
@@ -34,6 +39,9 @@ function HomeComponent() {
|
|
|
34
39
|
{{#if (eq api "trpc")}}
|
|
35
40
|
const healthCheck = useQuery(trpc.healthCheck.queryOptions());
|
|
36
41
|
{{/if}}
|
|
42
|
+
{{#if (eq backend "convex")}}
|
|
43
|
+
const healthCheck = useQuery(api.healthCheck.get);
|
|
44
|
+
{{/if}}
|
|
37
45
|
|
|
38
46
|
return (
|
|
39
47
|
<div className="container mx-auto max-w-3xl px-4 py-2">
|
|
@@ -42,6 +50,7 @@ function HomeComponent() {
|
|
|
42
50
|
<section className="rounded-lg border p-4">
|
|
43
51
|
<h2 className="mb-2 font-medium">API Status</h2>
|
|
44
52
|
<div className="flex items-center gap-2">
|
|
53
|
+
{{#if (or (eq api "orpc") (eq api "trpc"))}}
|
|
45
54
|
<div
|
|
46
55
|
className={`h-2 w-2 rounded-full ${healthCheck.data ? "bg-green-500" : "bg-red-500"}`}
|
|
47
56
|
/>
|
|
@@ -52,46 +61,22 @@ function HomeComponent() {
|
|
|
52
61
|
? "Connected"
|
|
53
62
|
: "Disconnected"}
|
|
54
63
|
</span>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
<h2 className="mb-3 font-medium">Core Features</h2>
|
|
60
|
-
<ul className="grid grid-cols-2 gap-3">
|
|
61
|
-
<FeatureItem
|
|
62
|
-
title="Type-Safe API"
|
|
63
|
-
description="End-to-end type safety with tRPC"
|
|
64
|
-
/>
|
|
65
|
-
<FeatureItem
|
|
66
|
-
title="Modern React"
|
|
67
|
-
description="TanStack Router + TanStack Query"
|
|
68
|
-
/>
|
|
69
|
-
<FeatureItem
|
|
70
|
-
title="Fast Backend"
|
|
71
|
-
description="Lightweight Hono server"
|
|
72
|
-
/>
|
|
73
|
-
<FeatureItem
|
|
74
|
-
title="Beautiful UI"
|
|
75
|
-
description="TailwindCSS + shadcn/ui components"
|
|
64
|
+
{{/if}}
|
|
65
|
+
{{#if (eq backend "convex")}}
|
|
66
|
+
<div
|
|
67
|
+
className={`h-2 w-2 rounded-full ${healthCheck === "OK" ? "bg-green-500" : healthCheck === undefined ? "bg-orange-400" : "bg-red-500"}`}
|
|
76
68
|
/>
|
|
77
|
-
|
|
69
|
+
<span className="text-sm text-muted-foreground">
|
|
70
|
+
{healthCheck === undefined
|
|
71
|
+
? "Checking..."
|
|
72
|
+
: healthCheck === "OK"
|
|
73
|
+
? "Connected"
|
|
74
|
+
: "Error"}
|
|
75
|
+
</span>
|
|
76
|
+
{{/if}}
|
|
77
|
+
</div>
|
|
78
78
|
</section>
|
|
79
79
|
</div>
|
|
80
80
|
</div>
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
function FeatureItem({
|
|
85
|
-
title,
|
|
86
|
-
description,
|
|
87
|
-
}: {
|
|
88
|
-
title: string;
|
|
89
|
-
description: string;
|
|
90
|
-
}) {
|
|
91
|
-
return (
|
|
92
|
-
<li className="border-l-2 border-primary py-1 pl-3">
|
|
93
|
-
<h3 className="font-medium">{title}</h3>
|
|
94
|
-
<p className="text-sm text-muted-foreground">{description}</p>
|
|
95
|
-
</li>
|
|
96
|
-
);
|
|
97
|
-
}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
{{#if (eq backend "convex")}}
|
|
2
|
+
import { createRouter as createTanStackRouter } from "@tanstack/react-router";
|
|
3
|
+
import { QueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { routerWithQueryClient } from "@tanstack/react-router-with-query";
|
|
5
|
+
import { ConvexQueryClient } from "@convex-dev/react-query";
|
|
6
|
+
import { ConvexProvider } from "convex/react";
|
|
7
|
+
import { routeTree } from "./routeTree.gen";
|
|
8
|
+
import Loader from "./components/loader";
|
|
9
|
+
import "./index.css";
|
|
10
|
+
{{else}}
|
|
1
11
|
import {
|
|
2
12
|
QueryCache,
|
|
3
13
|
QueryClient,
|
|
@@ -7,18 +17,56 @@ import { createRouter as createTanstackRouter } from "@tanstack/react-router";
|
|
|
7
17
|
import Loader from "./components/loader";
|
|
8
18
|
import "./index.css";
|
|
9
19
|
import { routeTree } from "./routeTree.gen";
|
|
10
|
-
{{#if (eq api "trpc")}}
|
|
20
|
+
{{#if (eq api "trpc")}}
|
|
11
21
|
import { createTRPCClient, httpBatchLink } from "@trpc/client";
|
|
12
22
|
import { createTRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
|
13
23
|
import { toast } from "sonner";
|
|
14
24
|
import type { AppRouter } from "../../server/src/routers";
|
|
15
25
|
import { TRPCProvider } from "./utils/trpc";
|
|
16
|
-
{{/if}}
|
|
17
|
-
{{#if (eq api "orpc")}}
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{#if (eq api "orpc")}}
|
|
18
28
|
import { orpc, ORPCContext, queryClient } from "./utils/orpc";
|
|
29
|
+
{{/if}}
|
|
19
30
|
{{/if}}
|
|
20
31
|
|
|
21
|
-
{{#if (eq
|
|
32
|
+
{{#if (eq backend "convex")}}
|
|
33
|
+
export function createRouter() {
|
|
34
|
+
const CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL!;
|
|
35
|
+
if (!CONVEX_URL) {
|
|
36
|
+
console.error("missing envar VITE_CONVEX_URL");
|
|
37
|
+
}
|
|
38
|
+
const convexQueryClient = new ConvexQueryClient(CONVEX_URL);
|
|
39
|
+
|
|
40
|
+
const queryClient: QueryClient = new QueryClient({
|
|
41
|
+
defaultOptions: {
|
|
42
|
+
queries: {
|
|
43
|
+
queryKeyHashFn: convexQueryClient.hashFn(),
|
|
44
|
+
queryFn: convexQueryClient.queryFn(),
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
convexQueryClient.connect(queryClient);
|
|
49
|
+
|
|
50
|
+
const router = routerWithQueryClient(
|
|
51
|
+
createTanStackRouter({
|
|
52
|
+
routeTree,
|
|
53
|
+
defaultPreload: "intent",
|
|
54
|
+
defaultPendingComponent: () => <Loader />,
|
|
55
|
+
defaultNotFoundComponent: () => <div>Not Found</div>,
|
|
56
|
+
context: { queryClient },
|
|
57
|
+
Wrap: ({ children }) => (
|
|
58
|
+
<ConvexProvider client={convexQueryClient.convexClient}>
|
|
59
|
+
{children}
|
|
60
|
+
</ConvexProvider>
|
|
61
|
+
),
|
|
62
|
+
}),
|
|
63
|
+
queryClient,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
return router;
|
|
67
|
+
}
|
|
68
|
+
{{else}}
|
|
69
|
+
{{#if (eq api "trpc")}}
|
|
22
70
|
export const queryClient = new QueryClient({
|
|
23
71
|
queryCache: new QueryCache({
|
|
24
72
|
onError: (error) => {
|
|
@@ -38,11 +86,7 @@ export const queryClient = new QueryClient({
|
|
|
38
86
|
const trpcClient = createTRPCClient<AppRouter>({
|
|
39
87
|
links: [
|
|
40
88
|
httpBatchLink({
|
|
41
|
-
{{#if (includes frontend 'next')}}
|
|
42
|
-
url: `${process.env.NEXT_PUBLIC_SERVER_URL}/trpc`,
|
|
43
|
-
{{else}}
|
|
44
89
|
url: `${import.meta.env.VITE_SERVER_URL}/trpc`,
|
|
45
|
-
{{/if}}
|
|
46
90
|
{{#if auth}}
|
|
47
91
|
fetch(url, options) {
|
|
48
92
|
return fetch(url, {
|
|
@@ -59,8 +103,7 @@ const trpc = createTRPCOptionsProxy({
|
|
|
59
103
|
client: trpcClient,
|
|
60
104
|
queryClient: queryClient,
|
|
61
105
|
});
|
|
62
|
-
{{/if}}
|
|
63
|
-
|
|
106
|
+
{{/if}}
|
|
64
107
|
|
|
65
108
|
export const createRouter = () => {
|
|
66
109
|
const router = createTanstackRouter({
|
|
@@ -69,10 +112,10 @@ export const createRouter = () => {
|
|
|
69
112
|
defaultPreloadStaleTime: 0,
|
|
70
113
|
{{#if (eq api "trpc")}}
|
|
71
114
|
context: { trpc, queryClient },
|
|
72
|
-
{{/if}}
|
|
73
|
-
{{#if (eq api "orpc")}}
|
|
115
|
+
{{/if}}
|
|
116
|
+
{{#if (eq api "orpc")}}
|
|
74
117
|
context: { orpc, queryClient },
|
|
75
|
-
{{/if}}
|
|
118
|
+
{{/if}}
|
|
76
119
|
defaultPendingComponent: () => <Loader />,
|
|
77
120
|
defaultNotFoundComponent: () => <div>Not Found</div>,
|
|
78
121
|
Wrap: ({ children }) => (
|
|
@@ -93,6 +136,7 @@ export const createRouter = () => {
|
|
|
93
136
|
|
|
94
137
|
return router;
|
|
95
138
|
};
|
|
139
|
+
{{/if}}
|
|
96
140
|
|
|
97
141
|
// Register the router instance for type safety
|
|
98
142
|
declare module "@tanstack/react-router" {
|
|
@@ -11,26 +11,28 @@ import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
|
|
|
11
11
|
import Header from "../components/header";
|
|
12
12
|
import appCss from "../index.css?url";
|
|
13
13
|
import type { QueryClient } from "@tanstack/react-query";
|
|
14
|
-
{{#if (eq api "trpc")}}
|
|
15
|
-
import type { TRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
|
16
|
-
import type { AppRouter } from "../../../server/src/routers";
|
|
17
|
-
{{/if}}
|
|
18
|
-
{{#if (eq api "orpc")}}
|
|
19
|
-
import type { orpc } from "@/utils/orpc";
|
|
20
|
-
{{/if}}
|
|
21
14
|
import Loader from "@/components/loader";
|
|
22
15
|
|
|
23
|
-
{{#if (eq
|
|
16
|
+
{{#if (eq backend "convex")}}
|
|
17
|
+
export interface RouterAppContext {
|
|
18
|
+
queryClient: QueryClient;
|
|
19
|
+
}
|
|
20
|
+
{{else}}
|
|
21
|
+
{{#if (eq api "trpc")}}
|
|
22
|
+
import type { TRPCOptionsProxy } from "@trpc/tanstack-react-query";
|
|
23
|
+
import type { AppRouter } from "../../../server/src/routers";
|
|
24
24
|
export interface RouterAppContext {
|
|
25
25
|
trpc: TRPCOptionsProxy<AppRouter>;
|
|
26
26
|
queryClient: QueryClient;
|
|
27
27
|
}
|
|
28
|
-
{{/if}}
|
|
29
|
-
{{#if (eq api "orpc")}}
|
|
28
|
+
{{/if}}
|
|
29
|
+
{{#if (eq api "orpc")}}
|
|
30
|
+
import type { orpc } from "@/utils/orpc";
|
|
30
31
|
export interface RouterAppContext {
|
|
31
32
|
orpc: typeof orpc;
|
|
32
33
|
queryClient: QueryClient;
|
|
33
34
|
}
|
|
35
|
+
{{/if}}
|
|
34
36
|
{{/if}}
|
|
35
37
|
|
|
36
38
|
export const Route = createRootRouteWithContext<RouterAppContext>()({
|