create-better-t-stack 3.9.0 → 3.11.0-pr749.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/README.md +2 -1
- package/bin/create-better-t-stack +98 -0
- package/package.json +69 -59
- package/src/api.ts +203 -0
- package/src/cli.ts +185 -0
- package/src/constants.ts +270 -0
- package/src/helpers/addons/addons-setup.ts +201 -0
- package/src/helpers/addons/examples-setup.ts +137 -0
- package/src/helpers/addons/fumadocs-setup.ts +99 -0
- package/src/helpers/addons/oxlint-setup.ts +36 -0
- package/src/helpers/addons/ruler-setup.ts +135 -0
- package/src/helpers/addons/starlight-setup.ts +45 -0
- package/src/helpers/addons/tauri-setup.ts +90 -0
- package/src/helpers/addons/tui-setup.ts +64 -0
- package/src/helpers/addons/ultracite-setup.ts +228 -0
- package/src/helpers/addons/vite-pwa-setup.ts +59 -0
- package/src/helpers/addons/wxt-setup.ts +86 -0
- package/src/helpers/core/add-addons.ts +85 -0
- package/src/helpers/core/add-deployment.ts +102 -0
- package/src/helpers/core/api-setup.ts +280 -0
- package/src/helpers/core/auth-setup.ts +203 -0
- package/src/helpers/core/backend-setup.ts +69 -0
- package/src/helpers/core/command-handlers.ts +354 -0
- package/src/helpers/core/convex-codegen.ts +14 -0
- package/src/helpers/core/create-project.ts +134 -0
- package/src/helpers/core/create-readme.ts +694 -0
- package/src/helpers/core/db-setup.ts +184 -0
- package/src/helpers/core/detect-project-config.ts +41 -0
- package/src/helpers/core/env-setup.ts +481 -0
- package/src/helpers/core/git.ts +23 -0
- package/src/helpers/core/install-dependencies.ts +29 -0
- package/src/helpers/core/payments-setup.ts +48 -0
- package/src/helpers/core/post-installation.ts +403 -0
- package/src/helpers/core/project-config.ts +250 -0
- package/src/helpers/core/runtime-setup.ts +76 -0
- package/src/helpers/core/template-manager.ts +917 -0
- package/src/helpers/core/workspace-setup.ts +184 -0
- package/src/helpers/database-providers/d1-setup.ts +28 -0
- package/src/helpers/database-providers/docker-compose-setup.ts +50 -0
- package/src/helpers/database-providers/mongodb-atlas-setup.ts +182 -0
- package/src/helpers/database-providers/neon-setup.ts +240 -0
- package/src/helpers/database-providers/planetscale-setup.ts +78 -0
- package/src/helpers/database-providers/prisma-postgres-setup.ts +193 -0
- package/src/helpers/database-providers/supabase-setup.ts +196 -0
- package/src/helpers/database-providers/turso-setup.ts +309 -0
- package/src/helpers/deployment/alchemy/alchemy-combined-setup.ts +80 -0
- package/src/helpers/deployment/alchemy/alchemy-next-setup.ts +52 -0
- package/src/helpers/deployment/alchemy/alchemy-nuxt-setup.ts +105 -0
- package/src/helpers/deployment/alchemy/alchemy-react-router-setup.ts +33 -0
- package/src/helpers/deployment/alchemy/alchemy-solid-setup.ts +33 -0
- package/src/helpers/deployment/alchemy/alchemy-svelte-setup.ts +99 -0
- package/src/helpers/deployment/alchemy/alchemy-tanstack-router-setup.ts +34 -0
- package/src/helpers/deployment/alchemy/alchemy-tanstack-start-setup.ts +99 -0
- package/src/helpers/deployment/alchemy/env-dts-setup.ts +76 -0
- package/src/helpers/deployment/alchemy/index.ts +7 -0
- package/src/helpers/deployment/server-deploy-setup.ts +55 -0
- package/src/helpers/deployment/web-deploy-setup.ts +58 -0
- package/src/index.ts +51 -0
- package/src/prompts/addons.ts +200 -0
- package/src/prompts/api.ts +49 -0
- package/src/prompts/auth.ts +84 -0
- package/src/prompts/backend.ts +83 -0
- package/src/prompts/config-prompts.ts +138 -0
- package/src/prompts/database-setup.ts +112 -0
- package/src/prompts/database.ts +57 -0
- package/src/prompts/examples.ts +60 -0
- package/src/prompts/frontend.ts +118 -0
- package/src/prompts/git.ts +16 -0
- package/src/prompts/install.ts +16 -0
- package/src/prompts/orm.ts +53 -0
- package/src/prompts/package-manager.ts +32 -0
- package/src/prompts/payments.ts +50 -0
- package/src/prompts/project-name.ts +86 -0
- package/src/prompts/runtime.ts +47 -0
- package/src/prompts/server-deploy.ts +91 -0
- package/src/prompts/web-deploy.ts +107 -0
- package/src/tui/app.tsx +1062 -0
- package/src/types.ts +70 -0
- package/src/utils/add-package-deps.ts +57 -0
- package/src/utils/analytics.ts +39 -0
- package/src/utils/better-auth-plugin-setup.ts +71 -0
- package/src/utils/bts-config.ts +122 -0
- package/src/utils/command-exists.ts +16 -0
- package/src/utils/compatibility-rules.ts +337 -0
- package/src/utils/compatibility.ts +11 -0
- package/src/utils/config-processing.ts +130 -0
- package/src/utils/config-validation.ts +470 -0
- package/src/utils/display-config.ts +96 -0
- package/src/utils/docker-utils.ts +70 -0
- package/src/utils/errors.ts +30 -0
- package/src/utils/file-formatter.ts +11 -0
- package/src/utils/generate-reproducible-command.ts +53 -0
- package/src/utils/get-latest-cli-version.ts +27 -0
- package/src/utils/get-package-manager.ts +13 -0
- package/src/utils/open-url.ts +18 -0
- package/src/utils/package-runner.ts +23 -0
- package/src/utils/project-directory.ts +102 -0
- package/src/utils/project-name-validation.ts +43 -0
- package/src/utils/render-title.ts +48 -0
- package/src/utils/setup-catalogs.ts +192 -0
- package/src/utils/sponsors.ts +101 -0
- package/src/utils/telemetry.ts +19 -0
- package/src/utils/template-processor.ts +64 -0
- package/src/utils/templates.ts +94 -0
- package/src/utils/ts-morph.ts +26 -0
- package/src/validation.ts +117 -0
- 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 +17 -17
- package/templates/auth/better-auth/convex/backend/convex/http.ts.hbs +4 -4
- 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 +10 -10
- 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 +14 -12
- package/templates/auth/better-auth/convex/web/react/tanstack-start/src/components/user-menu.tsx.hbs +13 -16
- 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 +1 -1
- package/templates/auth/better-auth/web/react/next/src/components/user-menu.tsx.hbs +17 -15
- package/templates/auth/better-auth/web/react/react-router/src/components/user-menu.tsx.hbs +16 -15
- package/templates/auth/better-auth/web/react/tanstack-router/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
- package/templates/auth/better-auth/web/react/tanstack-start/src/components/{user-menu.tsx → user-menu.tsx.hbs} +16 -15
- package/templates/backend/convex/packages/backend/convex/README.md +4 -4
- package/templates/backend/convex/packages/backend/convex/convex.config.ts.hbs +17 -0
- package/templates/backend/convex/packages/backend/convex/tsconfig.json.hbs +1 -1
- package/templates/examples/ai/convex/packages/backend/convex/agent.ts.hbs +9 -0
- package/templates/examples/ai/convex/packages/backend/convex/chat.ts.hbs +67 -0
- package/templates/examples/ai/native/bare/app/(drawer)/ai.tsx.hbs +301 -3
- package/templates/examples/ai/native/unistyles/app/(drawer)/ai.tsx.hbs +296 -10
- package/templates/examples/ai/native/uniwind/app/(drawer)/ai.tsx.hbs +180 -1
- package/templates/examples/ai/web/react/next/src/app/ai/page.tsx.hbs +172 -9
- package/templates/examples/ai/web/react/react-router/src/routes/ai.tsx.hbs +156 -6
- package/templates/examples/ai/web/react/tanstack-router/src/routes/ai.tsx.hbs +156 -4
- package/templates/examples/ai/web/react/tanstack-start/src/routes/ai.tsx.hbs +159 -6
- 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/mode-toggle.tsx.hbs +4 -6
- 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} +4 -6
- 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} +4 -6
- 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 +58 -64
- package/dist/cli.d.mts +0 -1
- package/dist/cli.mjs +0 -8
- package/dist/index.d.mts +0 -347
- package/dist/index.mjs +0 -4
- package/dist/src-DLvUK0Qf.mjs +0 -7069
- package/templates/auth/better-auth/convex/backend/convex/convex.config.ts.hbs +0 -7
- package/templates/examples/ai/web/react/base/src/components/response.tsx.hbs +0 -22
- 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
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bin stub for create-better-t-stack.
|
|
5
|
+
* Finds and runs the platform-specific compiled binary.
|
|
6
|
+
*
|
|
7
|
+
* The binary is installed as an optional dependency:
|
|
8
|
+
* @better-t-stack/cli-{platform}-{arch}
|
|
9
|
+
*
|
|
10
|
+
* This stub searches node_modules for the matching binary package.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { spawnSync } from "node:child_process";
|
|
14
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { platform as osPlatform, arch as osArch } from "node:os";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = dirname(__filename);
|
|
21
|
+
|
|
22
|
+
function run(target) {
|
|
23
|
+
const result = spawnSync(target, process.argv.slice(2), {
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
});
|
|
26
|
+
if (result.error) {
|
|
27
|
+
console.error(result.error.message);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const code = typeof result.status === "number" ? result.status : 0;
|
|
31
|
+
process.exit(code);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Allow override via environment variable
|
|
35
|
+
const envPath = process.env.CREATE_BETTER_T_STACK_BIN_PATH;
|
|
36
|
+
if (envPath) {
|
|
37
|
+
run(envPath);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const scriptPath = realpathSync(__filename);
|
|
41
|
+
const scriptDir = dirname(scriptPath);
|
|
42
|
+
|
|
43
|
+
const platformMap = {
|
|
44
|
+
darwin: "darwin",
|
|
45
|
+
linux: "linux",
|
|
46
|
+
win32: "windows",
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const archMap = {
|
|
50
|
+
x64: "x64",
|
|
51
|
+
arm64: "arm64",
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
let platform = platformMap[osPlatform()];
|
|
55
|
+
if (!platform) {
|
|
56
|
+
platform = osPlatform();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let arch = archMap[osArch()];
|
|
60
|
+
if (!arch) {
|
|
61
|
+
arch = osArch();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Scoped package name: @better-t-stack/cli-{platform}-{arch}
|
|
65
|
+
const scopedPackage = "@better-t-stack/cli-" + platform + "-" + arch;
|
|
66
|
+
const binary = platform === "windows" ? "create-better-t-stack.exe" : "create-better-t-stack";
|
|
67
|
+
|
|
68
|
+
function findBinary(startDir) {
|
|
69
|
+
let current = startDir;
|
|
70
|
+
for (; ;) {
|
|
71
|
+
const modules = join(current, "node_modules");
|
|
72
|
+
if (existsSync(modules)) {
|
|
73
|
+
// Check for scoped package: node_modules/@better-t-stack/cli-{platform}-{arch}
|
|
74
|
+
const scopedPath = join(modules, "@better-t-stack", "cli-" + platform + "-" + arch);
|
|
75
|
+
const candidate = join(scopedPath, "bin", binary);
|
|
76
|
+
if (existsSync(candidate)) {
|
|
77
|
+
return candidate;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
const parent = dirname(current);
|
|
81
|
+
if (parent === current) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
current = parent;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const resolved = findBinary(scriptDir);
|
|
89
|
+
|
|
90
|
+
if (!resolved) {
|
|
91
|
+
console.error(
|
|
92
|
+
'No binary found for your platform (' + platform + '-' + arch + ').\n' +
|
|
93
|
+
'You can try manually installing the "' + scopedPackage + '" package.',
|
|
94
|
+
);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
run(resolved);
|
package/package.json
CHANGED
|
@@ -1,95 +1,105 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.11.0-pr749.0",
|
|
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
|
},
|
|
39
|
+
"bin": {
|
|
40
|
+
"create-better-t-stack": "./bin/create-better-t-stack"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"bin",
|
|
44
|
+
"src",
|
|
45
|
+
"templates"
|
|
46
|
+
],
|
|
47
|
+
"type": "module",
|
|
48
|
+
"main": "./src/index.ts",
|
|
49
|
+
"types": "./src/index.ts",
|
|
50
|
+
"exports": {
|
|
51
|
+
".": {
|
|
52
|
+
"types": "./src/index.ts",
|
|
53
|
+
"import": "./src/index.ts"
|
|
54
|
+
}
|
|
55
|
+
},
|
|
46
56
|
"publishConfig": {
|
|
47
57
|
"access": "public"
|
|
48
58
|
},
|
|
49
|
-
"homepage": "https://better-t-stack.dev/",
|
|
50
59
|
"scripts": {
|
|
51
|
-
"build": "
|
|
52
|
-
"
|
|
60
|
+
"build": "bun run scripts/build.ts",
|
|
61
|
+
"build:local": "bun run scripts/build.ts --single",
|
|
62
|
+
"dev": "bun run src/cli.ts",
|
|
53
63
|
"check-types": "tsc --noEmit",
|
|
54
|
-
"test": "bun
|
|
55
|
-
"test:watch": "bun
|
|
56
|
-
"test:coverage": "bun
|
|
57
|
-
"test:ci": "
|
|
58
|
-
"prepublishOnly": "
|
|
59
|
-
},
|
|
60
|
-
"exports": {
|
|
61
|
-
".": {
|
|
62
|
-
"types": "./dist/index.d.mts",
|
|
63
|
-
"import": "./dist/index.mjs"
|
|
64
|
-
},
|
|
65
|
-
"./cli": {
|
|
66
|
-
"import": "./dist/cli.mjs"
|
|
67
|
-
}
|
|
64
|
+
"test": "bun test",
|
|
65
|
+
"test:watch": "bun test --watch",
|
|
66
|
+
"test:coverage": "bun test --coverage",
|
|
67
|
+
"test:ci": "AGENT=1 bun test --bail=5",
|
|
68
|
+
"prepublishOnly": "echo 'Build binaries separately before publishing'"
|
|
68
69
|
},
|
|
69
70
|
"dependencies": {
|
|
70
|
-
"@better-t-stack/types": "
|
|
71
|
-
"@
|
|
72
|
-
"@
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"fs-extra": "^11.3.2",
|
|
76
|
-
"gradient-string": "^3.0.0",
|
|
71
|
+
"@better-t-stack/types": "workspace:*",
|
|
72
|
+
"@opentui/core": "^0.1.62",
|
|
73
|
+
"@opentui/react": "^0.1.62",
|
|
74
|
+
"commander": "^14.0.2",
|
|
75
|
+
"fs-extra": "^11.3.3",
|
|
77
76
|
"handlebars": "^4.7.8",
|
|
78
77
|
"jsonc-parser": "^3.3.1",
|
|
79
|
-
"
|
|
80
|
-
"picocolors": "^1.1.1",
|
|
78
|
+
"react": "^19.1.0",
|
|
81
79
|
"tinyglobby": "^0.2.15",
|
|
82
|
-
"trpc-cli": "^0.12.1",
|
|
83
80
|
"ts-morph": "^27.0.2",
|
|
84
81
|
"yaml": "^2.8.2",
|
|
85
|
-
"zod": "^4.1
|
|
82
|
+
"zod": "^4.2.1"
|
|
86
83
|
},
|
|
87
84
|
"devDependencies": {
|
|
88
|
-
"@types/bun": "^1.
|
|
85
|
+
"@types/bun": "^1.3.5",
|
|
89
86
|
"@types/fs-extra": "^11.0.4",
|
|
90
|
-
"@types/node": "^
|
|
91
|
-
"
|
|
92
|
-
"tsdown": "^0.
|
|
87
|
+
"@types/node": "^25.0.3",
|
|
88
|
+
"@types/react": "^19.2.7",
|
|
89
|
+
"tsdown": "^0.18.2",
|
|
93
90
|
"typescript": "^5.9.3"
|
|
91
|
+
},
|
|
92
|
+
"optionalDependencies": {
|
|
93
|
+
"@better-t-stack/cli-darwin-arm64": "3.11.0-pr749.0",
|
|
94
|
+
"@better-t-stack/cli-darwin-x64": "3.11.0-pr749.0",
|
|
95
|
+
"@better-t-stack/cli-linux-arm64": "3.11.0-pr749.0",
|
|
96
|
+
"@better-t-stack/cli-linux-x64": "3.11.0-pr749.0",
|
|
97
|
+
"@better-t-stack/cli-windows-x64": "3.11.0-pr749.0",
|
|
98
|
+
"@opentui/core-darwin-arm64": "0.1.63",
|
|
99
|
+
"@opentui/core-darwin-x64": "0.1.63",
|
|
100
|
+
"@opentui/core-linux-arm64": "0.1.63",
|
|
101
|
+
"@opentui/core-linux-x64": "0.1.63",
|
|
102
|
+
"@opentui/core-win32-arm64": "0.1.63",
|
|
103
|
+
"@opentui/core-win32-x64": "0.1.63"
|
|
94
104
|
}
|
|
95
105
|
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic API for create-better-t-stack
|
|
3
|
+
*
|
|
4
|
+
* This module provides a clean, programmatic interface for creating Better-T-Stack projects.
|
|
5
|
+
* It returns JSON results and never calls process.exit().
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import { getDefaultConfig } from "./constants";
|
|
11
|
+
import { createProject } from "./helpers/core/create-project";
|
|
12
|
+
import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
|
|
13
|
+
import type {
|
|
14
|
+
Addons,
|
|
15
|
+
API,
|
|
16
|
+
Auth,
|
|
17
|
+
Backend,
|
|
18
|
+
Database,
|
|
19
|
+
DatabaseSetup,
|
|
20
|
+
Examples,
|
|
21
|
+
Frontend,
|
|
22
|
+
ORM,
|
|
23
|
+
PackageManager,
|
|
24
|
+
Payments,
|
|
25
|
+
ProjectConfig,
|
|
26
|
+
Runtime,
|
|
27
|
+
ServerDeploy,
|
|
28
|
+
WebDeploy,
|
|
29
|
+
} from "./types";
|
|
30
|
+
|
|
31
|
+
export interface CreateOptions {
|
|
32
|
+
/** Project name or path */
|
|
33
|
+
projectName?: string;
|
|
34
|
+
/** Use defaults for unspecified options */
|
|
35
|
+
defaults?: boolean;
|
|
36
|
+
/** Frontend frameworks to use */
|
|
37
|
+
frontend?: Frontend[];
|
|
38
|
+
/** Backend framework */
|
|
39
|
+
backend?: Backend;
|
|
40
|
+
/** Runtime (bun or node) */
|
|
41
|
+
runtime?: Runtime;
|
|
42
|
+
/** Database type */
|
|
43
|
+
database?: Database;
|
|
44
|
+
/** ORM to use */
|
|
45
|
+
orm?: ORM;
|
|
46
|
+
/** Authentication provider */
|
|
47
|
+
auth?: Auth;
|
|
48
|
+
/** Payments provider */
|
|
49
|
+
payments?: Payments;
|
|
50
|
+
/** API type (trpc, orpc, etc.) */
|
|
51
|
+
api?: API;
|
|
52
|
+
/** Addons to include */
|
|
53
|
+
addons?: Addons[];
|
|
54
|
+
/** Examples to include */
|
|
55
|
+
examples?: Examples[];
|
|
56
|
+
/** Database setup method */
|
|
57
|
+
dbSetup?: DatabaseSetup;
|
|
58
|
+
/** Web deployment target */
|
|
59
|
+
webDeploy?: WebDeploy;
|
|
60
|
+
/** Server deployment target */
|
|
61
|
+
serverDeploy?: ServerDeploy;
|
|
62
|
+
/** Initialize git repository */
|
|
63
|
+
git?: boolean;
|
|
64
|
+
/** Package manager to use */
|
|
65
|
+
packageManager?: PackageManager;
|
|
66
|
+
/** Install dependencies after creation */
|
|
67
|
+
install?: boolean;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface CreateResult {
|
|
71
|
+
/** Whether the project was created successfully */
|
|
72
|
+
success: boolean;
|
|
73
|
+
/** Absolute path to the created project directory */
|
|
74
|
+
projectDirectory?: string;
|
|
75
|
+
/** Relative path from current working directory */
|
|
76
|
+
relativePath?: string;
|
|
77
|
+
/** The final project configuration */
|
|
78
|
+
config?: ProjectConfig;
|
|
79
|
+
/** Command to reproduce this configuration */
|
|
80
|
+
reproducibleCommand?: string;
|
|
81
|
+
/** Time taken in milliseconds */
|
|
82
|
+
elapsedTimeMs: number;
|
|
83
|
+
/** Error message if creation failed */
|
|
84
|
+
error?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Create a new Better-T-Stack project programmatically.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* import { create } from "create-better-t-stack";
|
|
93
|
+
*
|
|
94
|
+
* const result = await create({
|
|
95
|
+
* projectName: "my-app",
|
|
96
|
+
* frontend: ["tanstack-router"],
|
|
97
|
+
* backend: "hono",
|
|
98
|
+
* database: "sqlite",
|
|
99
|
+
* orm: "drizzle",
|
|
100
|
+
* defaults: true,
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* if (result.success) {
|
|
104
|
+
* console.log(`Created at: ${result.projectDirectory}`);
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export async function create(options: CreateOptions = {}): Promise<CreateResult> {
|
|
109
|
+
const startTime = Date.now();
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const defaultConfig = getDefaultConfig();
|
|
113
|
+
const projectName = options.projectName ?? defaultConfig.projectName;
|
|
114
|
+
const projectDir = path.resolve(process.cwd(), projectName);
|
|
115
|
+
const relativePath = projectName;
|
|
116
|
+
|
|
117
|
+
// Ensure directory doesn't exist or is empty
|
|
118
|
+
if (await fs.pathExists(projectDir)) {
|
|
119
|
+
const contents = await fs.readdir(projectDir);
|
|
120
|
+
if (contents.length > 0) {
|
|
121
|
+
return {
|
|
122
|
+
success: false,
|
|
123
|
+
elapsedTimeMs: Date.now() - startTime,
|
|
124
|
+
error: `Directory "${projectName}" already exists and is not empty`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Build final configuration
|
|
130
|
+
const config: ProjectConfig = {
|
|
131
|
+
projectName,
|
|
132
|
+
projectDir,
|
|
133
|
+
relativePath,
|
|
134
|
+
frontend:
|
|
135
|
+
options.frontend ?? (options.defaults ? defaultConfig.frontend : defaultConfig.frontend),
|
|
136
|
+
backend:
|
|
137
|
+
options.backend ?? (options.defaults ? defaultConfig.backend : defaultConfig.backend),
|
|
138
|
+
runtime:
|
|
139
|
+
options.runtime ?? (options.defaults ? defaultConfig.runtime : defaultConfig.runtime),
|
|
140
|
+
database:
|
|
141
|
+
options.database ?? (options.defaults ? defaultConfig.database : defaultConfig.database),
|
|
142
|
+
orm: options.orm ?? (options.defaults ? defaultConfig.orm : defaultConfig.orm),
|
|
143
|
+
auth: options.auth ?? (options.defaults ? defaultConfig.auth : defaultConfig.auth),
|
|
144
|
+
payments:
|
|
145
|
+
options.payments ?? (options.defaults ? defaultConfig.payments : defaultConfig.payments),
|
|
146
|
+
api: options.api ?? (options.defaults ? defaultConfig.api : defaultConfig.api),
|
|
147
|
+
addons: options.addons ?? (options.defaults ? defaultConfig.addons : defaultConfig.addons),
|
|
148
|
+
examples:
|
|
149
|
+
options.examples ?? (options.defaults ? defaultConfig.examples : defaultConfig.examples),
|
|
150
|
+
dbSetup:
|
|
151
|
+
options.dbSetup ?? (options.defaults ? defaultConfig.dbSetup : defaultConfig.dbSetup),
|
|
152
|
+
webDeploy:
|
|
153
|
+
options.webDeploy ?? (options.defaults ? defaultConfig.webDeploy : defaultConfig.webDeploy),
|
|
154
|
+
serverDeploy:
|
|
155
|
+
options.serverDeploy ??
|
|
156
|
+
(options.defaults ? defaultConfig.serverDeploy : defaultConfig.serverDeploy),
|
|
157
|
+
git: options.git ?? (options.defaults ? defaultConfig.git : defaultConfig.git),
|
|
158
|
+
packageManager:
|
|
159
|
+
options.packageManager ??
|
|
160
|
+
(options.defaults ? defaultConfig.packageManager : defaultConfig.packageManager),
|
|
161
|
+
install: options.install ?? false,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
// Create project directory
|
|
165
|
+
await fs.ensureDir(projectDir);
|
|
166
|
+
|
|
167
|
+
// Create the project (suppress console output)
|
|
168
|
+
const originalLog = console.log;
|
|
169
|
+
const originalInfo = console.info;
|
|
170
|
+
const originalWarn = console.warn;
|
|
171
|
+
console.log = () => {};
|
|
172
|
+
console.info = () => {};
|
|
173
|
+
console.warn = () => {};
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
await createProject(config, { manualDb: true });
|
|
177
|
+
} finally {
|
|
178
|
+
console.log = originalLog;
|
|
179
|
+
console.info = originalInfo;
|
|
180
|
+
console.warn = originalWarn;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const reproducibleCommand = generateReproducibleCommand(config);
|
|
184
|
+
const elapsedTimeMs = Date.now() - startTime;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
projectDirectory: projectDir,
|
|
189
|
+
relativePath,
|
|
190
|
+
config,
|
|
191
|
+
reproducibleCommand,
|
|
192
|
+
elapsedTimeMs,
|
|
193
|
+
};
|
|
194
|
+
} catch (error) {
|
|
195
|
+
return {
|
|
196
|
+
success: false,
|
|
197
|
+
elapsedTimeMs: Date.now() - startTime,
|
|
198
|
+
error: error instanceof Error ? error.message : String(error),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Types are exported above via the main export
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* CLI entry point with TUI support
|
|
4
|
+
* Replaces the old clack-prompts based CLI with OpenTUI
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { renderTui } from "./tui/app";
|
|
8
|
+
import { create, type CreateOptions } from "./api";
|
|
9
|
+
import { createProject } from "./helpers/core/create-project";
|
|
10
|
+
import { getDefaultConfig } from "./constants";
|
|
11
|
+
import { getLatestCLIVersion } from "./utils/get-latest-cli-version";
|
|
12
|
+
import { generateReproducibleCommand } from "./utils/generate-reproducible-command";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import type {
|
|
15
|
+
PackageManager,
|
|
16
|
+
Database,
|
|
17
|
+
ORM,
|
|
18
|
+
Backend,
|
|
19
|
+
Runtime,
|
|
20
|
+
Frontend,
|
|
21
|
+
Addons,
|
|
22
|
+
Examples,
|
|
23
|
+
DatabaseSetup,
|
|
24
|
+
API,
|
|
25
|
+
WebDeploy,
|
|
26
|
+
ServerDeploy,
|
|
27
|
+
DirectoryConflict,
|
|
28
|
+
Template,
|
|
29
|
+
Auth,
|
|
30
|
+
Payments,
|
|
31
|
+
ProjectConfig,
|
|
32
|
+
} from "./types";
|
|
33
|
+
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.name("create-better-t-stack")
|
|
38
|
+
.description("Create a new Better-T-Stack project")
|
|
39
|
+
.version(getLatestCLIVersion())
|
|
40
|
+
.argument("[project-name]", "Name of the project")
|
|
41
|
+
.option("-t, --template <template>", "Use a predefined template")
|
|
42
|
+
.option("-y, --yes", "Use default configuration (skip TUI)", false)
|
|
43
|
+
.option("--yolo", "(WARNING) Bypass validations and compatibility checks", false)
|
|
44
|
+
.option("--verbose", "Show detailed result information", false)
|
|
45
|
+
.option("--database <database>", "Database to use")
|
|
46
|
+
.option("--orm <orm>", "ORM to use")
|
|
47
|
+
.option("--auth <auth>", "Authentication provider")
|
|
48
|
+
.option("--payments <payments>", "Payments provider")
|
|
49
|
+
.option("--frontend <frontend...>", "Frontend framework(s)")
|
|
50
|
+
.option("--addons <addons...>", "Addons to include")
|
|
51
|
+
.option("--examples <examples...>", "Examples to include")
|
|
52
|
+
.option("--git", "Initialize git repository")
|
|
53
|
+
.option("--no-git", "Skip git initialization")
|
|
54
|
+
.option("--package-manager <pm>", "Package manager to use")
|
|
55
|
+
.option("--install", "Install dependencies")
|
|
56
|
+
.option("--no-install", "Skip dependency installation")
|
|
57
|
+
.option("--db-setup <setup>", "Database setup method")
|
|
58
|
+
.option("--backend <backend>", "Backend framework")
|
|
59
|
+
.option("--runtime <runtime>", "Runtime to use")
|
|
60
|
+
.option("--api <api>", "API framework")
|
|
61
|
+
.option("--web-deploy <deploy>", "Web deployment target")
|
|
62
|
+
.option("--server-deploy <deploy>", "Server deployment target")
|
|
63
|
+
.option("--directory-conflict <action>", "How to handle directory conflicts")
|
|
64
|
+
.option("--disable-analytics", "Disable analytics", false)
|
|
65
|
+
.option("--manual-db", "Skip automatic database setup prompt", false)
|
|
66
|
+
.action(async (projectName: string | undefined, options: Record<string, unknown>) => {
|
|
67
|
+
try {
|
|
68
|
+
// Check if we should use TUI (interactive mode)
|
|
69
|
+
const useInteractive = !options.yes && process.stdin.isTTY && process.stdout.isTTY;
|
|
70
|
+
|
|
71
|
+
if (useInteractive) {
|
|
72
|
+
// Launch TUI
|
|
73
|
+
await renderTui({
|
|
74
|
+
initialConfig: {
|
|
75
|
+
projectName,
|
|
76
|
+
frontend: options.frontend as Frontend[] | undefined,
|
|
77
|
+
backend: options.backend as Backend | undefined,
|
|
78
|
+
runtime: options.runtime as Runtime | undefined,
|
|
79
|
+
database: options.database as Database | undefined,
|
|
80
|
+
orm: options.orm as ORM | undefined,
|
|
81
|
+
auth: options.auth as Auth | undefined,
|
|
82
|
+
payments: options.payments as Payments | undefined,
|
|
83
|
+
addons: options.addons as Addons[] | undefined,
|
|
84
|
+
},
|
|
85
|
+
onComplete: async (config: ProjectConfig) => {
|
|
86
|
+
// Create project in logs panel
|
|
87
|
+
console.log("\nCreating project...\n");
|
|
88
|
+
|
|
89
|
+
await createProject(config, {
|
|
90
|
+
manualDb: options.manualDb as boolean,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const reproducibleCommand = generateReproducibleCommand(config);
|
|
94
|
+
console.log(`\n✓ Project created at: ${config.projectDir}`);
|
|
95
|
+
console.log(`\nReproducible command:\n${reproducibleCommand}\n`);
|
|
96
|
+
},
|
|
97
|
+
onCancel: () => {
|
|
98
|
+
console.log("\nOperation cancelled.\n");
|
|
99
|
+
process.exit(0);
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
} else {
|
|
103
|
+
// Non-interactive mode: use defaults or provided flags
|
|
104
|
+
const result = await create({
|
|
105
|
+
projectName,
|
|
106
|
+
defaults: options.yes as boolean,
|
|
107
|
+
frontend: options.frontend as Frontend[] | undefined,
|
|
108
|
+
backend: options.backend as Backend | undefined,
|
|
109
|
+
runtime: options.runtime as Runtime | undefined,
|
|
110
|
+
database: options.database as Database | undefined,
|
|
111
|
+
orm: options.orm as ORM | undefined,
|
|
112
|
+
auth: options.auth as Auth | undefined,
|
|
113
|
+
payments: options.payments as Payments | undefined,
|
|
114
|
+
api: options.api as API | undefined,
|
|
115
|
+
addons: options.addons as Addons[] | undefined,
|
|
116
|
+
examples: options.examples as Examples[] | undefined,
|
|
117
|
+
dbSetup: options.dbSetup as DatabaseSetup | undefined,
|
|
118
|
+
webDeploy: options.webDeploy as WebDeploy | undefined,
|
|
119
|
+
serverDeploy: options.serverDeploy as ServerDeploy | undefined,
|
|
120
|
+
git: options.git as boolean | undefined,
|
|
121
|
+
packageManager: options.packageManager as PackageManager | undefined,
|
|
122
|
+
install: options.install as boolean | undefined,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
if (result.success) {
|
|
126
|
+
if (options.verbose) {
|
|
127
|
+
console.log(JSON.stringify(result, null, 2));
|
|
128
|
+
} else {
|
|
129
|
+
console.log(`\n✓ Project created at: ${result.projectDirectory}`);
|
|
130
|
+
console.log(`\nReproducible command:\n${result.reproducibleCommand}\n`);
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
console.error(`\n✗ Failed to create project: ${result.error}\n`);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.error(`\n✗ Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
program
|
|
144
|
+
.command("add")
|
|
145
|
+
.description("Add addons or deployment configurations to an existing project")
|
|
146
|
+
.option("--addons <addons...>", "Addons to add")
|
|
147
|
+
.option("--web-deploy <deploy>", "Web deployment target")
|
|
148
|
+
.option("--server-deploy <deploy>", "Server deployment target")
|
|
149
|
+
.option("--project-dir <dir>", "Project directory")
|
|
150
|
+
.option("--install", "Install dependencies after adding", false)
|
|
151
|
+
.option("--package-manager <pm>", "Package manager to use")
|
|
152
|
+
.action(async (options: Record<string, unknown>) => {
|
|
153
|
+
// TODO: Implement add command with TUI
|
|
154
|
+
console.log("Add command - TUI coming soon");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
program
|
|
158
|
+
.command("docs")
|
|
159
|
+
.description("Open Better-T-Stack documentation")
|
|
160
|
+
.action(async () => {
|
|
161
|
+
const { openUrl } = await import("./utils/open-url");
|
|
162
|
+
const DOCS_URL = "https://better-t-stack.dev/docs";
|
|
163
|
+
try {
|
|
164
|
+
await openUrl(DOCS_URL);
|
|
165
|
+
console.log("Opened docs in your default browser.");
|
|
166
|
+
} catch {
|
|
167
|
+
console.log(`Please visit ${DOCS_URL}`);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
program
|
|
172
|
+
.command("builder")
|
|
173
|
+
.description("Open the web-based stack builder")
|
|
174
|
+
.action(async () => {
|
|
175
|
+
const { openUrl } = await import("./utils/open-url");
|
|
176
|
+
const BUILDER_URL = "https://better-t-stack.dev/new";
|
|
177
|
+
try {
|
|
178
|
+
await openUrl(BUILDER_URL);
|
|
179
|
+
console.log("Opened builder in your default browser.");
|
|
180
|
+
} catch {
|
|
181
|
+
console.log(`Please visit ${BUILDER_URL}`);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
program.parse();
|