create-base-stack 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +15 -0
  2. package/bin/index.ts +26 -0
  3. package/package.json +12 -0
  4. package/template/.bun-version +1 -0
  5. package/template/README.md +15 -0
  6. package/template/apps/api/README.md +11 -0
  7. package/template/apps/api/package.json +25 -0
  8. package/template/apps/api/src/index.ts +19 -0
  9. package/template/apps/api/src/lib/rpc.ts +8 -0
  10. package/template/apps/api/src/lib/utils.ts +44 -0
  11. package/template/apps/api/src/middleware/cors.ts +7 -0
  12. package/template/apps/api/src/middleware/error.ts +28 -0
  13. package/template/apps/api/tsconfig.json +28 -0
  14. package/template/apps/api/tsdown.config.ts +6 -0
  15. package/template/apps/app/README.md +73 -0
  16. package/template/apps/app/components.json +22 -0
  17. package/template/apps/app/eslint.config.js +23 -0
  18. package/template/apps/app/index.html +13 -0
  19. package/template/apps/app/package.json +45 -0
  20. package/template/apps/app/public/vite.svg +1 -0
  21. package/template/apps/app/src/assets/react.svg +1 -0
  22. package/template/apps/app/src/components/provider/query-provider.tsx +26 -0
  23. package/template/apps/app/src/components/provider/theme-provider.tsx +73 -0
  24. package/template/apps/app/src/components/ui/button.tsx +62 -0
  25. package/template/apps/app/src/components/ui/card.tsx +92 -0
  26. package/template/apps/app/src/index.css +123 -0
  27. package/template/apps/app/src/lib/api-client.ts +7 -0
  28. package/template/apps/app/src/lib/utils.ts +6 -0
  29. package/template/apps/app/src/main.tsx +37 -0
  30. package/template/apps/app/src/routeTree.gen.ts +59 -0
  31. package/template/apps/app/src/routes/__root.tsx +16 -0
  32. package/template/apps/app/src/routes/index.tsx +69 -0
  33. package/template/apps/app/tsconfig.app.json +30 -0
  34. package/template/apps/app/tsconfig.json +17 -0
  35. package/template/apps/app/tsconfig.node.json +26 -0
  36. package/template/apps/app/vite.config.ts +23 -0
  37. package/template/biome.json +44 -0
  38. package/template/bun.lock +930 -0
  39. package/template/bunfig.toml +2 -0
  40. package/template/lefthook.yml +13 -0
  41. package/template/package.json +29 -0
  42. package/template/packages/shared/README.md +15 -0
  43. package/template/packages/shared/package.json +25 -0
  44. package/template/packages/shared/src/index.ts +2 -0
  45. package/template/packages/shared/src/lib/errors.ts +43 -0
  46. package/template/packages/shared/src/lib/types.ts +24 -0
  47. package/template/packages/shared/tsconfig.json +28 -0
  48. package/template/packages/shared/tsdown.config.ts +5 -0
  49. package/template/turbo.json +77 -0
@@ -0,0 +1,92 @@
1
+ import type * as React from "react";
2
+
3
+ import { cn } from "@/lib/utils";
4
+
5
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
6
+ return (
7
+ <div
8
+ data-slot="card"
9
+ className={cn(
10
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
11
+ className,
12
+ )}
13
+ {...props}
14
+ />
15
+ );
16
+ }
17
+
18
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
19
+ return (
20
+ <div
21
+ data-slot="card-header"
22
+ className={cn(
23
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
24
+ className,
25
+ )}
26
+ {...props}
27
+ />
28
+ );
29
+ }
30
+
31
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
32
+ return (
33
+ <div
34
+ data-slot="card-title"
35
+ className={cn("leading-none font-semibold", className)}
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
42
+ return (
43
+ <div
44
+ data-slot="card-description"
45
+ className={cn("text-muted-foreground text-sm", className)}
46
+ {...props}
47
+ />
48
+ );
49
+ }
50
+
51
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
52
+ return (
53
+ <div
54
+ data-slot="card-action"
55
+ className={cn(
56
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
57
+ className,
58
+ )}
59
+ {...props}
60
+ />
61
+ );
62
+ }
63
+
64
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
65
+ return (
66
+ <div
67
+ data-slot="card-content"
68
+ className={cn("px-6", className)}
69
+ {...props}
70
+ />
71
+ );
72
+ }
73
+
74
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
75
+ return (
76
+ <div
77
+ data-slot="card-footer"
78
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
79
+ {...props}
80
+ />
81
+ );
82
+ }
83
+
84
+ export {
85
+ Card,
86
+ CardHeader,
87
+ CardFooter,
88
+ CardTitle,
89
+ CardAction,
90
+ CardDescription,
91
+ CardContent,
92
+ };
@@ -0,0 +1,123 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+
4
+ @custom-variant dark (&:is(.dark *));
5
+
6
+ @theme inline {
7
+ --radius-sm: calc(var(--radius) - 4px);
8
+ --radius-md: calc(var(--radius) - 2px);
9
+ --radius-lg: var(--radius);
10
+ --radius-xl: calc(var(--radius) + 4px);
11
+ --radius-2xl: calc(var(--radius) + 8px);
12
+ --radius-3xl: calc(var(--radius) + 12px);
13
+ --radius-4xl: calc(var(--radius) + 16px);
14
+ --color-background: var(--background);
15
+ --color-foreground: var(--foreground);
16
+ --color-card: var(--card);
17
+ --color-card-foreground: var(--card-foreground);
18
+ --color-popover: var(--popover);
19
+ --color-popover-foreground: var(--popover-foreground);
20
+ --color-primary: var(--primary);
21
+ --color-primary-foreground: var(--primary-foreground);
22
+ --color-secondary: var(--secondary);
23
+ --color-secondary-foreground: var(--secondary-foreground);
24
+ --color-muted: var(--muted);
25
+ --color-muted-foreground: var(--muted-foreground);
26
+ --color-accent: var(--accent);
27
+ --color-accent-foreground: var(--accent-foreground);
28
+ --color-destructive: var(--destructive);
29
+ --color-border: var(--border);
30
+ --color-input: var(--input);
31
+ --color-ring: var(--ring);
32
+ --color-chart-1: var(--chart-1);
33
+ --color-chart-2: var(--chart-2);
34
+ --color-chart-3: var(--chart-3);
35
+ --color-chart-4: var(--chart-4);
36
+ --color-chart-5: var(--chart-5);
37
+ --color-sidebar: var(--sidebar);
38
+ --color-sidebar-foreground: var(--sidebar-foreground);
39
+ --color-sidebar-primary: var(--sidebar-primary);
40
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
41
+ --color-sidebar-accent: var(--sidebar-accent);
42
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
43
+ --color-sidebar-border: var(--sidebar-border);
44
+ --color-sidebar-ring: var(--sidebar-ring);
45
+ }
46
+
47
+ :root {
48
+ --radius: 0.625rem;
49
+ --background: oklch(1 0 0);
50
+ --foreground: oklch(0.145 0 0);
51
+ --card: oklch(1 0 0);
52
+ --card-foreground: oklch(0.145 0 0);
53
+ --popover: oklch(1 0 0);
54
+ --popover-foreground: oklch(0.145 0 0);
55
+ --primary: oklch(0.205 0 0);
56
+ --primary-foreground: oklch(0.985 0 0);
57
+ --secondary: oklch(0.97 0 0);
58
+ --secondary-foreground: oklch(0.205 0 0);
59
+ --muted: oklch(0.97 0 0);
60
+ --muted-foreground: oklch(0.556 0 0);
61
+ --accent: oklch(0.97 0 0);
62
+ --accent-foreground: oklch(0.205 0 0);
63
+ --destructive: oklch(0.577 0.245 27.325);
64
+ --border: oklch(0.922 0 0);
65
+ --input: oklch(0.922 0 0);
66
+ --ring: oklch(0.708 0 0);
67
+ --chart-1: oklch(0.646 0.222 41.116);
68
+ --chart-2: oklch(0.6 0.118 184.704);
69
+ --chart-3: oklch(0.398 0.07 227.392);
70
+ --chart-4: oklch(0.828 0.189 84.429);
71
+ --chart-5: oklch(0.769 0.188 70.08);
72
+ --sidebar: oklch(0.985 0 0);
73
+ --sidebar-foreground: oklch(0.145 0 0);
74
+ --sidebar-primary: oklch(0.205 0 0);
75
+ --sidebar-primary-foreground: oklch(0.985 0 0);
76
+ --sidebar-accent: oklch(0.97 0 0);
77
+ --sidebar-accent-foreground: oklch(0.205 0 0);
78
+ --sidebar-border: oklch(0.922 0 0);
79
+ --sidebar-ring: oklch(0.708 0 0);
80
+ }
81
+
82
+ .dark {
83
+ --background: oklch(0.145 0 0);
84
+ --foreground: oklch(0.985 0 0);
85
+ --card: oklch(0.205 0 0);
86
+ --card-foreground: oklch(0.985 0 0);
87
+ --popover: oklch(0.205 0 0);
88
+ --popover-foreground: oklch(0.985 0 0);
89
+ --primary: oklch(0.922 0 0);
90
+ --primary-foreground: oklch(0.205 0 0);
91
+ --secondary: oklch(0.269 0 0);
92
+ --secondary-foreground: oklch(0.985 0 0);
93
+ --muted: oklch(0.269 0 0);
94
+ --muted-foreground: oklch(0.708 0 0);
95
+ --accent: oklch(0.269 0 0);
96
+ --accent-foreground: oklch(0.985 0 0);
97
+ --destructive: oklch(0.704 0.191 22.216);
98
+ --border: oklch(1 0 0 / 10%);
99
+ --input: oklch(1 0 0 / 15%);
100
+ --ring: oklch(0.556 0 0);
101
+ --chart-1: oklch(0.488 0.243 264.376);
102
+ --chart-2: oklch(0.696 0.17 162.48);
103
+ --chart-3: oklch(0.769 0.188 70.08);
104
+ --chart-4: oklch(0.627 0.265 303.9);
105
+ --chart-5: oklch(0.645 0.246 16.439);
106
+ --sidebar: oklch(0.205 0 0);
107
+ --sidebar-foreground: oklch(0.985 0 0);
108
+ --sidebar-primary: oklch(0.488 0.243 264.376);
109
+ --sidebar-primary-foreground: oklch(0.985 0 0);
110
+ --sidebar-accent: oklch(0.269 0 0);
111
+ --sidebar-accent-foreground: oklch(0.985 0 0);
112
+ --sidebar-border: oklch(1 0 0 / 10%);
113
+ --sidebar-ring: oklch(0.556 0 0);
114
+ }
115
+
116
+ @layer base {
117
+ * {
118
+ @apply border-border outline-ring/50;
119
+ }
120
+ body {
121
+ @apply bg-background text-foreground;
122
+ }
123
+ }
@@ -0,0 +1,7 @@
1
+ import { hcWithType } from "@base/api";
2
+
3
+ export const apiClient = hcWithType("http://localhost:3000", {
4
+ init: {
5
+ credentials: "include",
6
+ },
7
+ }).api.v1;
@@ -0,0 +1,6 @@
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs));
6
+ }
@@ -0,0 +1,37 @@
1
+ import { createRouter, RouterProvider } from "@tanstack/react-router";
2
+ import { StrictMode } from "react";
3
+ import ReactDOM from "react-dom/client";
4
+ import "./index.css";
5
+
6
+ import { QueryClient } from "@tanstack/react-query";
7
+ import QueryProvider from "./components/provider/query-provider";
8
+ import ThemeProvider from "./components/provider/theme-provider";
9
+ // Import the generated route tree
10
+ import { routeTree } from "./routeTree.gen";
11
+
12
+ const queryClient = new QueryClient();
13
+
14
+ // Create a new router instance
15
+ const router = createRouter({ routeTree, context: { queryClient } });
16
+
17
+ // Register the router instance for type safety
18
+ declare module "@tanstack/react-router" {
19
+ interface Register {
20
+ router: typeof router;
21
+ }
22
+ }
23
+
24
+ // Render the app
25
+ const rootElement = document.getElementById("root")!;
26
+ if (!rootElement.innerHTML) {
27
+ const root = ReactDOM.createRoot(rootElement);
28
+ root.render(
29
+ <StrictMode>
30
+ <QueryProvider>
31
+ <ThemeProvider defaultTheme="light" storageKey="vite-ui-theme">
32
+ <RouterProvider router={router} />
33
+ </ThemeProvider>
34
+ </QueryProvider>
35
+ </StrictMode>,
36
+ );
37
+ }
@@ -0,0 +1,59 @@
1
+ /* eslint-disable */
2
+
3
+ // @ts-nocheck
4
+
5
+ // noinspection JSUnusedGlobalSymbols
6
+
7
+ // This file was automatically generated by TanStack Router.
8
+ // You should NOT make any changes in this file as it will be overwritten.
9
+ // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10
+
11
+ import { Route as rootRouteImport } from './routes/__root'
12
+ import { Route as IndexRouteImport } from './routes/index'
13
+
14
+ const IndexRoute = IndexRouteImport.update({
15
+ id: '/',
16
+ path: '/',
17
+ getParentRoute: () => rootRouteImport,
18
+ } as any)
19
+
20
+ export interface FileRoutesByFullPath {
21
+ '/': typeof IndexRoute
22
+ }
23
+ export interface FileRoutesByTo {
24
+ '/': typeof IndexRoute
25
+ }
26
+ export interface FileRoutesById {
27
+ __root__: typeof rootRouteImport
28
+ '/': typeof IndexRoute
29
+ }
30
+ export interface FileRouteTypes {
31
+ fileRoutesByFullPath: FileRoutesByFullPath
32
+ fullPaths: '/'
33
+ fileRoutesByTo: FileRoutesByTo
34
+ to: '/'
35
+ id: '__root__' | '/'
36
+ fileRoutesById: FileRoutesById
37
+ }
38
+ export interface RootRouteChildren {
39
+ IndexRoute: typeof IndexRoute
40
+ }
41
+
42
+ declare module '@tanstack/react-router' {
43
+ interface FileRoutesByPath {
44
+ '/': {
45
+ id: '/'
46
+ path: '/'
47
+ fullPath: '/'
48
+ preLoaderRoute: typeof IndexRouteImport
49
+ parentRoute: typeof rootRouteImport
50
+ }
51
+ }
52
+ }
53
+
54
+ const rootRouteChildren: RootRouteChildren = {
55
+ IndexRoute: IndexRoute,
56
+ }
57
+ export const routeTree = rootRouteImport
58
+ ._addFileChildren(rootRouteChildren)
59
+ ._addFileTypes<FileRouteTypes>()
@@ -0,0 +1,16 @@
1
+ import type { QueryClient } from "@tanstack/react-query";
2
+ import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
3
+
4
+ interface RootContext {
5
+ queryClient: QueryClient;
6
+ }
7
+
8
+ const RootLayout = () => (
9
+ <>
10
+ <Outlet />
11
+ </>
12
+ );
13
+
14
+ export const Route = createRootRouteWithContext<RootContext>()({
15
+ component: RootLayout,
16
+ });
@@ -0,0 +1,69 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+ import { createFileRoute } from "@tanstack/react-router";
3
+ import { Loader2 } from "lucide-react";
4
+ import { Button } from "@/components/ui/button";
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "@/components/ui/card";
12
+ import { apiClient } from "@/lib/api-client";
13
+
14
+ export const Route = createFileRoute("/")({
15
+ component: Index,
16
+ });
17
+
18
+ function Index() {
19
+ const { data, isLoading, refetch, isFetching } = useQuery({
20
+ queryKey: ["api-data"],
21
+ queryFn: async () => {
22
+ const res = await apiClient.$get();
23
+ return await res.json();
24
+ },
25
+ enabled: false,
26
+ });
27
+
28
+ return (
29
+ <div className="flex h-screen w-full flex-col items-center justify-center gap-4 p-4">
30
+ <header className="text-center">
31
+ <h1 className="text-4xl font-bold tracking-tight">Base Stack</h1>
32
+ <p className="text-muted-foreground mt-1">
33
+ A full-stack TypeScript boilerplate with Hono and Vite.
34
+ </p>
35
+ </header>
36
+
37
+ <Button
38
+ onClick={() => refetch()}
39
+ disabled={isFetching}
40
+ className="min-w-30"
41
+ >
42
+ {isFetching ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : null}
43
+ {data ? "Refresh Data" : "Connect"}
44
+ </Button>
45
+
46
+ <Card className="w-full max-w-lg">
47
+ <CardHeader>
48
+ <CardTitle>API Response</CardTitle>
49
+ <CardDescription>GET /api/v1/endpoint</CardDescription>
50
+ </CardHeader>
51
+ <CardContent>
52
+ <div className="bg-muted rounded-lg p-4 font-mono text-sm">
53
+ {data ? (
54
+ <pre className="overflow-auto max-h-75">
55
+ {JSON.stringify(data, null, 2)}
56
+ </pre>
57
+ ) : (
58
+ <p className="text-muted-foreground italic">
59
+ {isLoading
60
+ ? "Fetching..."
61
+ : "Click connect to fetch data from the API"}
62
+ </p>
63
+ )}
64
+ </div>
65
+ </CardContent>
66
+ </Card>
67
+ </div>
68
+ );
69
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
+ "target": "ES2022",
5
+ "useDefineForClassFields": true,
6
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
+ "module": "ESNext",
8
+ "types": ["vite/client"],
9
+ "skipLibCheck": true,
10
+ "baseUrl": ".",
11
+ "paths": {
12
+ "@/*": ["./src/*"]
13
+ },
14
+ /* Bundler mode */
15
+ "moduleResolution": "bundler",
16
+ "allowImportingTsExtensions": true,
17
+ "verbatimModuleSyntax": true,
18
+ "moduleDetection": "force",
19
+ "noEmit": true,
20
+ "jsx": "react-jsx",
21
+ /* Linting */
22
+ "strict": true,
23
+ "noUnusedLocals": true,
24
+ "noUnusedParameters": true,
25
+ "erasableSyntaxOnly": true,
26
+ "noFallthroughCasesInSwitch": true,
27
+ "noUncheckedSideEffectImports": true
28
+ },
29
+ "include": ["src"]
30
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "files": [],
3
+ "references": [
4
+ {
5
+ "path": "./tsconfig.app.json"
6
+ },
7
+ {
8
+ "path": "./tsconfig.node.json"
9
+ }
10
+ ],
11
+ "compilerOptions": {
12
+ "baseUrl": ".",
13
+ "paths": {
14
+ "@/*": ["./src/*"]
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
+ "target": "ES2023",
5
+ "lib": ["ES2023"],
6
+ "module": "ESNext",
7
+ "types": ["node"],
8
+ "skipLibCheck": true,
9
+
10
+ /* Bundler mode */
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "moduleDetection": "force",
15
+ "noEmit": true,
16
+
17
+ /* Linting */
18
+ "strict": true,
19
+ "noUnusedLocals": true,
20
+ "noUnusedParameters": true,
21
+ "erasableSyntaxOnly": true,
22
+ "noFallthroughCasesInSwitch": true,
23
+ "noUncheckedSideEffectImports": true
24
+ },
25
+ "include": ["vite.config.ts"]
26
+ }
@@ -0,0 +1,23 @@
1
+ import path from "node:path";
2
+ import tailwindcss from "@tailwindcss/vite";
3
+ import { tanstackRouter } from "@tanstack/router-plugin/vite";
4
+ import react from "@vitejs/plugin-react";
5
+ import { defineConfig } from "vite";
6
+
7
+ // https://vite.dev/config/
8
+ export default defineConfig({
9
+ envDir: path.resolve(process.cwd(), "../../"),
10
+ plugins: [
11
+ tanstackRouter({
12
+ target: "react",
13
+ autoCodeSplitting: true,
14
+ }),
15
+ react(),
16
+ tailwindcss(),
17
+ ],
18
+ resolve: {
19
+ alias: {
20
+ "@": path.resolve(__dirname, "./src"),
21
+ },
22
+ },
23
+ });
@@ -0,0 +1,44 @@
1
+ {
2
+ "$schema": "https://biomejs.dev/schemas/2.3.10/schema.json",
3
+ "vcs": {
4
+ "enabled": true,
5
+ "clientKind": "git",
6
+ "useIgnoreFile": true
7
+ },
8
+ "files": {
9
+ "ignoreUnknown": false
10
+ },
11
+ "formatter": {
12
+ "enabled": true,
13
+ "indentStyle": "space",
14
+ "indentWidth": 2
15
+ },
16
+ "linter": {
17
+ "enabled": true,
18
+ "rules": {
19
+ "recommended": true
20
+ },
21
+ "domains": {
22
+ "next": "recommended",
23
+ "react": "recommended"
24
+ }
25
+ },
26
+ "javascript": {
27
+ "formatter": {
28
+ "quoteStyle": "double"
29
+ }
30
+ },
31
+ "css": {
32
+ "parser": {
33
+ "tailwindDirectives": true
34
+ }
35
+ },
36
+ "assist": {
37
+ "enabled": true,
38
+ "actions": {
39
+ "source": {
40
+ "organizeImports": "on"
41
+ }
42
+ }
43
+ }
44
+ }