diff --git a/.changeset/hot-waves-hope.md b/.changeset/hot-waves-hope.md new file mode 100644 index 0000000..14ec4d6 --- /dev/null +++ b/.changeset/hot-waves-hope.md @@ -0,0 +1,5 @@ +--- +"create-better-t-stack": patch +--- + +add consola errors, update to better-auth v1.2.6 diff --git a/apps/cli/package.json b/apps/cli/package.json index a528607..1b820f4 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -4,6 +4,7 @@ "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations", "type": "module", "license": "MIT", + "author": "Aman Varshney", "bin": { "create-better-t-stack": "dist/index.js" }, @@ -53,6 +54,7 @@ }, "dependencies": { "@clack/prompts": "^0.10.1", + "consola": "^3.4.2", "execa": "^8.0.1", "fs-extra": "^11.3.0", "gradient-string": "^3.0.0", diff --git a/apps/cli/src/constants.ts b/apps/cli/src/constants.ts index 51decda..829cdf2 100644 --- a/apps/cli/src/constants.ts +++ b/apps/cli/src/constants.ts @@ -24,8 +24,8 @@ export const DEFAULT_CONFIG: ProjectConfig = { }; export const dependencyVersionMap = { - "better-auth": "^1.2.5", - "@better-auth/expo": "^1.2.5", + "better-auth": "^1.2.6", + "@better-auth/expo": "^1.2.6", "drizzle-orm": "^0.38.4", "drizzle-kit": "^0.30.5", diff --git a/apps/cli/src/helpers/auth-setup.ts b/apps/cli/src/helpers/auth-setup.ts index 0ccaefe..261a294 100644 --- a/apps/cli/src/helpers/auth-setup.ts +++ b/apps/cli/src/helpers/auth-setup.ts @@ -1,5 +1,5 @@ import path from "node:path"; -import { log } from "@clack/prompts"; +import consola from "consola"; import pc from "picocolors"; import type { ProjectFrontend } from "../types"; import { addPackageDependency } from "../utils/add-package-deps"; @@ -54,9 +54,9 @@ export async function setupAuth( }); } } catch (error) { - log.error(pc.red("Failed to configure authentication")); + consola.error(pc.red("Failed to configure authentication")); if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } throw error; } diff --git a/apps/cli/src/helpers/create-readme.ts b/apps/cli/src/helpers/create-readme.ts index 606ce58..8de0934 100644 --- a/apps/cli/src/helpers/create-readme.ts +++ b/apps/cli/src/helpers/create-readme.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import consola from "consola"; import fs from "fs-extra"; import type { ProjectAddons, @@ -16,7 +17,7 @@ export async function createReadme(projectDir: string, options: ProjectConfig) { try { await fs.writeFile(readmePath, content); } catch (error) { - console.error("Failed to create README.md file:", error); + consola.error("Failed to create README.md file:", error); } } @@ -30,24 +31,33 @@ function generateReadmeContent(options: ProjectConfig): string { orm = "drizzle", runtime = "bun", frontend = ["tanstack-router"], + backend = "hono", } = options; const hasReactRouter = frontend.includes("react-router"); const hasTanstackRouter = frontend.includes("tanstack-router"); const hasNative = frontend.includes("native"); + const hasNext = frontend.includes("next"); + const hasTanstackStart = frontend.includes("tanstack-start"); const packageManagerRunCmd = packageManager === "npm" ? "npm run" : packageManager; - const port = hasReactRouter ? "5173" : "3001"; + // Determine the web port based on the frontend framework + let webPort = "3001"; // Default for TanStack Router and TanStack Start + if (hasReactRouter) { + webPort = "5173"; + } else if (hasNext) { + webPort = "3000"; + } return `# ${projectName} -This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${hasTanstackRouter ? "TanStack Router" : "React Router"}, Hono, tRPC, and more. +This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${hasTanstackRouter ? "TanStack Router" : hasReactRouter ? "React Router" : hasNext ? "Next.js" : hasTanstackStart ? "TanStack Start" : ""}, ${backend[0].toUpperCase() + backend.slice(1)}, tRPC, and more. ## Features -${generateFeaturesList(database, auth, addons, orm, runtime, frontend)} +${generateFeaturesList(database, auth, addons, orm, runtime, frontend, backend)} ## Getting Started @@ -66,8 +76,8 @@ ${packageManagerRunCmd} dev \`\`\` ${ - hasTanstackRouter || hasReactRouter - ? `Open [http://localhost:${port}](http://localhost:${port}) in your browser to see the web application.` + hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart + ? `Open [http://localhost:${webPort}](http://localhost:${webPort}) in your browser to see the web application.` : "" } ${hasNative ? "Use the Expo Go app to run the mobile application.\n" : ""} @@ -84,12 +94,12 @@ ${ \`\`\` ${projectName}/ ├── apps/ -${hasTanstackRouter || hasReactRouter ? `│ ├── web/ # Frontend application (React, ${hasTanstackRouter ? "TanStack Router" : "React Router"})\n` : ""}${hasNative ? "│ ├── native/ # Mobile application (React Native, Expo)\n" : ""}│ └── server/ # Backend API (Hono, tRPC) +${hasTanstackRouter || hasReactRouter || hasNext || hasTanstackStart ? `│ ├── web/ # Frontend application (${hasTanstackRouter ? "React + TanStack Router" : hasReactRouter ? "React + React Router" : hasNext ? "Next.js" : "React + TanStack Start"})\n` : ""}${hasNative ? "│ ├── native/ # Mobile application (React Native, Expo)\n" : ""}${addons.includes("starlight") ? "│ ├── docs/ # Documentation site (Astro Starlight)\n" : ""}│ └── server/ # Backend API (${backend[0].toUpperCase() + backend.slice(1)}, tRPC) \`\`\` ## Available Scripts -${generateScriptsList(packageManagerRunCmd, database, orm, auth, hasNative)} +${generateScriptsList(packageManagerRunCmd, database, orm, auth, hasNative, addons, backend)} `; } @@ -100,10 +110,13 @@ function generateFeaturesList( orm: ProjectOrm, runtime: ProjectRuntime, frontend: ProjectFrontend[], + backend: string, ): string { const hasTanstackRouter = frontend.includes("tanstack-router"); const hasReactRouter = frontend.includes("react-router"); const hasNative = frontend.includes("native"); + const hasNext = frontend.includes("next"); + const hasTanstackStart = frontend.includes("tanstack-start"); const addonsList = [ "- **TypeScript** - For type safety and improved developer experience", @@ -115,6 +128,12 @@ function generateFeaturesList( ); } else if (hasReactRouter) { addonsList.push("- **React Router** - Declarative routing for React"); + } else if (hasNext) { + addonsList.push("- **Next.js** - Full-stack React framework"); + } else if (hasTanstackStart) { + addonsList.push( + "- **TanStack Start** - SSR framework with TanStack Router", + ); } if (hasNative) { @@ -125,7 +144,19 @@ function generateFeaturesList( addonsList.push( "- **TailwindCSS** - Utility-first CSS for rapid UI development", "- **shadcn/ui** - Reusable UI components", - "- **Hono** - Lightweight, performant server framework", + ); + + if (backend === "hono") { + addonsList.push("- **Hono** - Lightweight, performant server framework"); + } else if (backend === "express") { + addonsList.push("- **Express** - Fast, unopinionated web framework"); + } else if (backend === "elysia") { + addonsList.push("- **Elysia** - Type-safe, high-performance framework"); + } else if (backend === "next") { + addonsList.push("- **Next.js** - Full-stack React framework"); + } + + addonsList.push( "- **tRPC** - End-to-end type-safe APIs", `- **${runtime === "bun" ? "Bun" : "Node.js"}** - Runtime environment`, ); @@ -133,7 +164,7 @@ function generateFeaturesList( if (database !== "none") { addonsList.push( `- **${orm === "drizzle" ? "Drizzle" : "Prisma"}** - TypeScript-first ORM`, - `- **${database === "sqlite" ? "SQLite/Turso" : "PostgreSQL"}** - Database engine`, + `- **${database === "sqlite" ? "SQLite/Turso" : database === "postgres" ? "PostgreSQL" : database === "mysql" ? "MySQL" : "MongoDB"}** - Database engine`, ); } @@ -152,6 +183,8 @@ function generateFeaturesList( addonsList.push("- **Biome** - Linting and formatting"); } else if (addon === "husky") { addonsList.push("- **Husky** - Git hooks for code quality"); + } else if (addon === "starlight") { + addonsList.push("- **Starlight** - Documentation site with Astro"); } } @@ -185,11 +218,23 @@ cd apps/server && ${packageManagerRunCmd} db:local 1. Make sure you have a PostgreSQL database set up. 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details. +`; + } else if (database === "mysql") { + setup += `This project uses MySQL${orm === "drizzle" ? " with Drizzle ORM" : " with Prisma"}. + +1. Make sure you have a MySQL database set up. +2. Update your \`apps/server/.env\` file with your MySQL connection details. +`; + } else if (database === "mongodb") { + setup += `This project uses MongoDB with Prisma ORM. + +1. Make sure you have MongoDB set up. +2. Update your \`apps/server/.env\` file with your MongoDB connection URI. `; } setup += ` -${auth ? "4" : "3"}. ${ +${auth ? "3" : "3"}. ${ orm === "prisma" ? `Generate the Prisma client and push the schema: \`\`\`bash @@ -211,9 +256,11 @@ function generateScriptsList( orm: ProjectOrm, auth: boolean, hasNative: boolean, + addons: ProjectAddons[], + backend: string, ): string { - let scripts = `- \`${packageManagerRunCmd} dev\`: Start both web and server in development mode -- \`${packageManagerRunCmd} build\`: Build both web and server + let scripts = `- \`${packageManagerRunCmd} dev\`: Start all applications in development mode +- \`${packageManagerRunCmd} build\`: Build all applications - \`${packageManagerRunCmd} dev:web\`: Start only the web application - \`${packageManagerRunCmd} dev:server\`: Start only the server - \`${packageManagerRunCmd} check-types\`: Check TypeScript types across all apps`; @@ -229,9 +276,32 @@ function generateScriptsList( - \`${packageManagerRunCmd} db:studio\`: Open database studio UI`; if (database === "sqlite" && orm === "drizzle") { - scripts += `\n- \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`; + scripts += ` +- \`cd apps/server && ${packageManagerRunCmd} db:local\`: Start the local SQLite database`; } } + if (addons.includes("biome")) { + scripts += ` +- \`${packageManagerRunCmd} check\`: Run Biome formatting and linting`; + } + + if (addons.includes("pwa")) { + scripts += ` +- \`cd apps/web && ${packageManagerRunCmd} generate-pwa-assets\`: Generate PWA assets`; + } + + if (addons.includes("tauri")) { + scripts += ` +- \`cd apps/web && ${packageManagerRunCmd} desktop:dev\`: Start Tauri desktop app in development +- \`cd apps/web && ${packageManagerRunCmd} desktop:build\`: Build Tauri desktop app`; + } + + if (addons.includes("starlight")) { + scripts += ` +- \`cd apps/docs && ${packageManagerRunCmd} dev\`: Start documentation site +- \`cd apps/docs && ${packageManagerRunCmd} build\`: Build documentation site`; + } + return scripts; } diff --git a/apps/cli/src/helpers/db-setup.ts b/apps/cli/src/helpers/db-setup.ts index 826df1a..c3851fc 100644 --- a/apps/cli/src/helpers/db-setup.ts +++ b/apps/cli/src/helpers/db-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { log, spinner } from "@clack/prompts"; +import consola from "consola"; import fs from "fs-extra"; import pc from "picocolors"; import type { @@ -74,7 +75,7 @@ export async function setupDatabase( } catch (error) { s.stop(pc.red("Failed to set up database")); if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } throw error; } diff --git a/apps/cli/src/helpers/install-dependencies.ts b/apps/cli/src/helpers/install-dependencies.ts index 361939e..985f145 100644 --- a/apps/cli/src/helpers/install-dependencies.ts +++ b/apps/cli/src/helpers/install-dependencies.ts @@ -1,4 +1,5 @@ import { log, spinner } from "@clack/prompts"; +import consola from "consola"; import { $ } from "execa"; import pc from "picocolors"; import type { ProjectAddons, ProjectPackageManager } from "../types"; @@ -30,7 +31,7 @@ export async function installDependencies({ } catch (error) { s.stop(pc.red("Failed to install dependencies")); if (error instanceof Error) { - log.error(pc.red(`Installation error: ${error.message}`)); + consola.error(pc.red(`Installation error: ${error.message}`)); } throw error; } diff --git a/apps/cli/src/helpers/mongodb-atlas-setup.ts b/apps/cli/src/helpers/mongodb-atlas-setup.ts index cabaf74..bfeef7a 100644 --- a/apps/cli/src/helpers/mongodb-atlas-setup.ts +++ b/apps/cli/src/helpers/mongodb-atlas-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { cancel, isCancel, log, spinner, text } from "@clack/prompts"; +import consola from "consola"; import { execa } from "execa"; import fs from "fs-extra"; import pc from "picocolors"; @@ -34,7 +35,7 @@ async function initMongoDBAtlas( const hasAtlas = await checkAtlasCLI(); if (!hasAtlas) { - log.error(pc.red("MongoDB Atlas CLI not found.")); + consola.error(pc.red("MongoDB Atlas CLI not found.")); log.info( pc.yellow( "Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/", @@ -74,7 +75,7 @@ async function initMongoDBAtlas( }; } catch (error) { if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } return null; } @@ -105,7 +106,7 @@ async function writeEnvFile(projectDir: string, config?: MongoDBConfig) { await fs.writeFile(envPath, envContent.trim()); } catch (error) { - log.error("Failed to update environment configuration"); + consola.error("Failed to update environment configuration"); throw error; } } @@ -154,7 +155,7 @@ export async function setupMongoDBAtlas(projectDir: string) { } } catch (error) { mainSpinner.stop(pc.red("MongoDB Atlas setup failed")); - log.error( + consola.error( pc.red( `Error during MongoDB Atlas setup: ${error instanceof Error ? error.message : String(error)}`, ), diff --git a/apps/cli/src/helpers/neon-setup.ts b/apps/cli/src/helpers/neon-setup.ts index 5c36cda..2c44c4b 100644 --- a/apps/cli/src/helpers/neon-setup.ts +++ b/apps/cli/src/helpers/neon-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { cancel, isCancel, log, spinner, text } from "@clack/prompts"; +import { consola } from "consola"; import { execa } from "execa"; import fs from "fs-extra"; import pc from "picocolors"; @@ -82,7 +83,7 @@ async function authenticateWithNeon(packageManager: string) { log.success("Authenticated with Neon successfully!"); return true; } catch (error) { - log.error(pc.red("Failed to authenticate with Neon")); + consola.error(pc.red("Failed to authenticate with Neon")); throw error; } } @@ -116,10 +117,12 @@ async function createNeonProject( roleName: params.role, }; } - log.error(pc.red("Failed to extract connection information from response")); + consola.error( + pc.red("Failed to extract connection information from response"), + ); return null; } catch (error) { - log.error(pc.red("Failed to create Neon project")); + consola.error(pc.red("Failed to create Neon project")); throw error; } } @@ -198,7 +201,7 @@ export async function setupNeonPostgres( setupSpinner.stop(pc.red("Neon PostgreSQL setup failed")); if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } await writeEnvFile(projectDir); diff --git a/apps/cli/src/helpers/post-installation.ts b/apps/cli/src/helpers/post-installation.ts index 604e1e6..b8e5c52 100644 --- a/apps/cli/src/helpers/post-installation.ts +++ b/apps/cli/src/helpers/post-installation.ts @@ -1,4 +1,4 @@ -import { note } from "@clack/prompts"; +import { consola } from "consola"; import pc from "picocolors"; import type { ProjectAddons, @@ -57,8 +57,8 @@ export function displayPostInstallInstructions( const webPort = hasReactRouter ? "5173" : "3001"; - note( - `${pc.cyan("1.")} ${cdCmd} + consola.box( + `${pc.bold("Next steps")}\n${pc.cyan("1.")} ${cdCmd} ${!depsInstalled ? `${pc.cyan("2.")} ${packageManager} install\n` : ""}${pc.cyan(depsInstalled ? "2." : "3.")} ${runCmd} dev ${pc.bold("Your project will be available at:")} @@ -68,9 +68,9 @@ ${ : `${pc.yellow("NOTE:")} You are creating a backend-only app (no frontend selected)\n` }${pc.cyan("•")} API: http://localhost:3000 ${addons?.includes("starlight") ? `${pc.cyan("•")} Docs: http://localhost:4321\n` : ""}${nativeInstructions ? `\n${nativeInstructions.trim()}` : ""}${databaseInstructions ? `\n${databaseInstructions.trim()}` : ""}${tauriInstructions ? `\n${tauriInstructions.trim()}` : ""}${lintingInstructions ? `\n${lintingInstructions.trim()}` : ""}${pwaInstructions ? `\n${pwaInstructions.trim()}` : ""}${starlightInstructions ? `\n${starlightInstructions.trim()}` : ""} -\n${pc.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub: + +${pc.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub: ${pc.cyan("https://github.com/AmanVarshney01/create-better-t-stack")}`, - "Next steps", ); } diff --git a/apps/cli/src/helpers/prisma-postgres-setup.ts b/apps/cli/src/helpers/prisma-postgres-setup.ts index 79d1008..74b8de2 100644 --- a/apps/cli/src/helpers/prisma-postgres-setup.ts +++ b/apps/cli/src/helpers/prisma-postgres-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { cancel, isCancel, log, password, spinner } from "@clack/prompts"; +import { consola } from "consola"; import { execa } from "execa"; import fs from "fs-extra"; import pc from "picocolors"; @@ -62,7 +63,7 @@ async function initPrismaDatabase( } catch (error) { s.stop(pc.red("Failed to initialize Prisma PostgreSQL")); if (error instanceof Error) { - log.error(error.message); + consola.error(error.message); } return null; } @@ -93,7 +94,7 @@ async function writeEnvFile(projectDir: string, config?: PrismaConfig) { await fs.writeFile(envPath, envContent.trim()); } catch (error) { - log.error("Failed to update environment configuration"); + consola.error("Failed to update environment configuration"); throw error; } } @@ -181,7 +182,7 @@ export async function setupPrismaPostgres( } } catch (error) { s.stop(pc.red("Prisma PostgreSQL setup failed")); - log.error( + consola.error( pc.red( `Error during Prisma PostgreSQL setup: ${error instanceof Error ? error.message : String(error)}`, ), diff --git a/apps/cli/src/helpers/starlight-setup.ts b/apps/cli/src/helpers/starlight-setup.ts index f9d0328..28ac8f5 100644 --- a/apps/cli/src/helpers/starlight-setup.ts +++ b/apps/cli/src/helpers/starlight-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { log, spinner } from "@clack/prompts"; +import consola from "consola"; import { execa } from "execa"; import pc from "picocolors"; import type { ProjectPackageManager } from "../types"; @@ -57,7 +58,7 @@ export async function setupStarlight( } catch (error) { s.stop(pc.red("Failed to set up Starlight documentation site")); if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } throw error; } diff --git a/apps/cli/src/helpers/tauri-setup.ts b/apps/cli/src/helpers/tauri-setup.ts index 7d29ac0..b8d9b9b 100644 --- a/apps/cli/src/helpers/tauri-setup.ts +++ b/apps/cli/src/helpers/tauri-setup.ts @@ -1,5 +1,6 @@ import path from "node:path"; import { log, spinner } from "@clack/prompts"; +import { consola } from "consola"; import { execa } from "execa"; import fs from "fs-extra"; import pc from "picocolors"; @@ -88,7 +89,7 @@ export async function setupTauri( } catch (error) { s.stop(pc.red("Failed to set up Tauri")); if (error instanceof Error) { - log.error(pc.red(error.message)); + consola.error(pc.red(error.message)); } throw error; } diff --git a/apps/cli/src/helpers/turso-setup.ts b/apps/cli/src/helpers/turso-setup.ts index 26202d3..ec7aafa 100644 --- a/apps/cli/src/helpers/turso-setup.ts +++ b/apps/cli/src/helpers/turso-setup.ts @@ -9,6 +9,7 @@ import { spinner, text, } from "@clack/prompts"; +import consola from "consola"; import { $ } from "execa"; import fs from "fs-extra"; import pc from "picocolors"; @@ -301,7 +302,7 @@ export async function setupTurso( } } catch (error) { setupSpinner.stop(pc.red("Failed to set up Turso database")); - log.error( + consola.error( pc.red( `Error during Turso setup: ${error instanceof Error ? error.message : String(error)}`, ), diff --git a/apps/cli/src/index.ts b/apps/cli/src/index.ts index 12b4e23..e62a09e 100644 --- a/apps/cli/src/index.ts +++ b/apps/cli/src/index.ts @@ -1,4 +1,5 @@ -import { cancel, intro, log, outro, spinner } from "@clack/prompts"; +import { cancel, intro, log, outro } from "@clack/prompts"; +import { consola } from "consola"; import pc from "picocolors"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; @@ -49,7 +50,6 @@ process.on("SIGTERM", exit); async function main() { const startTime = Date.now(); - const s = spinner(); try { const argv = await yargs(hideBin(process.argv)) @@ -146,14 +146,15 @@ async function main() { .wrap(null) .parse(); - renderTitle(); - intro(pc.magenta("Creating a new Better-T-Stack project")); - const options = argv as YargsArgv; const projectDirectory = options.projectDirectory; + renderTitle(); + const flagConfig = processAndValidateFlags(options, projectDirectory); + intro(pc.magenta("Creating a new Better-T-Stack project")); + if (!options.yes && Object.keys(flagConfig).length > 0) { log.info(pc.yellow("Using these pre-selected options:")); log.message(displayConfig(flagConfig)); @@ -177,9 +178,11 @@ async function main() { await createProject(config); log.success( - `You can reproduce this setup with the following command:\n${pc.white( - generateReproducibleCommand(config), - )}`, + pc.blue( + `You can reproduce this setup with the following command:\n${generateReproducibleCommand( + config, + )}`, + ), ); const elapsedTimeInSeconds = ((Date.now() - startTime) / 1000).toFixed(2); @@ -189,16 +192,16 @@ async function main() { ), ); } catch (error) { - s.stop(pc.red("Failed")); if (error instanceof Error) { if (error.name === "YError") { cancel(pc.red(`Invalid arguments: ${error.message}`)); } else { - cancel(pc.red(`An unexpected error occurred: ${error.message}`)); + consola.error(`An unexpected error occurred: ${error.message}`); + consola.error(error.stack); } process.exit(1); } else { - cancel(pc.red("An unexpected error occurred.")); + consola.error("An unexpected error occurred."); console.error(error); process.exit(1); } @@ -227,10 +230,8 @@ function processAndValidateFlags( (config.database ?? options.database) === "mongodb" && (config.orm ?? options.orm) === "drizzle" ) { - cancel( - pc.red( - "MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle", - ), + consola.fatal( + "MongoDB is only available with Prisma. Cannot use --database mongodb with --orm drizzle", ); process.exit(1); } @@ -243,50 +244,40 @@ function processAndValidateFlags( if (dbSetup === "turso") { if (options.database && options.database !== "sqlite") { - cancel( - pc.red( - `Turso setup requires a SQLite database. Cannot use --db-setup turso with --database ${options.database}`, - ), + consola.fatal( + `Turso setup requires a SQLite database. Cannot use --db-setup turso with --database ${options.database}`, ); process.exit(1); } config.database = "sqlite"; if (options.orm === "prisma") { - cancel( - pc.red( - "Turso setup is not compatible with Prisma. Cannot use --db-setup turso with --orm prisma", - ), + consola.fatal( + "Turso setup is not compatible with Prisma. Cannot use --db-setup turso with --orm prisma", ); process.exit(1); } config.orm = "drizzle"; } else if (dbSetup === "prisma-postgres") { if (options.database && options.database !== "postgres") { - cancel( - pc.red( - "Prisma PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup prisma-postgres with a different database type.", - ), + consola.fatal( + "Prisma PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup prisma-postgres with a different database type.", ); process.exit(1); } config.database = "postgres"; if (options.orm && options.orm !== "prisma" && options.orm !== "none") { - cancel( - pc.red( - "Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with a different ORM.", - ), + consola.fatal( + "Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with a different ORM.", ); process.exit(1); } config.orm = "prisma"; } else if (dbSetup === "mongodb-atlas") { if (options.database && options.database !== "mongodb") { - cancel( - pc.red( - "MongoDB Atlas setup requires MongoDB database. Cannot use --db-setup mongodb-atlas with a different database type.", - ), + consola.fatal( + "MongoDB Atlas setup requires MongoDB database. Cannot use --db-setup mongodb-atlas with a different database type.", ); process.exit(1); } @@ -294,10 +285,8 @@ function processAndValidateFlags( config.orm = "prisma"; } else if (dbSetup === "neon") { if (options.database && options.database !== "postgres") { - cancel( - pc.red( - "Neon PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup neon with a different database type.", - ), + consola.fatal( + "Neon PostgreSQL setup requires PostgreSQL database. Cannot use --db-setup neon with a different database type.", ); process.exit(1); } @@ -311,20 +300,16 @@ function processAndValidateFlags( const effectiveDatabase = config.database ?? options.database; if (effectiveDatabase === "none") { if (options.auth === true) { - cancel( - pc.red( - "Authentication requires a database. Cannot use --auth with --database none.", - ), + consola.fatal( + "Authentication requires a database. Cannot use --auth with --database none.", ); process.exit(1); } const effectiveOrm = config.orm ?? options.orm; if (effectiveOrm && effectiveOrm !== "none") { - cancel( - pc.red( - `Cannot use ORM with no database. Cannot use --orm ${effectiveOrm} with --database none.`, - ), + consola.fatal( + `Cannot use ORM with no database. Cannot use --orm ${effectiveOrm} with --database none.`, ); process.exit(1); } @@ -332,10 +317,8 @@ function processAndValidateFlags( const effectiveDbSetup = config.dbSetup ?? options.dbSetup; if (effectiveDbSetup && effectiveDbSetup !== "none") { - cancel( - pc.red( - `Database setup requires a database. Cannot use --db-setup ${effectiveDbSetup} with --database none.`, - ), + consola.fatal( + `Database setup requires a database. Cannot use --db-setup ${effectiveDbSetup} with --database none.`, ); process.exit(1); } @@ -357,7 +340,7 @@ function processAndValidateFlags( if (options.frontend && options.frontend.length > 0) { if (options.frontend.includes("none")) { if (options.frontend.length > 1) { - cancel(pc.red(`Cannot combine 'none' with other frontend options.`)); + consola.fatal(`Cannot combine 'none' with other frontend options.`); process.exit(1); } config.frontend = []; @@ -374,10 +357,8 @@ function processAndValidateFlags( ); if (webFrontends.length > 1) { - cancel( - pc.red( - "Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router", - ), + consola.fatal( + "Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router", ); process.exit(1); } @@ -388,7 +369,7 @@ function processAndValidateFlags( if (options.addons && options.addons.length > 0) { if (options.addons.includes("none")) { if (options.addons.length > 1) { - cancel(pc.red(`Cannot combine 'none' with other addons.`)); + consola.fatal(`Cannot combine 'none' with other addons.`); process.exit(1); } config.addons = []; @@ -411,18 +392,14 @@ function processAndValidateFlags( if (hasWebSpecificAddons && !hasCompatibleWebFrontend) { if (options.frontend) { - cancel( - pc.red( - "PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection.", - ), + consola.fatal( + "PWA and Tauri addons require tanstack-router or react-router. Cannot use these addons with your frontend selection.", ); process.exit(1); } else if (!options.yes) { } else { - cancel( - pc.red( - "PWA and Tauri addons require tanstack-router or react-router (default frontend incompatible).", - ), + consola.fatal( + "PWA and Tauri addons require tanstack-router or react-router (default frontend incompatible).", ); process.exit(1); } @@ -439,7 +416,7 @@ function processAndValidateFlags( if (options.examples && options.examples.length > 0) { if (options.examples.includes("none")) { if (options.examples.length > 1) { - cancel(pc.red("Cannot combine 'none' with other examples.")); + consola.fatal("Cannot combine 'none' with other examples."); process.exit(1); } config.examples = []; @@ -454,10 +431,8 @@ function processAndValidateFlags( effectiveBackend === "elysia" && !(options.yes && DEFAULT_CONFIG.backend !== "elysia") ) { - cancel( - pc.red( - "AI example is only compatible with Hono backend. Cannot use --examples ai with --backend elysia", - ), + consola.fatal( + "AI example is only compatible with Hono backend. Cannot use --examples ai with --backend elysia", ); process.exit(1); } @@ -473,18 +448,14 @@ function processAndValidateFlags( if (!hasWebFrontend) { if (options.frontend) { - cancel( - pc.red( - "Examples require a web frontend (tanstack-router, react-router, or tanstack-start). Cannot use --examples with your frontend selection.", - ), + consola.fatal( + "Examples require a web frontend (tanstack-router, react-router, or tanstack-start). Cannot use --examples with your frontend selection.", ); process.exit(1); } else if (!options.yes) { } else { - cancel( - pc.red( - "Examples require a web frontend (tanstack-router, react-router, or tanstack-start) (default frontend incompatible).", - ), + consola.fatal( + "Examples require a web frontend (tanstack-router, react-router, or tanstack-start) (default frontend incompatible).", ); process.exit(1); } @@ -514,12 +485,12 @@ function processAndValidateFlags( } main().catch((err) => { - log.error("Aborting installation due to unexpected error..."); + consola.error("Aborting installation due to unexpected error..."); if (err instanceof Error) { - log.error(err.message); + consola.error(err.message); console.error(err.stack); } else { - log.error( + consola.error( "An unknown error has occurred. Please open an issue on GitHub with the below:", ); console.error(err); diff --git a/bun.lock b/bun.lock index 753d8f4..d42bf15 100644 --- a/bun.lock +++ b/bun.lock @@ -14,12 +14,13 @@ }, "apps/cli": { "name": "create-better-t-stack", - "version": "1.12.3", + "version": "1.13.1", "bin": { "create-better-t-stack": "dist/index.js", }, "dependencies": { "@clack/prompts": "^0.10.1", + "consola": "^3.4.2", "execa": "^8.0.1", "fs-extra": "^11.3.0", "gradient-string": "^3.0.0", @@ -677,7 +678,7 @@ "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], - "consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "create-better-t-stack": ["create-better-t-stack@workspace:apps/cli"], @@ -1793,6 +1794,8 @@ "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + "tsup/consola": ["consola@3.4.0", "", {}, "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA=="], + "web/@types/node": ["@types/node@22.13.11", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-iEUCUJoU0i3VnrCmgoWCXttklWcvoCIx4jzcP22fioIVSdTmjgoEvmAO/QPw6TcS9k5FrNgn4w7q5lGOd1CT5g=="], "web/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],