create-better-t-stack 3.8.3 → 3.9.0-pr730.0ee9844
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/README.md +17 -16
- package/dist/cli.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-BCBL3cO2.mjs → src-DW15ZRe9.mjs} +80 -108
- package/package.json +44 -44
- package/templates/auth/better-auth/convex/backend/convex/auth.config.ts.hbs +5 -7
- package/templates/auth/better-auth/convex/backend/convex/auth.ts.hbs +21 -19
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +6 -2
- package/templates/auth/better-auth/convex/native/base/lib/auth-client.ts.hbs +11 -6
- package/templates/auth/better-auth/convex/web/react/next/src/app/api/auth/[...all]/route.ts.hbs +2 -2
- package/templates/auth/better-auth/convex/web/react/next/src/components/user-menu.tsx.hbs +23 -20
- package/templates/auth/better-auth/convex/web/react/next/src/lib/auth-server.ts.hbs +13 -5
- package/templates/auth/better-auth/convex/web/react/tanstack-router/src/components/user-menu.tsx.hbs +25 -22
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +23 -24
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/lib/auth-server.ts.hbs +11 -5
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/routes/api/auth/$.ts.hbs +4 -4
- package/templates/auth/better-auth/fullstack/tanstack-start/src/routes/api/auth/$.ts.hbs +6 -10
- package/templates/auth/better-auth/web/react/base/src/lib/auth-client.ts.hbs +0 -1
- package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +24 -21
- package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +24 -21
- package/templates/auth/better-auth/web/react/{tanstack-start/src/components/user-menu.tsx → tanstack-router/src/components/user-menu.tsx.hbs} +26 -23
- package/templates/auth/better-auth/web/react/{tanstack-router/src/components/user-menu.tsx → tanstack-start/src/components/user-menu.tsx.hbs} +26 -23
- package/templates/frontend/react/next/package.json.hbs +8 -7
- package/templates/frontend/react/next/src/app/layout.tsx.hbs +28 -1
- package/templates/frontend/react/next/src/components/providers.tsx.hbs +14 -4
- package/templates/frontend/react/react-router/package.json.hbs +2 -1
- package/templates/frontend/react/{tanstack-router/src/components/mode-toggle.tsx → react-router/src/components/mode-toggle.tsx.hbs} +1 -1
- package/templates/frontend/react/tanstack-router/package.json.hbs +2 -1
- package/templates/frontend/react/{react-router/src/components/mode-toggle.tsx → tanstack-router/src/components/mode-toggle.tsx.hbs} +1 -1
- package/templates/frontend/react/tanstack-start/package.json.hbs +2 -1
- package/templates/frontend/react/tanstack-start/src/router.tsx.hbs +6 -0
- package/templates/frontend/react/tanstack-start/src/routes/__root.tsx.hbs +13 -14
- package/templates/frontend/react/tanstack-start/vite.config.ts.hbs +5 -0
- package/templates/frontend/react/web-base/components.json +5 -2
- package/templates/frontend/react/web-base/src/components/ui/button.tsx.hbs +57 -0
- package/templates/frontend/react/web-base/src/components/ui/card.tsx.hbs +103 -0
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx.hbs +26 -0
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx.hbs +262 -0
- package/templates/frontend/react/web-base/src/components/ui/input.tsx.hbs +20 -0
- package/templates/frontend/react/web-base/src/components/ui/label.tsx.hbs +20 -0
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx.hbs +13 -0
- package/templates/frontend/react/web-base/src/components/ui/sonner.tsx.hbs +44 -0
- package/templates/frontend/react/web-base/src/index.css.hbs +57 -63
- package/templates/frontend/react/web-base/src/components/ui/button.tsx +0 -56
- package/templates/frontend/react/web-base/src/components/ui/card.tsx +0 -75
- package/templates/frontend/react/web-base/src/components/ui/checkbox.tsx +0 -27
- package/templates/frontend/react/web-base/src/components/ui/dropdown-menu.tsx +0 -228
- package/templates/frontend/react/web-base/src/components/ui/input.tsx +0 -21
- package/templates/frontend/react/web-base/src/components/ui/label.tsx +0 -19
- package/templates/frontend/react/web-base/src/components/ui/skeleton.tsx +0 -13
- package/templates/frontend/react/web-base/src/components/ui/sonner.tsx +0 -25
- /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-router/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-router/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/react/tanstack-start/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/components/{sign-in-form.tsx → sign-in-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/components/{sign-up-form.tsx → sign-up-form.tsx.hbs} +0 -0
- /package/templates/auth/better-auth/web/solid/src/routes/{login.tsx → login.tsx.hbs} +0 -0
- /package/templates/frontend/react/react-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
- /package/templates/frontend/react/tanstack-router/src/components/{theme-provider.tsx → theme-provider.tsx.hbs} +0 -0
- /package/templates/frontend/react/web-base/src/lib/{utils.ts → utils.ts.hbs} +0 -0
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|
|
38
38
|
| **Runtime** | • Bun<br>• Node.js<br>• Cloudflare Workers<br>• None |
|
|
39
39
|
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
|
40
40
|
| **ORM** | • Drizzle (TypeScript-first)<br>• Prisma (feature-rich)<br>• Mongoose (for MongoDB)<br>• None |
|
|
41
|
-
| **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres<br>• MongoDB Atlas<br>• None (manual setup)
|
|
41
|
+
| **Database Setup** | • Turso (SQLite)<br>• Cloudflare D1 (SQLite)<br>• Neon (PostgreSQL)<br>• Supabase (PostgreSQL)<br>• Prisma Postgres<br>• MongoDB Atlas<br>• None (manual setup) |
|
|
42
42
|
| **Authentication** | Better-Auth (email/password, with more options coming soon) |
|
|
43
43
|
| **Styling** | Tailwind CSS with shadcn/ui components |
|
|
44
44
|
| **Addons** | • PWA support<br>• Tauri (desktop applications)<br>• Starlight (documentation site)<br>• Biome (linting and formatting)<br>• Husky (Git hooks)<br>• Turborepo (optimized builds) |
|
|
@@ -77,6 +77,7 @@ Options:
|
|
|
77
77
|
## Telemetry
|
|
78
78
|
|
|
79
79
|
This CLI collects anonymous usage data to help improve the tool. The data collected includes:
|
|
80
|
+
|
|
80
81
|
- Configuration options selected
|
|
81
82
|
- CLI version
|
|
82
83
|
- Node.js version
|
|
@@ -90,7 +91,7 @@ You can disable telemetry by setting the `BTS_TELEMETRY_DISABLED` environment va
|
|
|
90
91
|
|
|
91
92
|
```bash
|
|
92
93
|
# Disable telemetry for a single run
|
|
93
|
-
BTS_TELEMETRY_DISABLED=1 npx create-better-t-stack
|
|
94
|
+
BTS_TELEMETRY_DISABLED=1 npx create-better-t-stack
|
|
94
95
|
|
|
95
96
|
# Disable telemetry globally in your shell profile (.bashrc, .zshrc, etc.)
|
|
96
97
|
export BTS_TELEMETRY_DISABLED=1
|
|
@@ -101,85 +102,85 @@ export BTS_TELEMETRY_DISABLED=1
|
|
|
101
102
|
Create a project with default configuration:
|
|
102
103
|
|
|
103
104
|
```bash
|
|
104
|
-
npx create-better-t-stack
|
|
105
|
+
npx create-better-t-stack --yes
|
|
105
106
|
```
|
|
106
107
|
|
|
107
108
|
Create a project with specific options:
|
|
108
109
|
|
|
109
110
|
```bash
|
|
110
|
-
npx create-better-t-stack
|
|
111
|
+
npx create-better-t-stack --database postgres --orm drizzle --auth --addons pwa biome
|
|
111
112
|
```
|
|
112
113
|
|
|
113
114
|
Create a project with Elysia backend and Node.js runtime:
|
|
114
115
|
|
|
115
116
|
```bash
|
|
116
|
-
npx create-better-t-stack
|
|
117
|
+
npx create-better-t-stack --backend elysia --runtime node
|
|
117
118
|
```
|
|
118
119
|
|
|
119
120
|
Create a project with multiple frontend options (one web + one native):
|
|
120
121
|
|
|
121
122
|
```bash
|
|
122
|
-
npx create-better-t-stack
|
|
123
|
+
npx create-better-t-stack --frontend tanstack-router native-bare
|
|
123
124
|
```
|
|
124
125
|
|
|
125
126
|
Create a project with examples:
|
|
126
127
|
|
|
127
128
|
```bash
|
|
128
|
-
npx create-better-t-stack
|
|
129
|
+
npx create-better-t-stack --examples todo ai
|
|
129
130
|
```
|
|
130
131
|
|
|
131
132
|
Create a project with Turso database setup:
|
|
132
133
|
|
|
133
134
|
```bash
|
|
134
|
-
npx create-better-t-stack
|
|
135
|
+
npx create-better-t-stack --database sqlite --orm drizzle --db-setup turso
|
|
135
136
|
```
|
|
136
137
|
|
|
137
138
|
Create a project with Supabase PostgreSQL setup:
|
|
138
139
|
|
|
139
140
|
```bash
|
|
140
|
-
npx create-better-t-stack
|
|
141
|
+
npx create-better-t-stack --database postgres --orm drizzle --db-setup supabase --auth
|
|
141
142
|
```
|
|
142
143
|
|
|
143
144
|
Create a project with Convex backend:
|
|
144
145
|
|
|
145
146
|
```bash
|
|
146
|
-
npx create-better-t-stack
|
|
147
|
+
npx create-better-t-stack --backend convex --frontend tanstack-router
|
|
147
148
|
```
|
|
148
149
|
|
|
149
150
|
Create a project with documentation site:
|
|
150
151
|
|
|
151
152
|
```bash
|
|
152
|
-
npx create-better-t-stack
|
|
153
|
+
npx create-better-t-stack --addons starlight
|
|
153
154
|
```
|
|
154
155
|
|
|
155
156
|
Create a minimal TypeScript project with no backend:
|
|
156
157
|
|
|
157
158
|
```bash
|
|
158
|
-
npx create-better-t-stack
|
|
159
|
+
npx create-better-t-stack --backend none --frontend tanstack-router
|
|
159
160
|
```
|
|
160
161
|
|
|
161
162
|
Create a backend-only project with no frontend:
|
|
162
163
|
|
|
163
164
|
```bash
|
|
164
|
-
npx create-better-t-stack
|
|
165
|
+
npx create-better-t-stack --frontend none --backend hono --database postgres --orm drizzle
|
|
165
166
|
```
|
|
166
167
|
|
|
167
168
|
Create a simple frontend-only project:
|
|
168
169
|
|
|
169
170
|
```bash
|
|
170
|
-
npx create-better-t-stack
|
|
171
|
+
npx create-better-t-stack --backend none --frontend next --addons none --examples none
|
|
171
172
|
```
|
|
172
173
|
|
|
173
174
|
Create a Cloudflare Workers project:
|
|
174
175
|
|
|
175
176
|
```bash
|
|
176
|
-
npx create-better-t-stack
|
|
177
|
+
npx create-better-t-stack --backend hono --runtime workers --database sqlite --orm drizzle --db-setup d1
|
|
177
178
|
```
|
|
178
179
|
|
|
179
180
|
Create a minimal API-only project:
|
|
180
181
|
|
|
181
182
|
```bash
|
|
182
|
-
npx create-better-t-stack
|
|
183
|
+
npx create-better-t-stack --frontend none --backend hono --api trpc --database none --addons none
|
|
183
184
|
```
|
|
184
185
|
|
|
185
186
|
## Compatibility Notes
|
package/dist/cli.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-
|
|
2
|
+
import { a as router, i as init, n as createBtsCli, o as sponsors, r as docs, t as builder } from "./src-DW15ZRe9.mjs";
|
|
3
3
|
|
|
4
4
|
export { builder, createBtsCli, docs, init, router, sponsors };
|
|
@@ -15,7 +15,7 @@ import { $, execa } from "execa";
|
|
|
15
15
|
import { IndentationText, Node, Project, QuoteKind, SyntaxKind } from "ts-morph";
|
|
16
16
|
import { glob } from "tinyglobby";
|
|
17
17
|
import handlebars from "handlebars";
|
|
18
|
-
import {
|
|
18
|
+
import { format } from "oxfmt";
|
|
19
19
|
import yaml from "yaml";
|
|
20
20
|
import os$1 from "node:os";
|
|
21
21
|
|
|
@@ -95,7 +95,8 @@ const dependencyVersionMap = {
|
|
|
95
95
|
"@vite-pwa/assets-generator": "^1.0.0",
|
|
96
96
|
"@tauri-apps/cli": "^2.4.0",
|
|
97
97
|
"@biomejs/biome": "^2.2.0",
|
|
98
|
-
oxlint: "^1.
|
|
98
|
+
oxlint: "^1.34.0",
|
|
99
|
+
oxfmt: "^0.19.0",
|
|
99
100
|
husky: "^9.1.7",
|
|
100
101
|
"lint-staged": "^16.1.2",
|
|
101
102
|
tsx: "^4.19.2",
|
|
@@ -136,7 +137,7 @@ const dependencyVersionMap = {
|
|
|
136
137
|
"convex-svelte": "^0.0.12",
|
|
137
138
|
"convex-nuxt": "0.1.5",
|
|
138
139
|
"convex-vue": "^0.1.5",
|
|
139
|
-
"@convex-dev/better-auth": "^0.
|
|
140
|
+
"@convex-dev/better-auth": "^0.10.4",
|
|
140
141
|
"@tanstack/svelte-query": "^5.85.3",
|
|
141
142
|
"@tanstack/svelte-query-devtools": "^5.85.3",
|
|
142
143
|
"@tanstack/vue-query-devtools": "^5.90.2",
|
|
@@ -363,7 +364,7 @@ function getAddonDisplay(addon) {
|
|
|
363
364
|
break;
|
|
364
365
|
case "oxlint":
|
|
365
366
|
label = "Oxlint";
|
|
366
|
-
hint = "
|
|
367
|
+
hint = "Oxlint + Oxfmt (linting & formatting)";
|
|
367
368
|
break;
|
|
368
369
|
case "ultracite":
|
|
369
370
|
label = "Ultracite";
|
|
@@ -1233,7 +1234,7 @@ const getLatestCLIVersion = () => {
|
|
|
1233
1234
|
*/
|
|
1234
1235
|
function isTelemetryEnabled() {
|
|
1235
1236
|
const BTS_TELEMETRY_DISABLED = process.env.BTS_TELEMETRY_DISABLED;
|
|
1236
|
-
const BTS_TELEMETRY = "
|
|
1237
|
+
const BTS_TELEMETRY = "0";
|
|
1237
1238
|
if (BTS_TELEMETRY_DISABLED !== void 0) return BTS_TELEMETRY_DISABLED !== "1";
|
|
1238
1239
|
if (BTS_TELEMETRY !== void 0) return BTS_TELEMETRY === "1";
|
|
1239
1240
|
return true;
|
|
@@ -1241,16 +1242,7 @@ function isTelemetryEnabled() {
|
|
|
1241
1242
|
|
|
1242
1243
|
//#endregion
|
|
1243
1244
|
//#region src/utils/analytics.ts
|
|
1244
|
-
|
|
1245
|
-
async function sendConvexEvent(payload) {
|
|
1246
|
-
try {
|
|
1247
|
-
await fetch(CONVEX_INGEST_URL, {
|
|
1248
|
-
method: "POST",
|
|
1249
|
-
headers: { "Content-Type": "application/json" },
|
|
1250
|
-
body: JSON.stringify(payload)
|
|
1251
|
-
});
|
|
1252
|
-
} catch {}
|
|
1253
|
-
}
|
|
1245
|
+
async function sendConvexEvent(payload) {}
|
|
1254
1246
|
async function trackProjectCreation(config, disableAnalytics = false) {
|
|
1255
1247
|
if (!isTelemetryEnabled() || disableAnalytics) return;
|
|
1256
1248
|
const { projectName: _projectName, projectDir: _projectDir, relativePath: _relativePath, ...safeConfig } = config;
|
|
@@ -2031,6 +2023,38 @@ async function setupFumadocs(config) {
|
|
|
2031
2023
|
}
|
|
2032
2024
|
}
|
|
2033
2025
|
|
|
2026
|
+
//#endregion
|
|
2027
|
+
//#region src/helpers/addons/oxlint-setup.ts
|
|
2028
|
+
async function setupOxlint(projectDir, packageManager) {
|
|
2029
|
+
await addPackageDependency({
|
|
2030
|
+
devDependencies: ["oxlint", "oxfmt"],
|
|
2031
|
+
projectDir
|
|
2032
|
+
});
|
|
2033
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
2034
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
2035
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
2036
|
+
packageJson.scripts = {
|
|
2037
|
+
...packageJson.scripts,
|
|
2038
|
+
check: "oxlint && oxfmt --write"
|
|
2039
|
+
};
|
|
2040
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
2041
|
+
}
|
|
2042
|
+
const s = spinner();
|
|
2043
|
+
const oxlintInitCommand = getPackageExecutionCommand(packageManager, "oxlint@latest --init");
|
|
2044
|
+
s.start("Initializing oxlint and oxfmt...");
|
|
2045
|
+
await execa(oxlintInitCommand, {
|
|
2046
|
+
cwd: projectDir,
|
|
2047
|
+
env: { CI: "true" },
|
|
2048
|
+
shell: true
|
|
2049
|
+
});
|
|
2050
|
+
await execa(getPackageExecutionCommand(packageManager, "oxfmt@latest --init"), {
|
|
2051
|
+
cwd: projectDir,
|
|
2052
|
+
env: { CI: "true" },
|
|
2053
|
+
shell: true
|
|
2054
|
+
});
|
|
2055
|
+
s.stop("oxlint and oxfmt initialized successfully!");
|
|
2056
|
+
}
|
|
2057
|
+
|
|
2034
2058
|
//#endregion
|
|
2035
2059
|
//#region src/helpers/addons/ruler-setup.ts
|
|
2036
2060
|
async function setupRuler(config) {
|
|
@@ -2400,7 +2424,7 @@ ${pc.cyan("Docs:")} ${pc.underline("https://turborepo.com/docs")}
|
|
|
2400
2424
|
await setupHusky(projectDir, linter);
|
|
2401
2425
|
}
|
|
2402
2426
|
}
|
|
2403
|
-
if (
|
|
2427
|
+
if (hasOxlint) await setupOxlint(projectDir, packageManager);
|
|
2404
2428
|
if (addons.includes("starlight")) await setupStarlight(config);
|
|
2405
2429
|
if (addons.includes("ruler")) await setupRuler(config);
|
|
2406
2430
|
if (addons.includes("fumadocs")) await setupFumadocs(config);
|
|
@@ -2442,7 +2466,7 @@ async function setupHusky(projectDir, linter) {
|
|
|
2442
2466
|
...packageJson.scripts,
|
|
2443
2467
|
prepare: "husky"
|
|
2444
2468
|
};
|
|
2445
|
-
if (linter === "oxlint") packageJson["lint-staged"] = { "
|
|
2469
|
+
if (linter === "oxlint") packageJson["lint-staged"] = { "*": ["oxlint", "oxfmt --write"] };
|
|
2446
2470
|
else if (linter === "biome") packageJson["lint-staged"] = { "*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": ["biome check --write ."] };
|
|
2447
2471
|
else packageJson["lint-staged"] = { "**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,astro,svelte}": "" };
|
|
2448
2472
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
@@ -2473,30 +2497,6 @@ async function setupPwa(projectDir, frontends) {
|
|
|
2473
2497
|
const viteConfigTs = path.join(clientPackageDir, "vite.config.ts");
|
|
2474
2498
|
if (await fs.pathExists(viteConfigTs)) await addPwaToViteConfig(viteConfigTs, path.basename(projectDir));
|
|
2475
2499
|
}
|
|
2476
|
-
async function setupOxlint(projectDir, packageManager) {
|
|
2477
|
-
await addPackageDependency({
|
|
2478
|
-
devDependencies: ["oxlint"],
|
|
2479
|
-
projectDir
|
|
2480
|
-
});
|
|
2481
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
2482
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
2483
|
-
const packageJson = await fs.readJson(packageJsonPath);
|
|
2484
|
-
packageJson.scripts = {
|
|
2485
|
-
...packageJson.scripts,
|
|
2486
|
-
check: "oxlint"
|
|
2487
|
-
};
|
|
2488
|
-
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
2489
|
-
}
|
|
2490
|
-
const oxlintInitCommand = getPackageExecutionCommand(packageManager, "oxlint@latest --init");
|
|
2491
|
-
const s = spinner();
|
|
2492
|
-
s.start("Initializing oxlint...");
|
|
2493
|
-
await execa(oxlintInitCommand, {
|
|
2494
|
-
cwd: projectDir,
|
|
2495
|
-
env: { CI: "true" },
|
|
2496
|
-
shell: true
|
|
2497
|
-
});
|
|
2498
|
-
s.stop("oxlint initialized successfully!");
|
|
2499
|
-
}
|
|
2500
2500
|
|
|
2501
2501
|
//#endregion
|
|
2502
2502
|
//#region src/helpers/core/detect-project-config.ts
|
|
@@ -2552,61 +2552,16 @@ async function installDependencies({ projectDir, packageManager }) {
|
|
|
2552
2552
|
}
|
|
2553
2553
|
|
|
2554
2554
|
//#endregion
|
|
2555
|
-
//#region src/utils/
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
formatter: {
|
|
2562
|
-
enabled: true,
|
|
2563
|
-
indentStyle: "tab",
|
|
2564
|
-
indentWidth: 2,
|
|
2565
|
-
lineWidth: 80
|
|
2566
|
-
},
|
|
2567
|
-
linter: { enabled: false },
|
|
2568
|
-
javascript: { formatter: { enabled: true } },
|
|
2569
|
-
json: { formatter: { enabled: true } }
|
|
2570
|
-
});
|
|
2571
|
-
return {
|
|
2572
|
-
biome,
|
|
2573
|
-
projectKey
|
|
2574
|
-
};
|
|
2575
|
-
} catch {
|
|
2576
|
-
return null;
|
|
2577
|
-
}
|
|
2578
|
-
}
|
|
2579
|
-
function isSupportedFile(filePath) {
|
|
2580
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
2581
|
-
return [
|
|
2582
|
-
".js",
|
|
2583
|
-
".jsx",
|
|
2584
|
-
".ts",
|
|
2585
|
-
".tsx",
|
|
2586
|
-
".json",
|
|
2587
|
-
".jsonc"
|
|
2588
|
-
].includes(ext);
|
|
2589
|
-
}
|
|
2590
|
-
function shouldSkipFile(filePath) {
|
|
2591
|
-
const basename = path.basename(filePath);
|
|
2592
|
-
return [
|
|
2593
|
-
".hbs",
|
|
2594
|
-
"package-lock.json",
|
|
2595
|
-
"yarn.lock",
|
|
2596
|
-
"pnpm-lock.yaml",
|
|
2597
|
-
"bun.lock",
|
|
2598
|
-
".d.ts"
|
|
2599
|
-
].some((pattern) => basename.includes(pattern));
|
|
2600
|
-
}
|
|
2601
|
-
function formatFileWithBiome(filePath, content) {
|
|
2602
|
-
if (!isSupportedFile(filePath) || shouldSkipFile(filePath)) return null;
|
|
2555
|
+
//#region src/utils/file-formatter.ts
|
|
2556
|
+
const formatOptions = {
|
|
2557
|
+
experimentalSortPackageJson: true,
|
|
2558
|
+
experimentalSortImports: { order: "asc" }
|
|
2559
|
+
};
|
|
2560
|
+
async function formatFile(filePath, content) {
|
|
2603
2561
|
try {
|
|
2604
|
-
const
|
|
2605
|
-
if (
|
|
2606
|
-
|
|
2607
|
-
const result = biome.formatContent(projectKey, content, { filePath: path.basename(filePath) });
|
|
2608
|
-
if (result.diagnostics && result.diagnostics.length > 0) consola.debug(`Biome formatting diagnostics for ${filePath}:`, result.diagnostics);
|
|
2609
|
-
return result.content;
|
|
2562
|
+
const result = await format(path.basename(filePath), content, formatOptions);
|
|
2563
|
+
if (result.errors && result.errors.length > 0) return null;
|
|
2564
|
+
return result.code;
|
|
2610
2565
|
} catch {
|
|
2611
2566
|
return null;
|
|
2612
2567
|
}
|
|
@@ -2636,7 +2591,7 @@ async function processTemplate(srcPath, destPath, context) {
|
|
|
2636
2591
|
content = handlebars.compile(templateContent)(context);
|
|
2637
2592
|
} else content = await fs.readFile(srcPath, "utf-8");
|
|
2638
2593
|
try {
|
|
2639
|
-
const formattedContent = await
|
|
2594
|
+
const formattedContent = await formatFile(destPath, content);
|
|
2640
2595
|
if (formattedContent) content = formattedContent;
|
|
2641
2596
|
} catch (formatError) {
|
|
2642
2597
|
consola.debug(`Failed to format ${destPath}:`, formatError);
|
|
@@ -4240,12 +4195,12 @@ async function setupAuth(config) {
|
|
|
4240
4195
|
if (convexBackendDirExists) {
|
|
4241
4196
|
await addPackageDependency({
|
|
4242
4197
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4243
|
-
customDependencies: { "better-auth": "1.
|
|
4198
|
+
customDependencies: { "better-auth": "1.4.7" },
|
|
4244
4199
|
projectDir: convexBackendDir
|
|
4245
4200
|
});
|
|
4246
4201
|
if (hasNativeForBA) await addPackageDependency({
|
|
4247
4202
|
dependencies: ["@better-auth/expo"],
|
|
4248
|
-
customDependencies: { "@better-auth/expo": "1.
|
|
4203
|
+
customDependencies: { "@better-auth/expo": "1.4.7" },
|
|
4249
4204
|
projectDir: convexBackendDir
|
|
4250
4205
|
});
|
|
4251
4206
|
}
|
|
@@ -4255,17 +4210,17 @@ async function setupAuth(config) {
|
|
|
4255
4210
|
const hasViteReactOther = frontend.some((f) => ["tanstack-router", "react-router"].includes(f));
|
|
4256
4211
|
if (hasNextJs) await addPackageDependency({
|
|
4257
4212
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4258
|
-
customDependencies: { "better-auth": "1.
|
|
4213
|
+
customDependencies: { "better-auth": "1.4.7" },
|
|
4259
4214
|
projectDir: clientDir
|
|
4260
4215
|
});
|
|
4261
4216
|
else if (hasTanStackStart) await addPackageDependency({
|
|
4262
4217
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4263
|
-
customDependencies: { "better-auth": "1.
|
|
4218
|
+
customDependencies: { "better-auth": "1.4.7" },
|
|
4264
4219
|
projectDir: clientDir
|
|
4265
4220
|
});
|
|
4266
4221
|
else if (hasViteReactOther) await addPackageDependency({
|
|
4267
4222
|
dependencies: ["better-auth", "@convex-dev/better-auth"],
|
|
4268
|
-
customDependencies: { "better-auth": "1.
|
|
4223
|
+
customDependencies: { "better-auth": "1.4.7" },
|
|
4269
4224
|
projectDir: clientDir
|
|
4270
4225
|
});
|
|
4271
4226
|
}
|
|
@@ -4279,8 +4234,8 @@ async function setupAuth(config) {
|
|
|
4279
4234
|
"@convex-dev/better-auth"
|
|
4280
4235
|
],
|
|
4281
4236
|
customDependencies: {
|
|
4282
|
-
"better-auth": "1.
|
|
4283
|
-
"@better-auth/expo": "1.
|
|
4237
|
+
"better-auth": "1.4.7",
|
|
4238
|
+
"@better-auth/expo": "1.4.7"
|
|
4284
4239
|
},
|
|
4285
4240
|
projectDir: nativeDir
|
|
4286
4241
|
});
|
|
@@ -4522,10 +4477,12 @@ async function setupEnvironmentVariables(config) {
|
|
|
4522
4477
|
const hasNative = frontend.includes("native-bare") || frontend.includes("native-uniwind") || frontend.includes("native-unistyles");
|
|
4523
4478
|
const hasWeb = hasWebFrontend$1;
|
|
4524
4479
|
if (!await fs.pathExists(envLocalPath) || !(await fs.readFile(envLocalPath, "utf8")).includes("npx convex env set")) {
|
|
4480
|
+
let siteUrlComments = "";
|
|
4481
|
+
if (hasWeb) siteUrlComments += "# npx convex env set SITE_URL http://localhost:3001\n";
|
|
4482
|
+
if (hasNative) siteUrlComments += "# npx convex env set NATIVE_SITE_URL http://localhost:8081 # For Expo Web\n";
|
|
4525
4483
|
const convexCommands = `# Set Convex environment variables
|
|
4526
4484
|
# npx convex env set BETTER_AUTH_SECRET=$(openssl rand -base64 32)
|
|
4527
|
-
${
|
|
4528
|
-
`;
|
|
4485
|
+
${siteUrlComments}`;
|
|
4529
4486
|
await fs.appendFile(envLocalPath, convexCommands);
|
|
4530
4487
|
}
|
|
4531
4488
|
const convexBackendVars = [];
|
|
@@ -4534,6 +4491,11 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
|
|
|
4534
4491
|
value: "",
|
|
4535
4492
|
condition: true,
|
|
4536
4493
|
comment: "Same as CONVEX_URL but ends in .site"
|
|
4494
|
+
}, {
|
|
4495
|
+
key: "NATIVE_SITE_URL",
|
|
4496
|
+
value: "http://localhost:8081",
|
|
4497
|
+
condition: true,
|
|
4498
|
+
comment: "Expo Web URL for authentication"
|
|
4537
4499
|
});
|
|
4538
4500
|
if (hasWeb) convexBackendVars.push({
|
|
4539
4501
|
key: hasNextJs ? "NEXT_PUBLIC_CONVEX_SITE_URL" : "VITE_CONVEX_SITE_URL",
|
|
@@ -4543,7 +4505,8 @@ ${hasWeb ? "# npx convex env set SITE_URL http://localhost:3001\n" : ""}
|
|
|
4543
4505
|
}, {
|
|
4544
4506
|
key: "SITE_URL",
|
|
4545
4507
|
value: "http://localhost:3001",
|
|
4546
|
-
condition: true
|
|
4508
|
+
condition: true,
|
|
4509
|
+
comment: "Web app URL for authentication"
|
|
4547
4510
|
});
|
|
4548
4511
|
await addEnvVariablesToFile(envLocalPath, convexBackendVars);
|
|
4549
4512
|
}
|
|
@@ -5949,6 +5912,7 @@ function generateFeaturesList(database, auth, addons, orm, runtime, frontend, ba
|
|
|
5949
5912
|
for (const addon of addons) if (addon === "pwa") addonsList.push("- **PWA** - Progressive Web App support");
|
|
5950
5913
|
else if (addon === "tauri") addonsList.push("- **Tauri** - Build native desktop applications");
|
|
5951
5914
|
else if (addon === "biome") addonsList.push("- **Biome** - Linting and formatting");
|
|
5915
|
+
else if (addon === "oxlint") addonsList.push("- **Oxlint** - Oxlint + Oxfmt (linting & formatting)");
|
|
5952
5916
|
else if (addon === "husky") addonsList.push("- **Husky** - Git hooks for code quality");
|
|
5953
5917
|
else if (addon === "starlight") addonsList.push("- **Starlight** - Documentation site with Astro");
|
|
5954
5918
|
else if (addon === "turborepo") addonsList.push("- **Turborepo** - Optimized monorepo build system");
|
|
@@ -6024,6 +5988,8 @@ function generateScriptsList(packageManagerRunCmd, database, orm, _auth, hasNati
|
|
|
6024
5988
|
}
|
|
6025
5989
|
if (addons.includes("biome")) scripts += `
|
|
6026
5990
|
- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`;
|
|
5991
|
+
if (addons.includes("oxlint")) scripts += `
|
|
5992
|
+
- \`${packageManagerRunCmd} check\`: Run Oxlint and Oxfmt`;
|
|
6027
5993
|
if (addons.includes("pwa")) scripts += `
|
|
6028
5994
|
- \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`;
|
|
6029
5995
|
if (addons.includes("tauri")) scripts += `
|
|
@@ -6175,10 +6141,12 @@ async function displayPostInstallInstructions(config) {
|
|
|
6175
6141
|
const isBackendSelf = backend === "self";
|
|
6176
6142
|
const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
|
|
6177
6143
|
const cdCmd = `cd ${relativePath}`;
|
|
6178
|
-
const
|
|
6144
|
+
const hasHusky = addons?.includes("husky");
|
|
6145
|
+
const hasLinting = addons?.includes("biome") || addons?.includes("oxlint");
|
|
6179
6146
|
const databaseInstructions = !isConvex && database !== "none" ? await getDatabaseInstructions(database, orm, runCmd, runtime, dbSetup, serverDeploy, backend) : "";
|
|
6180
6147
|
const tauriInstructions = addons?.includes("tauri") ? getTauriInstructions(runCmd) : "";
|
|
6181
|
-
const
|
|
6148
|
+
const huskyInstructions = hasHusky ? getHuskyInstructions(runCmd) : "";
|
|
6149
|
+
const lintingInstructions = hasLinting ? getLintingInstructions(runCmd) : "";
|
|
6182
6150
|
const nativeInstructions = (frontend?.includes("native-bare") || frontend?.includes("native-uniwind") || frontend?.includes("native-unistyles")) && backend !== "none" ? getNativeInstructions(isConvex, isBackendSelf, frontend || []) : "";
|
|
6183
6151
|
const pwaInstructions = addons?.includes("pwa") && frontend?.includes("react-router") ? getPwaInstructions() : "";
|
|
6184
6152
|
const starlightInstructions = addons?.includes("starlight") ? getStarlightInstructions(runCmd) : "";
|
|
@@ -6232,6 +6200,7 @@ async function displayPostInstallInstructions(config) {
|
|
|
6232
6200
|
if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
|
|
6233
6201
|
if (databaseInstructions) output += `\n${databaseInstructions.trim()}\n`;
|
|
6234
6202
|
if (tauriInstructions) output += `\n${tauriInstructions.trim()}\n`;
|
|
6203
|
+
if (huskyInstructions) output += `\n${huskyInstructions.trim()}\n`;
|
|
6235
6204
|
if (lintingInstructions) output += `\n${lintingInstructions.trim()}\n`;
|
|
6236
6205
|
if (pwaInstructions) output += `\n${pwaInstructions.trim()}\n`;
|
|
6237
6206
|
if (alchemyDeployInstructions) output += `\n${alchemyDeployInstructions.trim()}\n`;
|
|
@@ -6253,6 +6222,9 @@ function getNativeInstructions(isConvex, isBackendSelf, _frontend) {
|
|
|
6253
6222
|
if (isConvex) instructions += `\n${pc.yellow("IMPORTANT:")} When using local development with Convex and native apps,\n ensure you use your local IP address instead of localhost or 127.0.0.1\n for proper connectivity.\n`;
|
|
6254
6223
|
return instructions;
|
|
6255
6224
|
}
|
|
6225
|
+
function getHuskyInstructions(runCmd) {
|
|
6226
|
+
return `${pc.bold("Git hooks with Husky:")}\n${pc.cyan("•")} Initialize hooks: ${`${runCmd} prepare`}\n`;
|
|
6227
|
+
}
|
|
6256
6228
|
function getLintingInstructions(runCmd) {
|
|
6257
6229
|
return `${pc.bold("Linting and formatting:")}\n${pc.cyan("•")} Format and lint fix: ${`${runCmd} check`}\n`;
|
|
6258
6230
|
}
|
package/package.json
CHANGED
|
@@ -1,60 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0-pr730.0ee9844",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "Aman Varshney",
|
|
8
|
-
"bin": {
|
|
9
|
-
"create-better-t-stack": "dist/cli.mjs"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"templates",
|
|
13
|
-
"dist"
|
|
14
|
-
],
|
|
15
5
|
"keywords": [
|
|
6
|
+
"better-auth",
|
|
16
7
|
"better-t-stack",
|
|
17
|
-
"
|
|
8
|
+
"biome",
|
|
18
9
|
"boilerplate",
|
|
19
|
-
"starter",
|
|
20
10
|
"cli",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"better-auth",
|
|
24
|
-
"monorepo",
|
|
25
|
-
"fullstack",
|
|
26
|
-
"type-safety",
|
|
27
|
-
"react",
|
|
28
|
-
"react-native",
|
|
11
|
+
"drizzle",
|
|
12
|
+
"elysia",
|
|
29
13
|
"expo",
|
|
14
|
+
"fullstack",
|
|
30
15
|
"hono",
|
|
31
|
-
"
|
|
32
|
-
"drizzle",
|
|
16
|
+
"monorepo",
|
|
33
17
|
"prisma",
|
|
34
|
-
"tanstack",
|
|
35
|
-
"tailwind",
|
|
36
|
-
"shadcn",
|
|
37
18
|
"pwa",
|
|
19
|
+
"react",
|
|
20
|
+
"react-native",
|
|
21
|
+
"shadcn",
|
|
22
|
+
"starter",
|
|
23
|
+
"tailwind",
|
|
24
|
+
"tanstack",
|
|
38
25
|
"tauri",
|
|
39
|
-
"
|
|
26
|
+
"trpc",
|
|
27
|
+
"turborepo",
|
|
28
|
+
"type-safety",
|
|
29
|
+
"typescript"
|
|
40
30
|
],
|
|
31
|
+
"homepage": "https://better-t-stack.dev/",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"author": "Aman Varshney",
|
|
41
34
|
"repository": {
|
|
42
35
|
"type": "git",
|
|
43
36
|
"url": "git+https://github.com/AmanVarshney01/create-better-t-stack.git",
|
|
44
37
|
"directory": "apps/cli"
|
|
45
38
|
},
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
},
|
|
49
|
-
"homepage": "https://better-t-stack.dev/",
|
|
50
|
-
"scripts": {
|
|
51
|
-
"build": "tsdown --publint",
|
|
52
|
-
"dev": "tsdown --watch",
|
|
53
|
-
"check-types": "tsc --noEmit",
|
|
54
|
-
"test": "bun run build && vitest run; rm -rf .smoke || true",
|
|
55
|
-
"test:ui": "bun run build && vitest --ui",
|
|
56
|
-
"prepublishOnly": "npm run build"
|
|
39
|
+
"bin": {
|
|
40
|
+
"create-better-t-stack": "dist/cli.mjs"
|
|
57
41
|
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"templates"
|
|
45
|
+
],
|
|
46
|
+
"type": "module",
|
|
58
47
|
"exports": {
|
|
59
48
|
".": {
|
|
60
49
|
"types": "./dist/index.d.mts",
|
|
@@ -64,10 +53,21 @@
|
|
|
64
53
|
"import": "./dist/cli.mjs"
|
|
65
54
|
}
|
|
66
55
|
},
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "tsdown --publint",
|
|
61
|
+
"dev": "tsdown --watch",
|
|
62
|
+
"check-types": "tsc --noEmit",
|
|
63
|
+
"test": "bun run build && bun test",
|
|
64
|
+
"test:watch": "bun run build && bun test --watch",
|
|
65
|
+
"test:coverage": "bun run build && bun test --coverage",
|
|
66
|
+
"test:ci": "bun run build && AGENT=1 bun test --bail=5",
|
|
67
|
+
"prepublishOnly": "npm run build"
|
|
68
|
+
},
|
|
67
69
|
"dependencies": {
|
|
68
|
-
"@better-t-stack/types": "
|
|
69
|
-
"@biomejs/js-api": "^4.0.0",
|
|
70
|
-
"@biomejs/wasm-nodejs": "^2.3.8",
|
|
70
|
+
"@better-t-stack/types": "3.9.0-pr730.0ee9844",
|
|
71
71
|
"@clack/prompts": "^1.0.0-alpha.8",
|
|
72
72
|
"@orpc/server": "^1.12.2",
|
|
73
73
|
"consola": "^3.4.2",
|
|
@@ -76,6 +76,7 @@
|
|
|
76
76
|
"gradient-string": "^3.0.0",
|
|
77
77
|
"handlebars": "^4.7.8",
|
|
78
78
|
"jsonc-parser": "^3.3.1",
|
|
79
|
+
"oxfmt": "^0.19.0",
|
|
79
80
|
"picocolors": "^1.1.1",
|
|
80
81
|
"tinyglobby": "^0.2.15",
|
|
81
82
|
"trpc-cli": "^0.12.1",
|
|
@@ -84,12 +85,11 @@
|
|
|
84
85
|
"zod": "^4.1.13"
|
|
85
86
|
},
|
|
86
87
|
"devDependencies": {
|
|
88
|
+
"@types/bun": "^1.2.17",
|
|
87
89
|
"@types/fs-extra": "^11.0.4",
|
|
88
90
|
"@types/node": "^24.10.2",
|
|
89
|
-
"@vitest/ui": "^4.0.15",
|
|
90
91
|
"publint": "^0.3.16",
|
|
91
92
|
"tsdown": "^0.17.2",
|
|
92
|
-
"typescript": "^5.9.3"
|
|
93
|
-
"vitest": "^4.0.15"
|
|
93
|
+
"typescript": "^5.9.3"
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
+
import { getAuthConfigProvider } from "@convex-dev/better-auth/auth-config";
|
|
2
|
+
import type { AuthConfig } from "convex/server";
|
|
3
|
+
|
|
1
4
|
export default {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
domain: process.env.CONVEX_SITE_URL,
|
|
5
|
-
applicationID: "convex",
|
|
6
|
-
},
|
|
7
|
-
],
|
|
8
|
-
};
|
|
5
|
+
providers: [getAuthConfigProvider()],
|
|
6
|
+
} satisfies AuthConfig;
|