From d8875f00732288478102c5ae67ee8c49aa729b3c Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Thu, 13 Feb 2025 22:20:07 +0530 Subject: [PATCH] bug fixes --- .changeset/tasty-points-kneel.md | 5 + .zed/settings.json | 11 +- apps/cli/src/helpers/create-project.ts | 25 +-- apps/cli/src/helpers/db-setup.ts | 121 ++++++++----- apps/cli/src/index.ts | 237 +++++++++++++------------ 5 files changed, 223 insertions(+), 176 deletions(-) create mode 100644 .changeset/tasty-points-kneel.md diff --git a/.changeset/tasty-points-kneel.md b/.changeset/tasty-points-kneel.md new file mode 100644 index 0000000..340577e --- /dev/null +++ b/.changeset/tasty-points-kneel.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": patch +--- + +bug fixes diff --git a/.zed/settings.json b/.zed/settings.json index 5cb0db5..916b5a0 100644 --- a/.zed/settings.json +++ b/.zed/settings.json @@ -1,3 +1,12 @@ { - "language_servers": ["typescript-language-server", "!vtsls", "biome", "..."] + "language_servers": ["typescript-language-server", "!vtsls", "biome", "..."], + "formatter": { + "language_server": { + "name": "biome" + } + }, + "code_actions_on_format": { + "source.fixAll.biome": true, + "source.organizeImports.biome": true + } } diff --git a/apps/cli/src/helpers/create-project.ts b/apps/cli/src/helpers/create-project.ts index 04ccc61..065098d 100644 --- a/apps/cli/src/helpers/create-project.ts +++ b/apps/cli/src/helpers/create-project.ts @@ -12,7 +12,7 @@ export async function createProject(options: ProjectConfig) { let shouldInstallDeps = false; try { - await tasks([ + const tasksList = [ { title: "Creating project directory", task: async () => { @@ -33,15 +33,22 @@ export async function createProject(options: ProjectConfig) { } }, }, - { + ]; + + if (options.git) { + tasksList.push({ title: "Initializing git repository", task: async () => { - if (options.git) { - await $`git init ${projectDir}`; - } + await $`git init ${projectDir}`; }, - }, - ]); + }); + } + + await tasks(tasksList); + + if (options.database === "libsql") { + await setupTurso(projectDir); + } const installDepsResponse = await confirm({ message: `📦 Install dependencies using ${options.packageManager}?`, @@ -71,10 +78,6 @@ export async function createProject(options: ProjectConfig) { } } - if (options.database === "libsql") { - await setupTurso(projectDir); - } - log.success("✨ Project created successfully!\n"); log.info(chalk.dim("Next steps:")); log.info(` cd ${options.projectName}`); diff --git a/apps/cli/src/helpers/db-setup.ts b/apps/cli/src/helpers/db-setup.ts index cd1fe7f..742e6e9 100644 --- a/apps/cli/src/helpers/db-setup.ts +++ b/apps/cli/src/helpers/db-setup.ts @@ -1,6 +1,19 @@ import os from "node:os"; import path from "node:path"; -import * as p from "@clack/prompts"; +import { + cancel, + confirm, + group, + intro, + isCancel, + log, + multiselect, + outro, + select, + spinner, + tasks, + text, +} from "@clack/prompts"; import { $ } from "execa"; import fs from "fs-extra"; import { isTursoInstalled, isTursoLoggedIn } from "../utils/turso-cli"; @@ -11,21 +24,21 @@ interface TursoConfig { } async function loginToTurso() { - const spinner = p.spinner(); + const s = spinner(); try { - spinner.start("Logging in to Turso..."); + s.start("Logging in to Turso..."); await $`turso auth login`; - spinner.stop("Logged in to Turso successfully!"); + s.stop("Logged in to Turso successfully!"); } catch (error) { - spinner.stop("Failed to log in to Turso"); + s.stop("Failed to log in to Turso"); throw error; } } async function installTursoCLI(isMac: boolean) { - const spinner = p.spinner(); + const s = spinner(); try { - spinner.start("Installing Turso CLI..."); + s.start("Installing Turso CLI..."); if (isMac) { await $`brew install tursodatabase/tap/turso`; @@ -35,14 +48,14 @@ async function installTursoCLI(isMac: boolean) { await $`bash -c '${installScript}'`; } - spinner.stop("Turso CLI installed successfully!"); + s.stop("Turso CLI installed successfully!"); } catch (error) { if (error instanceof Error && error.message.includes("User force closed")) { - spinner.stop(); - p.log.warn("Turso CLI installation cancelled by user"); + s.stop(); + log.warn("Turso CLI installation cancelled by user"); throw new Error("Installation cancelled"); } - spinner.stop("Failed to install Turso CLI"); + s.stop("Failed to install Turso CLI"); throw error; } } @@ -78,42 +91,38 @@ TURSO_AUTH_TOKEN=`; } function displayManualSetupInstructions() { - p.log.info("📝 Manual Turso Setup Instructions:"); - p.log.info("1. Visit https://turso.tech and create an account"); - p.log.info("2. Create a new database from the dashboard"); - p.log.info("3. Get your database URL and authentication token"); - p.log.info( - "4. Add these credentials to the .env file in packages/server/.env", - ); - p.log.info("\nThe .env file has been created with placeholder variables:"); - p.log.info("TURSO_DATABASE_URL=your_database_url"); - p.log.info("TURSO_AUTH_TOKEN=your_auth_token"); + log.info("📝 Manual Turso Setup Instructions:"); + log.info("1. Visit https://turso.tech and create an account"); + log.info("2. Create a new database from the dashboard"); + log.info("3. Get your database URL and authentication token"); + log.info("4. Add these credentials to the .env file in packages/server/.env"); + log.info("\nThe .env file has been created with placeholder variables:"); + log.info("TURSO_DATABASE_URL=your_database_url"); + log.info("TURSO_AUTH_TOKEN=your_auth_token"); } export async function setupTurso(projectDir: string) { - p.intro("Setting up Turso..."); - const platform = os.platform(); const isMac = platform === "darwin"; const canInstallCLI = platform !== "win32"; - try { - if (!canInstallCLI) { - p.log.warn("Automatic Turso setup is not supported on Windows."); - await writeEnvFile(projectDir); - displayManualSetupInstructions(); - return; - } + if (!canInstallCLI) { + log.warn("Automatic Turso setup is not supported on Windows."); + await writeEnvFile(projectDir); + displayManualSetupInstructions(); + return; + } + try { const isCliInstalled = await isTursoInstalled(); if (!isCliInstalled) { - const shouldInstall = await p.confirm({ + const shouldInstall = await confirm({ message: "Would you like to install Turso CLI?", }); - if (p.isCancel(shouldInstall)) { - p.cancel("Operation cancelled"); + if (isCancel(shouldInstall)) { + cancel("Operation cancelled"); process.exit(0); } @@ -123,12 +132,34 @@ export async function setupTurso(projectDir: string) { return; } - await installTursoCLI(isMac); + const s = spinner(); + s.start("Installing Turso CLI..."); + try { + if (isMac) { + await $`brew install tursodatabase/tap/turso`; + } else { + const { stdout: installScript } = + await $`curl -sSfL https://get.tur.so/install.sh`; + await $`bash -c '${installScript}'`; + } + s.stop("Turso CLI installed successfully!"); + } catch (error) { + s.stop("Failed to install Turso CLI"); + throw error; + } } const isLoggedIn = await isTursoLoggedIn(); if (!isLoggedIn) { - await loginToTurso(); + const s = spinner(); + s.start("Logging in to Turso..."); + try { + await $`turso auth login`; + s.stop("Logged in to Turso successfully!"); + } catch (error) { + s.stop("Failed to log in to Turso"); + throw error; + } } let success = false; @@ -136,40 +167,38 @@ export async function setupTurso(projectDir: string) { let suggestedName = path.basename(projectDir); while (!success) { - const dbNameResponse = await p.text({ + const dbNameResponse = await text({ message: "Enter database name:", defaultValue: suggestedName, }); - if (p.isCancel(dbNameResponse)) { - p.cancel("Operation cancelled"); + if (isCancel(dbNameResponse)) { + cancel("Operation cancelled"); process.exit(0); } dbName = dbNameResponse as string; - const spinner = p.spinner(); + const s = spinner(); try { - spinner.start(`Creating Turso database "${dbName}"...`); + s.start(`Creating Turso database "${dbName}"...`); const config = await createTursoDatabase(dbName); await writeEnvFile(projectDir, config); - spinner.stop("Turso database configured successfully!"); + s.stop("Turso database configured successfully!"); success = true; } catch (error) { if (error instanceof Error && error.message === "DATABASE_EXISTS") { - spinner.stop(`Database "${dbName}" already exists`); + s.stop(`Database "${dbName}" already exists`); suggestedName = `${dbName}-${Math.floor(Math.random() * 1000)}`; } else { throw error; } } } - - p.outro("Turso setup completed successfully!"); } catch (error) { - p.log.error(`Error during Turso setup: ${error}`); + log.error(`Error during Turso setup: ${error}`); await writeEnvFile(projectDir); displayManualSetupInstructions(); - p.outro("Setup completed with manual configuration required."); + outro("Setup completed with manual configuration required."); } } diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index aa93970..f7d9192 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -3,6 +3,7 @@ import { confirm, group, intro, + log, multiselect, outro, select, @@ -25,8 +26,7 @@ import { getUserPkgManager } from "./utils/get-package-manager"; import { getVersion } from "./utils/get-version"; process.on("SIGINT", () => { - console.log("\n"); - chalk.red("Operation cancelled"); + log.error("Operation cancelled"); process.exit(0); }); @@ -35,102 +35,119 @@ const program = new Command(); async function gatherConfig( flags: Partial, ): Promise { - const result = await group({ - projectName: () => - text({ - message: "📝 Project name", - placeholder: "my-better-t-app", - defaultValue: flags.projectName || "my-better-t-app", - validate: (value) => { - if (!value) return "Project name is required"; - }, - }), - database: () => - !flags.database - ? select({ - message: "💾 Select database", - options: [ - { - value: "libsql", - label: "libSQL", - hint: "✨ (Recommended) - Turso's embedded SQLite database", - }, - { - value: "postgres", - label: "PostgreSQL", - hint: "🐘 Traditional relational database", - }, - ], - }) - : Promise.resolve(flags.database), - auth: () => - flags.auth === undefined - ? confirm({ - message: "🔐 Add authentication with Better-Auth?", - }) - : Promise.resolve(flags.auth), - features: () => - !flags.features - ? multiselect({ - message: "🎯 Select additional features", - options: [ - { - value: "docker", - label: "Docker setup", - hint: "🐳 Containerize your application", - }, - { - value: "github-actions", - label: "GitHub Actions", - hint: "⚡ CI/CD workflows", - }, - { - value: "SEO", - label: "Basic SEO setup", - hint: "🔍 Search engine optimization configuration", - }, - ], - }) - : Promise.resolve(flags.features), - packageManager: async () => { - const detectedPackageManager = getUserPkgManager(); + const shouldAskGit = flags.git !== false; - const useDetected = await confirm({ - message: `📦 Use detected package manager (${detectedPackageManager})?`, - }); + const result = await group( + { + projectName: () => + text({ + message: "📝 Project name", + placeholder: "my-better-t-app", + initialValue: flags.projectName, + validate: (value) => { + if (!value) return "Project name is required"; + }, + }), + database: () => + !flags.database + ? select({ + message: "💾 Select database", + options: [ + { + value: "libsql", + label: "libSQL", + hint: "✨ (Recommended) - Turso's embedded SQLite database", + }, + { + value: "postgres", + label: "PostgreSQL", + hint: "🐘 Traditional relational database", + }, + ], + }) + : Promise.resolve(flags.database), + auth: () => + flags.auth === undefined + ? confirm({ + message: "🔐 Add authentication with Better-Auth?", + }) + : Promise.resolve(flags.auth), + features: () => + !flags.features + ? multiselect({ + message: "🎯 Select additional features", + options: [ + { + value: "docker", + label: "Docker setup", + hint: "🐳 Containerize your application", + }, + { + value: "github-actions", + label: "GitHub Actions", + hint: "⚡ CI/CD workflows", + }, + { + value: "SEO", + label: "Basic SEO setup", + hint: "🔍 Search engine optimization configuration", + }, + ], + }) + : Promise.resolve(flags.features), + git: () => + shouldAskGit + ? confirm({ + message: "🗃️ Initialize Git repository?", + initialValue: true, + }) + : Promise.resolve(false), + packageManager: async () => { + const detectedPackageManager = getUserPkgManager(); - if (useDetected) return detectedPackageManager; + const useDetected = await confirm({ + message: `📦 Use detected package manager (${detectedPackageManager})?`, + }); - return select({ - message: "📦 Select package manager", - options: [ - { value: "npm", label: "npm", hint: "Node Package Manager" }, - { - value: "yarn", - label: "yarn", - hint: "Fast, reliable, and secure dependency management", - }, - { - value: "pnpm", - label: "pnpm", - hint: "Fast, disk space efficient package manager", - }, - { - value: "bun", - label: "bun", - hint: "All-in-one JavaScript runtime & toolkit", - }, - ], - }); + if (useDetected) return detectedPackageManager; + + return select({ + message: "📦 Select package manager", + options: [ + { value: "npm", label: "npm", hint: "Node Package Manager" }, + { + value: "bun", + label: "bun", + hint: "All-in-one JavaScript runtime & toolkit (recommended)", + }, + { + value: "pnpm", + label: "pnpm", + hint: "Fast, disk space efficient package manager", + }, + { + value: "yarn", + label: "yarn", + hint: "Fast, reliable, and secure dependency management", + }, + ], + }); + }, }, - }); + { + onCancel: () => { + cancel("Operation cancelled."); + process.exit(0); + }, + }, + ); return { projectName: result.projectName as string, database: (result.database as ProjectDatabase) ?? "libsql", auth: (result.auth as boolean) ?? true, features: (result.features as ProjectFeature[]) ?? [], - git: flags.git ?? true, + git: (result.git as boolean) ?? true, packageManager: (result.packageManager as PackageManager) ?? "npm", }; } @@ -203,43 +220,27 @@ async function main() { git: chalk.cyan(config.git), }; - console.log(); - console.log( - chalk.dim("├─") + - chalk.blue(" Project Name: ") + - colorizedConfig.projectName, - ); - console.log( - chalk.dim("├─") + chalk.blue(" Database: ") + colorizedConfig.database, - ); - console.log( - chalk.dim("├─") + - chalk.blue(" Authentication: ") + - colorizedConfig.auth, - ); - console.log( - chalk.dim("├─") + - chalk.blue(" Features: ") + - (colorizedConfig.features.length + log.message( + `${chalk.blue("Project Name: ")}${ + colorizedConfig.projectName + }\n${chalk.blue("Database: ")}${colorizedConfig.database}\n${chalk.blue( + "Authentication: ", + )}${colorizedConfig.auth}\n${chalk.blue("Features: ")}${ + colorizedConfig.features.length ? colorizedConfig.features.join(", ") - : chalk.gray("none")), + : chalk.gray("none") + }\n${chalk.blue("Git Init: ")}${colorizedConfig.git}\n`, ); - console.log( - chalk.dim("└─") + chalk.blue(" Git Init: ") + colorizedConfig.git, - ); - console.log(); + s.stop("Configuration loaded"); } await createProject(config); - console.log(); - console.log( - chalk.dim("🔄 You can reproduce this setup with the following command:"), - ); - console.log(); - console.log(chalk.dim(" ") + generateReproducibleCommand(config)); - console.log(); + log.message("You can reproduce this setup with the following command:", { + symbol: chalk.cyan("🔄"), + }); + log.info(generateReproducibleCommand(config)); outro("Project created successfully! 🎉"); } catch (error) {