create-better-t-stack 0.1.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 ADDED
@@ -0,0 +1,72 @@
1
+ # Create Better-T-Stack
2
+
3
+ A CLI tool to scaffold Better-T Stack projects with best practices and modern tooling.
4
+
5
+ ## Features
6
+
7
+ - 🚀 Quick project setup with one command
8
+ - 📦 TypeScript/JavaScript support
9
+ - 🗄️ Database options (libSQL/PostgreSQL)
10
+ - 🔒 Optional authentication setup
11
+ - 🐳 Docker configuration
12
+ - 🔄 GitHub Actions workflows
13
+ - 🎯 SEO optimization
14
+
15
+ ## Quick Start
16
+
17
+ ```bash
18
+ # Using npm
19
+ npx create-better-t my-app
20
+
21
+ # Using bun
22
+ bunx create-better-t my-app
23
+ ```
24
+
25
+ Just follow the interactive prompts!
26
+
27
+ ## Options
28
+
29
+ ```bash
30
+ Usage: create-better-t [project-directory] [options]
31
+
32
+ Options:
33
+ --typescript Use TypeScript (default)
34
+ --javascript Use JavaScript
35
+ --git Initialize git repository (default)
36
+ --no-git Skip git initialization
37
+ -h, --help Display help
38
+ ```
39
+
40
+ ## Project Structure
41
+
42
+ The generated project follows the Better-T Stack architecture:
43
+ - Built with Bun
44
+ - Type-safe database with DrizzleORM
45
+ - Simple authentication system
46
+ - Modern development practices
47
+
48
+ ## Development
49
+
50
+ To contribute to this CLI:
51
+
52
+ ```bash
53
+ # Clone the repository
54
+ git clone https://github.com/your-username/better-t-stack-cli.git
55
+
56
+ # Install dependencies
57
+ bun install
58
+
59
+ # Start development
60
+ bun dev
61
+
62
+ # Build
63
+ bun run build
64
+ ```
65
+
66
+ ## License
67
+
68
+ MIT
69
+
70
+ ## Credits
71
+
72
+ Developed by Nitish Singh & Aman Varshney – Built on top of the Better-T Stack by [Aman Varshney](https://github.com/AmanVarshney01/Better-T-Stack)
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,316 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { checkbox, confirm as confirm2, input as input2, select } from "@inquirer/prompts";
5
+ import chalk2 from "chalk";
6
+ import { Command } from "commander";
7
+
8
+ // src/create-project.ts
9
+ import path2 from "node:path";
10
+ import { execa as execa2 } from "execa";
11
+ import fs2 from "fs-extra";
12
+ import ora2 from "ora";
13
+
14
+ // src/helpers/db-setup.ts
15
+ import os from "node:os";
16
+ import path from "node:path";
17
+ import { confirm, input } from "@inquirer/prompts";
18
+ import { execa } from "execa";
19
+ import fs from "fs-extra";
20
+ import ora from "ora";
21
+
22
+ // src/utils/logger.ts
23
+ import chalk from "chalk";
24
+ var logger = {
25
+ error(...args) {
26
+ console.log(chalk.red(...args));
27
+ },
28
+ warn(...args) {
29
+ console.log(chalk.yellow(...args));
30
+ },
31
+ info(...args) {
32
+ console.log(chalk.cyan(...args));
33
+ },
34
+ success(...args) {
35
+ console.log(chalk.green(...args));
36
+ }
37
+ };
38
+
39
+ // src/helpers/db-setup.ts
40
+ async function isTursoInstalled() {
41
+ try {
42
+ await execa("turso", ["--version"]);
43
+ return true;
44
+ } catch {
45
+ return false;
46
+ }
47
+ }
48
+ async function isTursoLoggedIn() {
49
+ try {
50
+ await execa("turso", ["auth", "whoami"]);
51
+ return true;
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+ async function installTursoCLI(isMac, spinner) {
57
+ try {
58
+ if (await isTursoLoggedIn()) {
59
+ spinner.succeed("Turso CLI already logged in!");
60
+ return;
61
+ }
62
+ spinner.start("Installing Turso CLI...");
63
+ if (isMac) {
64
+ await execa("brew", ["install", "tursodatabase/tap/turso"]);
65
+ } else {
66
+ const installScript = await execa("curl", [
67
+ "-sSfL",
68
+ "https://get.tur.so/install.sh"
69
+ ]);
70
+ await execa("bash", [], { input: installScript.stdout });
71
+ }
72
+ spinner.succeed("Turso CLI installed successfully!");
73
+ spinner.start("Logging in to Turso...");
74
+ await execa("turso", ["auth", "login"]);
75
+ spinner.succeed("Logged in to Turso!");
76
+ } catch (error) {
77
+ if (error instanceof Error && error.message.includes("User force closed")) {
78
+ spinner.stop();
79
+ console.log("\n");
80
+ logger.warn("Turso CLI installation cancelled by user");
81
+ throw error;
82
+ }
83
+ logger.error("Error during Turso CLI installation:", error);
84
+ spinner.fail(
85
+ "Failed to install Turso CLI. Proceeding with manual setup..."
86
+ );
87
+ throw error;
88
+ }
89
+ }
90
+ async function setupTurso(projectDir) {
91
+ const spinner = ora();
92
+ const platform = os.platform();
93
+ const isMac = platform === "darwin";
94
+ let canInstallCLI = platform !== "win32";
95
+ let installTurso = true;
96
+ const isCliInstalled = await isTursoInstalled();
97
+ if (canInstallCLI && !isCliInstalled) {
98
+ installTurso = await confirm({
99
+ message: "Would you like to install Turso CLI?",
100
+ default: true
101
+ });
102
+ }
103
+ canInstallCLI = canInstallCLI && installTurso;
104
+ if (canInstallCLI) {
105
+ try {
106
+ await installTursoCLI(isMac, spinner);
107
+ const defaultDbName = path.basename(projectDir);
108
+ const dbName = await input({
109
+ message: `Enter database name (default: ${defaultDbName}):`,
110
+ default: defaultDbName
111
+ });
112
+ spinner.start(`Creating Turso database "${dbName}"...`);
113
+ await execa("turso", ["db", "create", dbName]);
114
+ const { stdout: dbUrl } = await execa("turso", [
115
+ "db",
116
+ "show",
117
+ dbName,
118
+ "--url"
119
+ ]);
120
+ const { stdout: authToken } = await execa("turso", [
121
+ "db",
122
+ "tokens",
123
+ "create",
124
+ dbName
125
+ ]);
126
+ const envPath = path.join(projectDir, "packages/server", ".env");
127
+ const envContent = `TURSO_DATABASE_URL="${dbUrl.trim()}"
128
+ TURSO_AUTH_TOKEN="${authToken.trim()}"`;
129
+ await fs.writeFile(envPath, envContent);
130
+ spinner.succeed("Turso database configured successfully!");
131
+ return;
132
+ } catch (error) {
133
+ logger.error("Error during Turso database creation:", error);
134
+ spinner.fail(
135
+ "Failed to install Turso CLI. Proceeding with manual setup..."
136
+ );
137
+ installTurso = false;
138
+ }
139
+ }
140
+ if (!installTurso) {
141
+ const envPath = path.join(projectDir, "packages/server", ".env");
142
+ const envContent = `TURSO_DATABASE_URL=
143
+ TURSO_AUTH_TOKEN=`;
144
+ await fs.writeFile(envPath, envContent);
145
+ logger.info("\n\u{1F4DD} Manual Turso Setup Instructions:");
146
+ logger.info("1. Visit https://turso.tech and create an account");
147
+ logger.info("2. Create a new database from the dashboard");
148
+ logger.info("3. Get your database URL and authentication token");
149
+ logger.info(
150
+ "4. Add these credentials to the .env file in your project root"
151
+ );
152
+ logger.info("\nThe .env file has been created with placeholder variables:");
153
+ logger.info("TURSO_DATABASE_URL=your_database_url");
154
+ logger.info("TURSO_AUTH_TOKEN=your_auth_token");
155
+ }
156
+ }
157
+
158
+ // src/create-project.ts
159
+ async function createProject(options) {
160
+ const spinner = ora2("Creating project directory...").start();
161
+ const projectDir = path2.resolve(process.cwd(), options.projectName);
162
+ try {
163
+ await fs2.ensureDir(projectDir);
164
+ spinner.succeed();
165
+ spinner.start("Cloning template repository...");
166
+ await execa2("npx", [
167
+ "degit",
168
+ "https://github.com/AmanVarshney01/Better-T-Stack.git",
169
+ projectDir
170
+ ]);
171
+ spinner.succeed();
172
+ if (options.git) {
173
+ spinner.start("Initializing git repository...");
174
+ await execa2("git", ["init"], { cwd: projectDir });
175
+ spinner.succeed();
176
+ }
177
+ spinner.start("Installing dependencies...");
178
+ await execa2("bun", ["install"], { cwd: projectDir });
179
+ spinner.succeed();
180
+ if (options.database === "libsql") {
181
+ await setupTurso(projectDir);
182
+ }
183
+ logger.success("\n\u2728 Project created successfully!\n");
184
+ logger.info("Next steps:");
185
+ logger.info(` cd ${options.projectName}`);
186
+ logger.info(" bun dev");
187
+ } catch (error) {
188
+ spinner.fail("Failed to create project");
189
+ logger.error("Error during project creation:", error);
190
+ process.exit(1);
191
+ }
192
+ }
193
+
194
+ // src/render-title.ts
195
+ import gradient from "gradient-string";
196
+
197
+ // src/consts.ts
198
+ var TITLE_TEXT = `
199
+ \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
200
+ \u2551 \u2551
201
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2551
202
+ \u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2551
203
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2551
204
+ \u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557 \u2551
205
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551 \u2551
206
+ \u2551 \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u2551
207
+ \u2551 \u2551
208
+ \u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557 \u2551
209
+ \u2551 \u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D \u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D \u2551
210
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2551
211
+ \u2551 \u2588\u2588\u2551 \u255A\u2550\u2550\u2550\u2550\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2551
212
+ \u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2557 \u2551
213
+ \u2551 \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u2551
214
+ \u2551 \u2551
215
+ \u2551 The Modern Full-Stack Framework \u2551
216
+ \u2551 \u2551
217
+ \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
218
+ `;
219
+
220
+ // src/render-title.ts
221
+ var catppuccinTheme = {
222
+ rosewater: "#F5E0DC",
223
+ flamingo: "#F2CDCD",
224
+ pink: "#F5C2E7",
225
+ mauve: "#CBA6F7",
226
+ red: "#F38BA8",
227
+ maroon: "#E78284",
228
+ peach: "#FAB387",
229
+ yellow: "#F9E2AF",
230
+ green: "#A6E3A1",
231
+ teal: "#94E2D5",
232
+ sky: "#89DCEB",
233
+ sapphire: "#74C7EC",
234
+ lavender: "#B4BEFE"
235
+ };
236
+ var renderTitle = () => {
237
+ const catppuccinGradient = gradient(Object.values(catppuccinTheme));
238
+ console.log(catppuccinGradient.multiline(TITLE_TEXT));
239
+ };
240
+
241
+ // src/index.ts
242
+ var program = new Command();
243
+ async function main() {
244
+ try {
245
+ renderTitle();
246
+ console.log(chalk2.bold("\n\u{1F680} Creating a new Better-T Stack project...\n"));
247
+ const projectName = await input2({
248
+ message: "Project name:",
249
+ default: "my-better-t-app"
250
+ });
251
+ const database = await select({
252
+ message: chalk2.cyan("Select database:"),
253
+ choices: [
254
+ {
255
+ value: "libsql",
256
+ name: "libSQL",
257
+ description: chalk2.dim(
258
+ "(Recommended) - Turso's embedded SQLite database"
259
+ )
260
+ },
261
+ {
262
+ value: "postgres",
263
+ name: "PostgreSQL",
264
+ description: chalk2.dim("Traditional relational database")
265
+ }
266
+ ]
267
+ });
268
+ const auth = await confirm2({
269
+ message: "Add authentication with Better-Auth?",
270
+ default: true
271
+ });
272
+ const features = await checkbox({
273
+ message: chalk2.cyan("Select additional features:"),
274
+ choices: [
275
+ {
276
+ value: "docker",
277
+ name: "Docker setup",
278
+ description: chalk2.dim("Containerize your application")
279
+ },
280
+ {
281
+ value: "github-actions",
282
+ name: "GitHub Actions",
283
+ description: chalk2.dim("CI/CD workflows")
284
+ },
285
+ {
286
+ value: "SEO",
287
+ name: "Basic SEO setup",
288
+ description: chalk2.dim("Search engine optimization configuration")
289
+ }
290
+ ]
291
+ });
292
+ const projectOptions = {
293
+ projectName,
294
+ git: true,
295
+ database,
296
+ auth,
297
+ features
298
+ };
299
+ await createProject(projectOptions);
300
+ } catch (error) {
301
+ if (error instanceof Error && error.message.includes("User force closed")) {
302
+ console.log("\n");
303
+ logger.warn("Operation cancelled by user");
304
+ process.exit(0);
305
+ }
306
+ logger.error("An unexpected error occurred:", error);
307
+ process.exit(1);
308
+ }
309
+ }
310
+ process.on("SIGINT", () => {
311
+ console.log("\n");
312
+ logger.warn("Operation cancelled by user");
313
+ process.exit(0);
314
+ });
315
+ program.name("create-better-t-stack").description("Create a new Better-T Stack project").version("1.0.0").action(main);
316
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "create-better-t-stack",
3
+ "version": "0.1.0",
4
+ "description": "CLI tool to scaffold Better-T Stack projects",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "create-better-t-stack": "dist/index.js"
9
+ },
10
+ "keywords": [],
11
+ "scripts": {
12
+ "build": "tsup",
13
+ "dev": "tsup --watch",
14
+ "check-types": "tsc --noEmit",
15
+ "check": "biome check --write .",
16
+ "test": "vitest run",
17
+ "prepublishOnly": "npm run build"
18
+ },
19
+ "files": ["dist", "templates"],
20
+ "dependencies": {
21
+ "@inquirer/prompts": "^7.3.1",
22
+ "chalk": "^5.3.0",
23
+ "commander": "^13.1.0",
24
+ "execa": "^8.0.1",
25
+ "fs-extra": "^11.2.0",
26
+ "gradient-string": "^3.0.0",
27
+ "ora": "^7.0.1"
28
+ },
29
+ "devDependencies": {
30
+ "@types/fs-extra": "^11.0.4",
31
+ "@types/inquirer": "^9.0.7",
32
+ "@types/node": "^20.10.5",
33
+ "tsup": "^8.0.1",
34
+ "typescript": "^5.3.3",
35
+ "vitest": "^1.1.0"
36
+ }
37
+ }