create-better-t-stack 2.26.1 → 2.26.3

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 CHANGED
@@ -49,14 +49,16 @@ const DEFAULT_CONFIG = {
49
49
  webDeploy: "none"
50
50
  };
51
51
  const dependencyVersionMap = {
52
- "better-auth": "^1.2.10",
53
- "@better-auth/expo": "^1.2.10",
52
+ "better-auth": "^1.3.0",
53
+ "@better-auth/expo": "^1.3.0",
54
54
  "drizzle-orm": "^0.44.2",
55
55
  "drizzle-kit": "^0.31.2",
56
56
  "@libsql/client": "^0.15.9",
57
57
  "@neondatabase/serverless": "^1.0.1",
58
58
  pg: "^8.14.1",
59
59
  "@types/pg": "^8.11.11",
60
+ "@types/ws": "^8.18.1",
61
+ ws: "^8.18.3",
60
62
  mysql2: "^3.14.0",
61
63
  "@prisma/client": "^6.9.0",
62
64
  prisma: "^6.9.0",
@@ -2019,7 +2021,6 @@ async function processAndCopyFiles(sourcePattern, baseSourceDir, destDir, contex
2019
2021
  async function copyBaseTemplate(projectDir, context) {
2020
2022
  const templateDir = path.join(PKG_ROOT, "templates/base");
2021
2023
  await processAndCopyFiles(["**/*"], templateDir, projectDir, context);
2022
- await fs.ensureDir(path.join(projectDir, "packages"));
2023
2024
  }
2024
2025
  async function setupFrontendTemplates(projectDir, context) {
2025
2026
  const hasReactWeb = context.frontend.some((f) => [
@@ -3954,8 +3955,12 @@ async function setupDatabase(config) {
3954
3955
  projectDir: serverDir
3955
3956
  });
3956
3957
  else if (database === "postgres") if (dbSetup === "neon") await addPackageDependency({
3957
- dependencies: ["drizzle-orm", "@neondatabase/serverless"],
3958
- devDependencies: ["drizzle-kit"],
3958
+ dependencies: [
3959
+ "drizzle-orm",
3960
+ "@neondatabase/serverless",
3961
+ "ws"
3962
+ ],
3963
+ devDependencies: ["drizzle-kit", "@types/ws"],
3959
3964
  projectDir: serverDir
3960
3965
  });
3961
3966
  else await addPackageDependency({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.26.1",
3
+ "version": "2.26.3",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,7 +1,13 @@
1
1
  {{#if (or (eq runtime "bun") (eq runtime "node"))}}
2
2
  {{#if (eq dbSetup "neon")}}
3
- import { neon } from '@neondatabase/serverless';
3
+ import { neon, neonConfig } from '@neondatabase/serverless';
4
4
  import { drizzle } from 'drizzle-orm/neon-http';
5
+ import ws from "ws";
6
+
7
+ neonConfig.webSocketConstructor = ws;
8
+
9
+ // To work in edge environments (Cloudflare Workers, Vercel Edge, etc.), enable querying over fetch
10
+ // neonConfig.poolQueryViaFetch = true
5
11
 
6
12
  const sql = neon(process.env.DATABASE_URL || "");
7
13
  export const db = drizzle(sql);
@@ -14,9 +20,13 @@ export const db = drizzle(process.env.DATABASE_URL || "");
14
20
 
15
21
  {{#if (eq runtime "workers")}}
16
22
  {{#if (eq dbSetup "neon")}}
17
- import { neon } from '@neondatabase/serverless';
23
+ import { neon, neonConfig } from '@neondatabase/serverless';
18
24
  import { drizzle } from 'drizzle-orm/neon-http';
19
25
  import { env } from "cloudflare:workers";
26
+ import ws from "ws";
27
+
28
+ neonConfig.webSocketConstructor = ws;
29
+ neonConfig.poolQueryViaFetch = true;
20
30
 
21
31
  const sql = neon(env.DATABASE_URL || "");
22
32
  export const db = drizzle(sql);
@@ -1,73 +1,11 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
2
-
3
- type Theme = "dark" | "light" | "system";
4
-
5
- type ThemeProviderProps = {
6
- children: React.ReactNode;
7
- defaultTheme?: Theme;
8
- storageKey?: string;
9
- };
10
-
11
- type ThemeProviderState = {
12
- theme: Theme;
13
- setTheme: (theme: Theme) => void;
14
- };
15
-
16
- const initialState: ThemeProviderState = {
17
- theme: "system",
18
- setTheme: () => null,
19
- };
20
-
21
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
1
+ import * as React from "react";
2
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
22
3
 
23
4
  export function ThemeProvider({
24
5
  children,
25
- defaultTheme = "system",
26
- storageKey = "vite-ui-theme",
27
6
  ...props
28
- }: ThemeProviderProps) {
29
- const [theme, setTheme] = useState<Theme>(
30
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
- );
32
-
33
- useEffect(() => {
34
- const root = window.document.documentElement;
35
-
36
- root.classList.remove("light", "dark");
37
-
38
- if (theme === "system") {
39
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
40
- .matches
41
- ? "dark"
42
- : "light";
43
-
44
- root.classList.add(systemTheme);
45
- return;
46
- }
47
-
48
- root.classList.add(theme);
49
- }, [theme]);
50
-
51
- const value = {
52
- theme,
53
- setTheme: (theme: Theme) => {
54
- localStorage.setItem(storageKey, theme);
55
- setTheme(theme);
56
- },
57
- };
58
-
59
- return (
60
- <ThemeProviderContext.Provider {...props} value={value}>
61
- {children}
62
- </ThemeProviderContext.Provider>
63
- );
7
+ }: React.ComponentProps<typeof NextThemesProvider>) {
8
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
64
9
  }
65
10
 
66
- export const useTheme = () => {
67
- const context = useContext(ThemeProviderContext);
68
-
69
- if (context === undefined)
70
- throw new Error("useTheme must be used within a ThemeProvider");
71
-
72
- return context;
73
- };
11
+ export { useTheme } from "next-themes";
@@ -15,31 +15,25 @@ import { Toaster } from "./components/ui/sonner";
15
15
  {{#if (eq backend "convex")}}
16
16
  import { ConvexProvider, ConvexReactClient } from "convex/react";
17
17
  {{else}}
18
- {{#unless (eq api "none")}}
18
+ {{#unless (eq api "none")}}
19
19
  import { QueryClientProvider } from "@tanstack/react-query";
20
20
  import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
21
- {{#if (eq api "orpc")}}
21
+ {{#if (eq api "orpc")}}
22
22
  import { queryClient } from "./utils/orpc";
23
- {{/if}}
24
- {{#if (eq api "trpc")}}
23
+ {{/if}}
24
+ {{#if (eq api "trpc")}}
25
25
  import { queryClient } from "./utils/trpc";
26
- {{/if}}
27
- {{/unless}}
26
+ {{/if}}
27
+ {{/unless}}
28
28
  {{/if}}
29
29
 
30
30
  export const links: Route.LinksFunction = () => [
31
- {
32
- rel: "preconnect",
33
- href: "https://fonts.googleapis.com",
34
- },
35
- {
36
- rel: "preconnect",
37
- href: "https://fonts.gstatic.com",
38
- crossOrigin: "anonymous",
39
- },
31
+ { rel: "preconnect", href: "https://fonts.googleapis.com" },
32
+ { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" },
40
33
  {
41
34
  rel: "stylesheet",
42
- href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
35
+ href:
36
+ "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
43
37
  },
44
38
  ];
45
39
 
@@ -68,7 +62,12 @@ export default function App() {
68
62
  );
69
63
  return (
70
64
  <ConvexProvider client={convex}>
71
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
65
+ <ThemeProvider
66
+ attribute="class"
67
+ defaultTheme="dark"
68
+ disableTransitionOnChange
69
+ storageKey="vite-ui-theme"
70
+ >
72
71
  <div className="grid grid-rows-[auto_1fr] h-svh">
73
72
  <Header />
74
73
  <Outlet />
@@ -82,13 +81,18 @@ export default function App() {
82
81
  export default function App() {
83
82
  return (
84
83
  <QueryClientProvider client={queryClient}>
85
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
86
- <div className="grid grid-rows-[auto_1fr] h-svh">
87
- <Header />
88
- <Outlet />
89
- </div>
90
- <Toaster richColors />
91
- </ThemeProvider>
84
+ <ThemeProvider
85
+ attribute="class"
86
+ defaultTheme="dark"
87
+ disableTransitionOnChange
88
+ storageKey="vite-ui-theme"
89
+ >
90
+ <div className="grid grid-rows-[auto_1fr] h-svh">
91
+ <Header />
92
+ <Outlet />
93
+ </div>
94
+ <Toaster richColors />
95
+ </ThemeProvider>
92
96
  <ReactQueryDevtools position="bottom" buttonPosition="bottom-right" />
93
97
  </QueryClientProvider>
94
98
  );
@@ -97,7 +101,12 @@ export default function App() {
97
101
  export default function App() {
98
102
  return (
99
103
  <QueryClientProvider client={queryClient}>
100
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
104
+ <ThemeProvider
105
+ attribute="class"
106
+ defaultTheme="dark"
107
+ disableTransitionOnChange
108
+ storageKey="vite-ui-theme"
109
+ >
101
110
  <div className="grid grid-rows-[auto_1fr] h-svh">
102
111
  <Header />
103
112
  <Outlet />
@@ -111,7 +120,12 @@ export default function App() {
111
120
  {{else}}
112
121
  export default function App() {
113
122
  return (
114
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
123
+ <ThemeProvider
124
+ attribute="class"
125
+ defaultTheme="dark"
126
+ disableTransitionOnChange
127
+ storageKey="vite-ui-theme"
128
+ >
115
129
  <div className="grid grid-rows-[auto_1fr] h-svh">
116
130
  <Header />
117
131
  <Outlet />
@@ -126,7 +140,6 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
126
140
  let message = "Oops!";
127
141
  let details = "An unexpected error occurred.";
128
142
  let stack: string | undefined;
129
-
130
143
  if (isRouteErrorResponse(error)) {
131
144
  message = error.status === 404 ? "404" : "Error";
132
145
  details =
@@ -137,7 +150,6 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
137
150
  details = error.message;
138
151
  stack = error.stack;
139
152
  }
140
-
141
153
  return (
142
154
  <main className="pt-16 p-4 container mx-auto">
143
155
  <h1>{message}</h1>
@@ -29,7 +29,7 @@ const TITLE_TEXT = `
29
29
  `;
30
30
 
31
31
  export function meta({}: Route.MetaArgs) {
32
- return [{ title: "My App" }, { name: "description", content: "My App" }];
32
+ return [{ title: "{{projectName}}" }, { name: "description", content: "{{projectName}} is a web application" }];
33
33
  }
34
34
 
35
35
  export default function Home() {
@@ -1,8 +1,9 @@
1
- <!doctype html>
1
+ <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{projectName}}</title>
6
7
  </head>
7
8
 
8
9
  <body>
@@ -1,73 +1,11 @@
1
- import { createContext, useContext, useEffect, useState } from "react";
2
-
3
- type Theme = "dark" | "light" | "system";
4
-
5
- type ThemeProviderProps = {
6
- children: React.ReactNode;
7
- defaultTheme?: Theme;
8
- storageKey?: string;
9
- };
10
-
11
- type ThemeProviderState = {
12
- theme: Theme;
13
- setTheme: (theme: Theme) => void;
14
- };
15
-
16
- const initialState: ThemeProviderState = {
17
- theme: "system",
18
- setTheme: () => null,
19
- };
20
-
21
- const ThemeProviderContext = createContext<ThemeProviderState>(initialState);
1
+ import * as React from "react";
2
+ import { ThemeProvider as NextThemesProvider } from "next-themes";
22
3
 
23
4
  export function ThemeProvider({
24
5
  children,
25
- defaultTheme = "system",
26
- storageKey = "vite-ui-theme",
27
6
  ...props
28
- }: ThemeProviderProps) {
29
- const [theme, setTheme] = useState<Theme>(
30
- () => (localStorage.getItem(storageKey) as Theme) || defaultTheme
31
- );
32
-
33
- useEffect(() => {
34
- const root = window.document.documentElement;
35
-
36
- root.classList.remove("light", "dark");
37
-
38
- if (theme === "system") {
39
- const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
40
- .matches
41
- ? "dark"
42
- : "light";
43
-
44
- root.classList.add(systemTheme);
45
- return;
46
- }
47
-
48
- root.classList.add(theme);
49
- }, [theme]);
50
-
51
- const value = {
52
- theme,
53
- setTheme: (theme: Theme) => {
54
- localStorage.setItem(storageKey, theme);
55
- setTheme(theme);
56
- },
57
- };
58
-
59
- return (
60
- <ThemeProviderContext.Provider {...props} value={value}>
61
- {children}
62
- </ThemeProviderContext.Provider>
63
- );
7
+ }: React.ComponentProps<typeof NextThemesProvider>) {
8
+ return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
64
9
  }
65
10
 
66
- export const useTheme = () => {
67
- const context = useContext(ThemeProviderContext);
68
-
69
- if (context === undefined)
70
- throw new Error("useTheme must be used within a ThemeProvider");
71
-
72
- return context;
73
- };
11
+ export { useTheme } from "next-themes";
@@ -45,11 +45,11 @@ export const Route = createRootRouteWithContext<RouterAppContext>()({
45
45
  head: () => ({
46
46
  meta: [
47
47
  {
48
- title: "My App",
48
+ title: "{{projectName}}",
49
49
  },
50
50
  {
51
51
  name: "description",
52
- content: "My App is a web application",
52
+ content: "{{projectName}} is a web application",
53
53
  },
54
54
  ],
55
55
  links: [
@@ -75,7 +75,12 @@ function RootComponent() {
75
75
  <>
76
76
  <HeadContent />
77
77
  {{#if (eq api "orpc")}}
78
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
78
+ <ThemeProvider
79
+ attribute="class"
80
+ defaultTheme="dark"
81
+ disableTransitionOnChange
82
+ storageKey="vite-ui-theme"
83
+ >
79
84
  <div className="grid grid-rows-[auto_1fr] h-svh">
80
85
  <Header />
81
86
  {isFetching ? <Loader /> : <Outlet />}
@@ -83,7 +88,12 @@ function RootComponent() {
83
88
  <Toaster richColors />
84
89
  </ThemeProvider>
85
90
  {{else}}
86
- <ThemeProvider defaultTheme="dark" storageKey="vite-ui-theme">
91
+ <ThemeProvider
92
+ attribute="class"
93
+ defaultTheme="dark"
94
+ disableTransitionOnChange
95
+ storageKey="vite-ui-theme"
96
+ >
87
97
  <div className="grid grid-rows-[auto_1fr] h-svh">
88
98
  <Header />
89
99
  {isFetching ? <Loader /> : <Outlet />}