UNPKG

create-better-t-stack

Version:

A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations

194 lines (154 loc) 71.6 kB
#!/usr/bin/env node import Go from"node:path";import{cancel as Jo,intro as Qo,log as _,outro as Ho}from"@clack/prompts";import{consola as j}from"consola";import sr from"fs-extra";import z from"picocolors";import Yo from"yargs";import{hideBin as Ko}from"yargs/helpers";import Me from"node:path";import{fileURLToPath as fr}from"node:url";var re=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};var gr=fr(import.meta.url),hr=Me.dirname(gr),P=Me.join(hr,"../"),h={projectName:"my-better-t-app",frontend:["tanstack-router"],database:"sqlite",orm:"drizzle",auth:!0,addons:[],examples:[],git:!0,packageManager:re(),install:!0,dbSetup:"none",backend:"hono",runtime:"bun",api:"trpc"},be={"better-auth":"^1.2.6","@better-auth/expo":"^1.2.6","drizzle-orm":"^0.38.4","drizzle-kit":"^0.30.5","@libsql/client":"^0.14.0",pg:"^8.14.1","@types/pg":"^8.11.11",mysql2:"^3.14.0","@prisma/client":"^6.6.0",prisma:"^6.6.0","vite-plugin-pwa":"^0.21.2","@vite-pwa/assets-generator":"^0.2.6","@tauri-apps/cli":"^2.4.0","@biomejs/biome":"1.9.4",husky:"^9.1.7","lint-staged":"^15.5.0","@hono/node-server":"^1.14.0",tsx:"^4.19.2","@types/node":"^22.13.11","@types/bun":"^1.2.6","@elysiajs/node":"^1.2.6","@elysiajs/cors":"^1.2.0","@elysiajs/trpc":"^1.1.0",elysia:"^1.2.25","@hono/trpc-server":"^0.3.4",hono:"^4.7.6",cors:"^2.8.5",express:"^5.1.0","@types/express":"^5.0.1","@types/cors":"^2.8.17",turbo:"^2.4.2",ai:"^4.2.8","@ai-sdk/google":"^1.2.3","@prisma/extension-accelerate":"^1.3.0","@orpc/server":"^1.0.3","@orpc/react-query":"^1.0.3","@orpc/client":"^1.0.3","@trpc/tanstack-react-query":"^11.0.0","@trpc/server":"^11.0.0","@trpc/client":"^11.0.0"};import Ba from"node:path";import{cancel as Na,log as Ia,spinner as Ma}from"@clack/prompts";import La from"fs-extra";import Ft from"picocolors";import Y from"node:path";import B from"fs-extra";import br from"node:path";import Le from"fs-extra";var g=async e=>{let{dependencies:r=[],devDependencies:t=[],projectDir:a}=e,o=br.join(a,"package.json"),n=await Le.readJson(o);n.dependencies||(n.dependencies={}),n.devDependencies||(n.devDependencies={});for(let i of r){let s=be[i];s?n.dependencies[i]=s:console.warn(`Warning: Dependency ${i} not found in version map.`)}for(let i of t){let s=be[i];s?n.devDependencies[i]=s:console.warn(`Warning: Dev dependency ${i} not found in version map.`)}await Le.writeJson(o,n,{spaces:2})};import Oe from"node:path";import{spinner as wr}from"@clack/prompts";import yr from"consola";import{execa as Pr}from"execa";import Ue from"picocolors";function R(e,r){switch(e){case"pnpm":return`pnpm dlx ${r}`;case"bun":return`bunx ${r}`;default:return`npx ${r}`}}async function _e(e){let{projectName:r,packageManager:t}=e,a=Oe.resolve(process.cwd(),r),o=wr();try{o.start("Setting up Starlight docs...");let s=`create-astro@latest ${["docs","--template","starlight","--no-install","--add","tailwind","--no-git","--skip-houston"].join(" ")}`,c=R(t,s);await Pr(c,{cwd:Oe.join(a,"apps"),env:{CI:"true"},shell:!0}),o.stop("Starlight docs setup successfully!")}catch(n){throw o.stop(Ue.red("Failed to set up Starlight docs")),n instanceof Error&&yr.error(Ue.red(n.message)),n}}import H from"node:path";import{spinner as jr}from"@clack/prompts";import{consola as vr}from"consola";import{execa as kr}from"execa";import ae from"fs-extra";import ze from"picocolors";async function We(e){let{projectName:r,packageManager:t,frontend:a}=e,o=H.resolve(process.cwd(),r),n=jr(),i=H.join(o,"apps/web");if(await ae.pathExists(i))try{n.start("Setting up Tauri desktop app support..."),await g({devDependencies:["@tauri-apps/cli"],projectDir:i});let s=H.join(i,"package.json");if(await ae.pathExists(s)){let S=await ae.readJson(s);S.scripts={...S.scripts,tauri:"tauri","desktop:dev":"tauri dev","desktop:build":"tauri build"},await ae.writeJson(s,S,{spaces:2})}let p=a.includes("react-router")?"http://localhost:5173":"http://localhost:3001",f=`@tauri-apps/cli@latest ${["init",`--app-name=${H.basename(o)}`,`--window-title=${H.basename(o)}`,"--frontend-dist=../dist",`--dev-url=${p}`,`--before-dev-command="${t} run dev"`,`--before-build-command="${t} run build"`].join(" ")}`,k=R(t,f);await kr(k,{cwd:i,env:{CI:"true"},shell:!0}),n.stop("Tauri desktop app support configured successfully!")}catch(s){throw n.stop(ze.red("Failed to set up Tauri")),s instanceof Error&&vr.error(ze.red(s.message)),s}}async function qe(e){let{projectName:r,addons:t,packageManager:a,frontend:o}=e,n=Y.resolve(process.cwd(),r),i=o.includes("react-router")||o.includes("tanstack-router");t.includes("turborepo")&&await g({devDependencies:["turbo"],projectDir:n}),t.includes("pwa")&&i&&await Dr(n,o),t.includes("tauri")&&i&&await We(e),t.includes("biome")&&await Sr(n),t.includes("husky")&&await Ar(n),t.includes("starlight")&&await _e(e)}function xr(e,r){return Y.join(e,"apps/web")}async function Sr(e){await g({devDependencies:["@biomejs/biome"],projectDir:e});let r=Y.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,check:"biome check --write ."},await B.writeJson(r,t,{spaces:2})}}async function Ar(e){await g({devDependencies:["husky","lint-staged"],projectDir:e});let r=Y.join(e,"package.json");if(await B.pathExists(r)){let t=await B.readJson(r);t.scripts={...t.scripts,prepare:"husky"},t["lint-staged"]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --write ."]},await B.writeJson(r,t,{spaces:2})}}async function Dr(e,r){let t=xr(e,r);if(!await B.pathExists(t))return;await g({dependencies:["vite-plugin-pwa"],devDependencies:["@vite-pwa/assets-generator"],projectDir:t});let a=Y.join(t,"package.json");if(await B.pathExists(a)){let o=await B.readJson(a);o.scripts={...o.scripts,"generate-pwa-assets":"pwa-assets-generator"},await B.writeJson(a,o,{spaces:2})}}import*as K from"node:path";async function Ve(e){if(e.api==="none")return;let{api:r,projectName:t}=e,a=K.resolve(process.cwd(),t),o=K.join(a,"apps/web"),n=K.join(a,"apps/server");r==="orpc"&&(await g({dependencies:["@orpc/react-query","@orpc/server","@orpc/client"],projectDir:o}),await g({dependencies:["@orpc/server","@orpc/client"],projectDir:n})),r==="trpc"&&(await g({dependencies:["@trpc/tanstack-react-query","@trpc/server","@trpc/client"],projectDir:o}),await g({dependencies:["@trpc/server","@trpc/client"],projectDir:n}))}import oe from"node:path";import Ge from"consola";import Je from"picocolors";function Qe(e=32){let r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t="",a=r.length;for(let o=0;o<e;o++)t+=r.charAt(Math.floor(Math.random()*a));return t}async function He(e){let{projectName:r,auth:t,frontend:a}=e;if(!t)return;let o=oe.resolve(process.cwd(),r),n=oe.join(o,"apps/server"),i=oe.join(o,"apps/web"),s=oe.join(o,"apps/native");try{await g({dependencies:["better-auth"],projectDir:n}),(a.includes("react-router")||a.includes("tanstack-router")||a.includes("tanstack-start"))&&await g({dependencies:["better-auth"],projectDir:i}),a.includes("native")&&(await g({dependencies:["better-auth","@better-auth/expo"],projectDir:s}),await g({dependencies:["@better-auth/expo"],projectDir:n}))}catch(c){throw Ge.error(Je.red("Failed to configure authentication")),c instanceof Error&&Ge.error(Je.red(c.message)),c}}import Ye from"node:path";async function Ke(e){let{projectName:r,backend:t,runtime:a}=e,o=Ye.resolve(process.cwd(),r),n=t,i=Ye.join(o,"apps/server"),s=[],c=[];n==="hono"?(s.push("hono","@hono/trpc-server"),a==="node"&&(s.push("@hono/node-server"),c.push("tsx","@types/node"))):n==="elysia"?(s.push("elysia","@elysiajs/cors","@elysiajs/trpc"),a==="node"&&(s.push("@elysiajs/node"),c.push("tsx","@types/node"))):n==="express"&&(s.push("express","cors"),c.push("@types/express","@types/cors"),a==="node"&&c.push("tsx","@types/node")),a==="bun"&&c.push("@types/bun"),await g({dependencies:s,devDependencies:c,projectDir:i})}import $r from"node:path";import Cr from"consola";import Er from"fs-extra";async function Xe(e,r){let t=$r.join(e,"README.md"),a=Tr(r);try{await Er.writeFile(t,a)}catch(o){Cr.error("Failed to create README.md file:",o)}}function Tr(e){let{projectName:r,packageManager:t,database:a,auth:o,addons:n=[],orm:i="drizzle",runtime:s="bun",frontend:c=["tanstack-router"],backend:p="hono"}=e,w=c.includes("react-router"),y=c.includes("tanstack-router"),f=c.includes("native"),k=c.includes("next"),S=c.includes("tanstack-start"),E=t==="npm"?"npm run":t,x="3001";return w?x="5173":k&&(x="3000"),`# ${r} This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines React, ${y?"TanStack Router":w?"React Router":k?"Next.js":S?"TanStack Start":""}, ${p[0].toUpperCase()+p.slice(1)}, tRPC, and more. ## Features ${Fr(a,o,n,i,s,c,p)} ## Getting Started First, install the dependencies: \`\`\`bash ${t} install \`\`\` ${Rr(a,o,E,i)} Then, run the development server: \`\`\`bash ${E} dev \`\`\` ${y||w||k||S?`Open [http://localhost:${x}](http://localhost:${x}) in your browser to see the web application.`:""} ${f?`Use the Expo Go app to run the mobile application. `:""} The API is running at [http://localhost:3000](http://localhost:3000). ${n.includes("pwa")&&w?` ## PWA Support with React Router v7 There is a known compatibility issue between VitePWA and React Router v7. See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809 `:""} ## Project Structure \`\`\` ${r}/ \u251C\u2500\u2500 apps/ ${y||w||k||S?`\u2502 \u251C\u2500\u2500 web/ # Frontend application (${y?"React + TanStack Router":w?"React + React Router":k?"Next.js":"React + TanStack Start"}) `:""}${f?`\u2502 \u251C\u2500\u2500 native/ # Mobile application (React Native, Expo) `:""}${n.includes("starlight")?`\u2502 \u251C\u2500\u2500 docs/ # Documentation site (Astro Starlight) `:""}\u2502 \u2514\u2500\u2500 server/ # Backend API (${p[0].toUpperCase()+p.slice(1)}, tRPC) \`\`\` ## Available Scripts ${Br(E,a,i,o,f,n,p)} `}function Fr(e,r,t,a,o,n,i){let s=n.includes("tanstack-router"),c=n.includes("react-router"),p=n.includes("native"),w=n.includes("next"),y=n.includes("tanstack-start"),f=["- **TypeScript** - For type safety and improved developer experience"];s?f.push("- **TanStack Router** - File-based routing with full type safety"):c?f.push("- **React Router** - Declarative routing for React"):w?f.push("- **Next.js** - Full-stack React framework"):y&&f.push("- **TanStack Start** - SSR framework with TanStack Router"),p&&(f.push("- **React Native** - Build mobile apps using React"),f.push("- **Expo** - Tools for React Native development")),f.push("- **TailwindCSS** - Utility-first CSS for rapid UI development","- **shadcn/ui** - Reusable UI components"),i==="hono"?f.push("- **Hono** - Lightweight, performant server framework"):i==="express"?f.push("- **Express** - Fast, unopinionated web framework"):i==="elysia"?f.push("- **Elysia** - Type-safe, high-performance framework"):i==="next"&&f.push("- **Next.js** - Full-stack React framework"),f.push("- **tRPC** - End-to-end type-safe APIs",`- **${o==="bun"?"Bun":"Node.js"}** - Runtime environment`),e!=="none"&&f.push(`- **${a==="drizzle"?"Drizzle":"Prisma"}** - TypeScript-first ORM`,`- **${e==="sqlite"?"SQLite/Turso":e==="postgres"?"PostgreSQL":e==="mysql"?"MySQL":"MongoDB"}** - Database engine`),r&&f.push("- **Authentication** - Email & password authentication with Better Auth");for(let k of t)k==="pwa"?f.push("- **PWA** - Progressive Web App support"):k==="tauri"?f.push("- **Tauri** - Build native desktop applications"):k==="biome"?f.push("- **Biome** - Linting and formatting"):k==="husky"?f.push("- **Husky** - Git hooks for code quality"):k==="starlight"&&f.push("- **Starlight** - Documentation site with Astro");return f.join(` `)}function Rr(e,r,t,a){if(e==="none")return"";let o=`## Database Setup `;return e==="sqlite"?o+=`This project uses SQLite${a==="drizzle"?" with Drizzle ORM":" with Prisma"}. 1. Start the local SQLite database: \`\`\`bash cd apps/server && ${t} db:local \`\`\` 2. Update your \`.env\` file in the \`apps/server\` directory with the appropriate connection details if needed. `:e==="postgres"?o+=`This project uses PostgreSQL${a==="drizzle"?" with Drizzle ORM":" with Prisma"}. 1. Make sure you have a PostgreSQL database set up. 2. Update your \`apps/server/.env\` file with your PostgreSQL connection details. `:e==="mysql"?o+=`This project uses MySQL${a==="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. `:e==="mongodb"&&(o+=`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. `),o+=` 3. ${a==="prisma"?`Generate the Prisma client and push the schema: \`\`\`bash ${t} db:push \`\`\``:`Apply the schema to your database: \`\`\`bash ${t} db:push \`\`\``} `,o}function Br(e,r,t,a,o,n,i){let s=`- \`${e} dev\`: Start all applications in development mode - \`${e} build\`: Build all applications - \`${e} dev:web\`: Start only the web application - \`${e} dev:server\`: Start only the server - \`${e} check-types\`: Check TypeScript types across all apps`;return o&&(s+=` - \`${e} dev:native\`: Start the React Native/Expo development server`),r!=="none"&&(s+=` - \`${e} db:push\`: Push schema changes to database - \`${e} db:studio\`: Open database studio UI`,r==="sqlite"&&t==="drizzle"&&(s+=` - \`cd apps/server && ${e} db:local\`: Start the local SQLite database`)),n.includes("biome")&&(s+=` - \`${e} check\`: Run Biome formatting and linting`),n.includes("pwa")&&(s+=` - \`cd apps/web && ${e} generate-pwa-assets\`: Generate PWA assets`),n.includes("tauri")&&(s+=` - \`cd apps/web && ${e} desktop:dev\`: Start Tauri desktop app in development - \`cd apps/web && ${e} desktop:build\`: Build Tauri desktop app`),n.includes("starlight")&&(s+=` - \`cd apps/docs && ${e} dev\`: Start documentation site - \`cd apps/docs && ${e} build\`: Build documentation site`),s}import Ce from"node:path";import{spinner as ma}from"@clack/prompts";import fa from"consola";import ga from"fs-extra";import pt from"picocolors";import se from"node:path";import{cancel as Nr,isCancel as Ir,log as W,spinner as tt,text as Mr}from"@clack/prompts";import ie from"consola";import{execa as Lr}from"execa";import X from"fs-extra";import D from"picocolors";import{execa as Ze}from"execa";async function ne(e){try{return process.platform==="win32"?(await Ze("where",[e])).exitCode===0:(await Ze("which",[e])).exitCode===0}catch{return!1}}async function Or(){let e=tt();e.start("Checking for MongoDB Atlas CLI");try{let r=await ne("atlas");return e.stop(r?"MongoDB Atlas CLI found":D.yellow("MongoDB Atlas CLI not found")),r}catch{return e.stop(D.red("Error checking for MongoDB Atlas CLI")),!1}}async function Ur(e){try{if(!await Or())return ie.error(D.red("MongoDB Atlas CLI not found.")),W.info(D.yellow("Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/")),null;W.info(D.blue("Running MongoDB Atlas setup...")),await Lr("atlas",["deployments","setup"],{cwd:e,stdio:"inherit"}),W.info(D.green("Atlas setup complete!"));let t=await Mr({message:"Enter your MongoDB connection string:",placeholder:"mongodb+srv://username:password@cluster.mongodb.net/database",validate(a){if(!a)return"Please enter a connection string";if(!a.startsWith("mongodb"))return"URL should start with mongodb:// or mongodb+srv://"}});return Ir(t)?(Nr("MongoDB setup cancelled"),null):{connectionString:t}}catch(r){return r instanceof Error&&ie.error(D.red(r.message)),null}}async function we(e,r){try{let t=se.join(e,"apps/server",".env");await X.ensureDir(se.dirname(t));let a="";await X.pathExists(t)&&(a=await X.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="mongodb://localhost:27017/mydb"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=` ${o}`,await X.writeFile(t,a.trim())}catch(t){throw ie.error("Failed to update environment configuration"),t}}function et(){W.info(` ${D.green("MongoDB Atlas Manual Setup Instructions:")} 1. Install Atlas CLI: ${D.blue("https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/")} 2. Run the following command and follow the prompts: ${D.blue("atlas deployments setup")} 3. Get your connection string from the Atlas dashboard: Format: ${D.dim("mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME")} 4. Add the connection string to your .env file: ${D.dim('DATABASE_URL="your_connection_string"')} `)}async function rt(e){let{projectName:r}=e,t=se.resolve(process.cwd(),r),a=tt();a.start("Setting up MongoDB Atlas");let o=se.join(t,"apps/server");try{await X.ensureDir(o),a.stop("Starting MongoDB Atlas setup");let n=await Ur(o);n?(await we(t,n),W.success(D.green("MongoDB Atlas setup complete! Connection saved to .env file."))):(W.warn(D.yellow("Falling back to local MongoDB configuration")),await we(t),et())}catch(n){a.stop(D.red("MongoDB Atlas setup failed")),ie.error(D.red(`Error during MongoDB Atlas setup: ${n instanceof Error?n.message:String(n)}`));try{await we(t),et()}catch{}}}import L from"node:path";import{cancel as _r,isCancel as zr,log as Z,password as Wr,spinner as Pe}from"@clack/prompts";import{consola as je}from"consola";import{execa as qr}from"execa";import N from"fs-extra";import q from"picocolors";async function Vr(e,r){let t=Pe();try{t.start("Initializing Prisma PostgreSQL");let a=L.join(e,"prisma");await N.ensureDir(a),t.stop("Initializing Prisma. Follow the prompts below:");let o=R(r,"prisma init --db");await qr(o,{cwd:e,stdio:"inherit",shell:!0}),Z.info(q.yellow(`Please copy the Prisma Postgres URL from the output above. It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let n=await Wr({message:"Paste your Prisma Postgres database URL:",validate(i){if(!i)return"Please enter a database URL";if(!i.startsWith("prisma+postgres://"))return"URL should start with prisma+postgres://"}});return zr(n)?(_r("Database setup cancelled"),null):{databaseUrl:n}}catch(a){return t.stop(q.red("Failed to initialize Prisma PostgreSQL")),a instanceof Error&&je.error(a.message),null}}async function ye(e,r){try{let t=L.join(e,"apps/server",".env");await N.ensureDir(L.dirname(t));let a="";await N.pathExists(t)&&(a=await N.readFile(t,"utf8"));let o=r?`DATABASE_URL="${r.databaseUrl}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';a.includes("DATABASE_URL=")?a=a.replace(/DATABASE_URL=.*(\r?\n|$)/,`${o}$1`):a+=` ${o}`,await N.writeFile(t,a.trim())}catch(t){throw je.error("Failed to update environment configuration"),t}}function at(){Z.info(`Manual Prisma PostgreSQL Setup Instructions: 1. Visit https://console.prisma.io and create an account 2. Create a new PostgreSQL database from the dashboard 3. Get your database URL 4. Add the database URL to the .env file in apps/server/.env DATABASE_URL="your_database_url"`)}async function Gr(e){try{await g({dependencies:["@prisma/extension-accelerate"],projectDir:e});let r=L.join(e,"prisma/index.ts");await N.writeFile(r,` import { PrismaClient } from '@prisma/client'; import { withAccelerate } from "@prisma/extension-accelerate"; const prisma = new PrismaClient().$extends(withAccelerate()); export default prisma; `.trim());let a=L.join(e,"src/db/index.ts");if(await N.pathExists(a)){let o=await N.readFile(a,"utf8");o.includes("@prisma/extension-accelerate")||(o=`import { withAccelerate } from "@prisma/extension-accelerate"; ${o}`,o=o.replace("export const db = new PrismaClient();","export const db = new PrismaClient().$extends(withAccelerate());"),await N.writeFile(a,o))}return!0}catch{return Z.warn(q.yellow("Could not add Prisma Accelerate extension automatically")),!1}}async function ot(e){let{projectName:r,packageManager:t}=e,a=L.resolve(process.cwd(),r),o=L.join(a,"apps/server"),n=Pe();n.start("Setting up Prisma PostgreSQL");try{await N.ensureDir(o),n.stop("Starting Prisma setup");let i=await Vr(o,t);if(i)await ye(a,i),await Gr(o),Z.success(q.green("Prisma PostgreSQL database configured successfully!"));else{let s=Pe();s.start("Setting up fallback configuration"),await ye(a),s.stop("Manual setup required"),at()}}catch(i){n.stop(q.red("Prisma PostgreSQL setup failed")),je.error(q.red(`Error during Prisma PostgreSQL setup: ${i instanceof Error?i.message:String(i)}`));try{await ye(a),at()}catch{}Z.info("Setup completed with manual configuration required.")}}import Jr from"node:os";import pe from"node:path";import{cancel as ke,confirm as Qr,isCancel as xe,log as V,select as Hr,spinner as G,text as Yr}from"@clack/prompts";import Kr from"consola";import{$ as I}from"execa";import nt from"fs-extra";import $ from"picocolors";async function Xr(){return ne("turso")}async function Zr(){try{return!(await I`turso auth whoami`).stdout.includes("You are not logged in")}catch{return!1}}async function ea(){let e=G();try{return e.start("Logging in to Turso..."),await I`turso auth login`,e.stop("Logged in to Turso successfully!"),!0}catch(r){throw e.stop($.red("Failed to log in to Turso")),r}}async function ta(e){let r=G();try{if(r.start("Installing Turso CLI..."),e)await I`brew install tursodatabase/tap/turso`;else{let{stdout:t}=await I`curl -sSfL https://get.tur.so/install.sh`;await I`bash -c '${t}'`}return r.stop("Turso CLI installed successfully!"),!0}catch(t){throw t instanceof Error&&t.message.includes("User force closed")?(r.stop("Turso CLI installation cancelled"),V.warn($.yellow("Turso CLI installation cancelled by user")),new Error("Installation cancelled")):(r.stop($.red("Failed to install Turso CLI")),t)}}async function ra(){let e=G();try{e.start("Fetching Turso groups...");let{stdout:r}=await I`turso group list`,t=r.trim().split(` `);if(t.length<=1)return e.stop("No Turso groups found"),[];let a=t.slice(1).map(o=>{let[n,i,s,c]=o.trim().split(/\s{2,}/);return{name:n,locations:i,version:s,status:c}});return e.stop(`Found ${a.length} Turso groups`),a}catch(r){return e.stop($.red("Error fetching Turso groups")),console.error("Error fetching Turso groups:",r),[]}}async function aa(){let e=await ra();if(e.length===0)return null;if(e.length===1)return V.info(`Using the only available group: ${$.blue(e[0].name)}`),e[0].name;let r=e.map(a=>({value:a.name,label:`${a.name} (${a.locations})`})),t=await Hr({message:"Select a Turso database group:",options:r});return xe(t)&&(ke($.red("Operation cancelled")),process.exit(0)),t}async function oa(e,r){let t=G();try{t.start(`Creating Turso database "${e}"${r?` in group "${r}"`:""}...`),r?await I`turso db create ${e} --group ${r}`:await I`turso db create ${e}`,t.stop(`Created database "${e}"`)}catch(a){throw t.stop($.red(`Failed to create database "${e}"`)),a instanceof Error&&a.message.includes("already exists")?new Error("DATABASE_EXISTS"):a}t.start("Retrieving database connection details...");try{let{stdout:a}=await I`turso db show ${e} --url`,{stdout:o}=await I`turso db tokens create ${e}`;return t.stop("Retrieved database connection details"),{dbUrl:a.trim(),authToken:o.trim()}}catch(a){throw t.stop($.red("Failed to retrieve database connection details")),a}}async function ce(e,r){let t=pe.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.dbUrl}" DATABASE_AUTH_TOKEN="${r.authToken}"`:`DATABASE_URL= DATABASE_AUTH_TOKEN=`;await nt.ensureDir(pe.dirname(t)),await nt.writeFile(t,a)}function ve(){V.info(`Manual Turso Setup Instructions: 1. Visit https://turso.tech and create an account 2. Create a new database from the dashboard 3. Get your database URL and authentication token 4. Add these credentials to the .env file in apps/server/.env DATABASE_URL=your_database_url DATABASE_AUTH_TOKEN=your_auth_token`)}async function st(e){let{projectName:r,orm:t}=e,a=pe.resolve(process.cwd(),r),o=t==="drizzle",n=G();n.start("Setting up Turso database");try{let i=Jr.platform(),s=i==="darwin",c=i==="linux";if(i==="win32"){n.stop($.yellow("Turso setup not supported on Windows")),V.warn($.yellow("Automatic Turso setup is not supported on Windows.")),await ce(a),ve();return}if(n.stop("Checking Turso CLI"),!await Xr()){let x=await Qr({message:"Would you like to install Turso CLI?",initialValue:!0});if(xe(x)&&(ke($.red("Operation cancelled")),process.exit(0)),!x){await ce(a),ve();return}await ta(s)}await Zr()||await ea();let f=await aa(),k=!1,S="",E=pe.basename(a);for(;!k;){let x=await Yr({message:"Enter a name for your database:",defaultValue:E,initialValue:E,placeholder:E});xe(x)&&(ke($.red("Operation cancelled")),process.exit(0)),S=x;try{let F=await oa(S,f),Q=G();Q.start("Writing configuration to .env file"),await ce(a,F),Q.stop("Turso database configured successfully!"),k=!0}catch(F){if(F instanceof Error&&F.message==="DATABASE_EXISTS")V.warn($.yellow(`Database "${$.red(S)}" already exists`)),E=`${S}-${Math.floor(Math.random()*1e3)}`;else throw F}}}catch(i){n.stop($.red("Failed to set up Turso database")),Kr.error($.red(`Error during Turso setup: ${i instanceof Error?i.message:String(i)}`)),await ce(a),ve(),V.success("Setup completed with manual configuration required.")}}import ee from"node:path";import{cancel as na,isCancel as sa,log as De,spinner as Se,text as ia}from"@clack/prompts";import{consola as le}from"consola";import{execa as ca}from"execa";import Ae from"fs-extra";import O from"picocolors";async function $e(e,r,t){let a=Se();try{let o=R(e,r);a&&a.start(t);let n=await ca(o,{shell:!0});return a&&a.stop(t),n}catch(o){throw a&&a.stop(O.red(`Failed: ${t}`)),o}}async function pa(e){try{let t=await $e(e,"neonctl projects list");return!t.stdout.includes("not authenticated")&&!t.stdout.includes("error")}catch{return!1}}async function la(e){try{return await $e(e,"neonctl auth","Authenticating with Neon..."),De.success("Authenticated with Neon successfully!"),!0}catch(r){throw le.error(O.red("Failed to authenticate with Neon")),r}}async function da(e,r){try{let t=`neonctl projects create --name "${e}" --output json`,{stdout:a}=await $e(r,t,`Creating Neon project "${e}"...`),o=JSON.parse(a);if(o.project&&o.connection_uris&&o.connection_uris.length>0){let n=o.project.id,i=o.connection_uris[0].connection_uri,s=o.connection_uris[0].connection_parameters;return{connectionString:i,projectId:n,dbName:s.database,roleName:s.role}}return le.error(O.red("Failed to extract connection information from response")),null}catch(t){throw le.error(O.red("Failed to create Neon project")),t}}async function it(e,r){let t=ee.join(e,"apps/server",".env"),a=r?`DATABASE_URL="${r.connectionString}"`:'DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"';return await Ae.ensureDir(ee.dirname(t)),await Ae.writeFile(t,a),!0}function ua(){De.info(`Manual Neon PostgreSQL Setup Instructions: 1. Visit https://neon.tech and create an account 2. Create a new project from the dashboard 3. Get your connection string 4. Add the database URL to the .env file in apps/server/.env DATABASE_URL="your_connection_string"`)}async function ct(e){let{projectName:r,packageManager:t}=e,a=ee.resolve(process.cwd(),r),o=Se();o.start("Setting up Neon PostgreSQL");try{let n=await pa(t);o.stop("Setting up Neon PostgreSQL"),n||(De.info("Please authenticate with Neon to continue:"),await la(t));let i=ee.basename(a),s=await ia({message:"Enter a name for your Neon project:",defaultValue:i,initialValue:i});sa(s)&&(na(O.red("Operation cancelled")),process.exit(0));let c=await da(s,t);if(!c)throw new Error("Failed to create project - couldn't get connection information");let p=Se();p.start("Configuring database connection"),await Ae.ensureDir(ee.join(a,"apps/server")),await it(a,c),p.stop("Neon database configured successfully!")}catch(n){o.stop(O.red("Neon PostgreSQL setup failed")),n instanceof Error&&le.error(O.red(n.message)),await it(a),ua()}}async function lt(e){let{projectName:r,database:t,orm:a,packageManager:o,dbSetup:n}=e,i=Ce.resolve(process.cwd(),r),s=ma(),c=Ce.join(i,"apps/server");if(t==="none"){await ga.remove(Ce.join(c,"src/db"));return}try{a==="prisma"?await g({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:c}):a==="drizzle"&&(t==="sqlite"?await g({dependencies:["drizzle-orm","@libsql/client"],devDependencies:["drizzle-kit"],projectDir:c}):t==="postgres"?await g({dependencies:["drizzle-orm","pg"],devDependencies:["drizzle-kit","@types/pg"],projectDir:c}):t==="mysql"&&await g({dependencies:["drizzle-orm","mysql2"],devDependencies:["drizzle-kit"],projectDir:c})),t==="sqlite"&&n==="turso"?await st(e):t==="postgres"?a==="prisma"&&n==="prisma-postgres"?await ot(e):n==="neon"&&await ct(e):t==="mongodb"&&n==="mongodb-atlas"&&await rt(e)}catch(p){throw s.stop(pt.red("Failed to set up database")),p instanceof Error&&fa.error(pt.red(p.message)),p}}import M from"node:path";import de from"fs-extra";async function Ee(e,r){await de.ensureDir(M.dirname(e));let t="";await de.pathExists(e)&&(t=await de.readFile(e,"utf8"));let a=!1;for(let{key:o,value:n,condition:i}of r)if(i){let s=new RegExp(`^${o}=.*$`,"m");s.test(t)?n&&(t=t.replace(s,`${o}=${n}`),a=!0):(t+=` ${o}=${n}`,a=!0)}a&&await de.writeFile(e,t.trim())}async function dt(e){let{projectName:r}=e,t=M.resolve(process.cwd(),r),a=e,o=M.join(t,"apps/server"),n=M.join(o,".env"),i=a.frontend.includes("react-router"),s=a.frontend.includes("tanstack-router"),c=a.frontend.includes("tanstack-start"),p=a.frontend.includes("next"),w=i||s||c||p,y="http://localhost:3000";i?y="http://localhost:5173":(s||c||p)&&(y="http://localhost:3001");let f="",k=a.dbSetup==="turso"||a.dbSetup==="prisma-postgres"||a.dbSetup==="mongodb-atlas"||a.dbSetup==="neon";k||(a.database==="postgres"?f="postgresql://postgres:postgres@localhost:5432/mydb?schema=public":a.database==="mysql"?f="mysql://root:password@localhost:3306/mydb":a.database==="mongodb"?f="mongodb://localhost:27017/mydatabase":a.database==="sqlite"&&(f="file:./local.db"));let S=[{key:"CORS_ORIGIN",value:y,condition:!0},{key:"BETTER_AUTH_SECRET",value:Qe(),condition:!!a.auth},{key:"BETTER_AUTH_URL",value:"http://localhost:3000",condition:!!a.auth},{key:"DATABASE_URL",value:f,condition:a.database!=="none"&&f!==""&&!k},{key:"GOOGLE_GENERATIVE_AI_API_KEY",value:"",condition:a.examples?.includes("ai")||!1}];if(await Ee(n,S),w){let E=M.join(t,"apps/web"),x="VITE_SERVER_URL";p&&(x="NEXT_PUBLIC_SERVER_URL");let F=[{key:x,value:"http://localhost:3000",condition:!0}];await Ee(M.join(E,".env"),F)}if(a.frontend.includes("native")){let E=M.join(t,"apps/native"),x=[{key:"EXPO_PUBLIC_SERVER_URL",value:"http://localhost:3000",condition:!0}];await Ee(M.join(E,".env"),x)}}import Te from"node:path";async function ut(e){let{projectName:r,examples:t,orm:a,auth:o,backend:n,frontend:i=["tanstack-router"]}=e,s=Te.resolve(process.cwd(),r);if(t.includes("ai")){let c=Te.join(s,"apps/web");await g({dependencies:["ai"],projectDir:c});let p=Te.join(s,"apps/server");await g({dependencies:["ai","@ai-sdk/google"],projectDir:p})}}import{log as ha,spinner as mt}from"@clack/prompts";import ba from"consola";import{$ as ft}from"execa";import ue from"picocolors";async function gt({projectDir:e,packageManager:r,addons:t=[]}){let a=mt();try{a.start(`Running ${r} install...`),await ft({cwd:e,stderr:"inherit"})`${r} install`,a.stop("Dependencies installed successfully"),(t.includes("biome")||t.includes("husky"))&&await wa(e,r)}catch(o){throw a.stop(ue.red("Failed to install dependencies")),o instanceof Error&&ba.error(ue.red(`Installation error: ${o.message}`)),o}}async function wa(e,r){let t=mt();try{t.start("Running Biome format check..."),await ft({cwd:e,stderr:"inherit"})`${r} biome check --write .`,t.stop("Biome check completed successfully")}catch{t.stop(ue.yellow("Biome check encountered issues")),ha.warn(ue.yellow("Some files may need manual formatting"))}}import{consola as ya}from"consola";import b from"picocolors";function ht(e){let{database:r,projectName:t,packageManager:a,depsInstalled:o,orm:n,addons:i,runtime:s,frontend:c,dbSetup:p}=e,w=a==="npm"?"npm run":a,y=`cd ${t}`,f=i?.includes("husky")||i?.includes("biome"),k=r!=="none"?va(r,n,w,s):"",S=i?.includes("tauri")?ka(w):"",E=f?ja(w):"",x=c?.includes("native")?Pa():"",F=i?.includes("pwa")&&c?.includes("react-router")?xa():"",Q=i?.includes("starlight")?Sa(w):"",cr=c?.includes("tanstack-router"),pr=c?.includes("tanstack-start"),Ne=c?.includes("react-router"),Ie=cr||Ne||pr,lr=c?.includes("native"),dr=Ie||lr,ur=Ne?"5173":"3001",mr=R(a,"taze -r");ya.box(`${b.bold("Next steps")} ${b.cyan("1.")} ${y} ${o?"":`${b.cyan("2.")} ${a} install `}${b.cyan(o?"2.":"3.")} ${w} dev ${b.bold("Your project will be available at:")} ${dr?`${Ie?`${b.cyan("\u2022")} Frontend: http://localhost:${ur} `:""}`:`${b.yellow("NOTE:")} You are creating a backend-only app (no frontend selected) `}${b.cyan("\u2022")} Backend: http://localhost:3000 ${i?.includes("starlight")?`${b.cyan("\u2022")} Docs: http://localhost:4321 `:""}${x?` ${x.trim()}`:""}${k?` ${k.trim()}`:""}${S?` ${S.trim()}`:""}${E?` ${E.trim()}`:""}${F?` ${F.trim()}`:""}${Q?` ${Q.trim()}`:""} ${b.bold(`Update all dependencies: `)}${b.cyan(mr)} ${b.bold("Like Better-T Stack?")} Please consider giving us a star on GitHub: ${b.cyan("https://github.com/AmanVarshney01/create-better-t-stack")}`)}function Pa(){return`${b.yellow("NOTE:")} For Expo connectivity issues, update apps/native/.env with your local IP: EXPO_PUBLIC_SERVER_URL=http://192.168.0.103:3000 `}function ja(e){return`${b.bold("Linting and formatting:")} ${b.cyan("\u2022")} Format and lint fix: ${`${e} check`} `}function va(e,r,t,a){let o=[];return r==="prisma"?(e==="sqlite"&&o.push(`${b.yellow("NOTE:")} Turso support with Prisma is in Early Access and requires additional setup.`,"Learn more at: https://www.prisma.io/docs/orm/overview/databases/turso"),a==="bun"&&o.push(`${b.yellow("NOTE:")} Prisma with Bun may require additional configuration. If you encounter errors, follow the guidance provided in the error messages`),o.push(`${b.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${b.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)):r==="drizzle"&&(o.push(`${b.cyan("\u2022")} Apply schema: ${`${t} db:push`}`),o.push(`${b.cyan("\u2022")} Database UI: ${`${t} db:studio`}`)),o.length?`${b.bold("Database commands:")} ${o.join(` `)} `:""}function ka(e){return` ${b.bold("Desktop app with Tauri:")} ${b.cyan("\u2022")} Start desktop app: ${`cd apps/web && ${e} desktop:dev`} ${b.cyan("\u2022")} Build desktop app: ${`cd apps/web && ${e} desktop:build`} ${b.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies. See: https://v2.tauri.app/start/prerequisites/ `}function xa(){return`${b.bold("PWA with React Router v7:")} ${b.yellow("NOTE:")} There is a known compatibility issue between VitePWA and React Router v7. See: https://github.com/vite-pwa/vite-plugin-pwa/issues/809 `}function Sa(e){return`${b.bold("Documentation with Starlight:")} ${b.cyan("\u2022")} Start docs site: ${`cd apps/docs && ${e} dev`} ${b.cyan("\u2022")} Build docs site: ${`cd apps/docs && ${e} build`} `}import wt from"node:path";import{log as Aa}from"@clack/prompts";import{$ as bt,execa as Da}from"execa";import J from"fs-extra";import $a from"picocolors";async function yt(e,r){await Ca(e,r),await Ea(e,r)}async function Ca(e,r){let t=wt.join(e,"package.json");if(await J.pathExists(t)){let a=await J.readJson(t);a.name=r.projectName;let o={dev:"turbo dev",build:"turbo build","check-types":"turbo check-types","dev:native":"turbo -F native dev","dev:web":"turbo -F web dev","dev:server":"turbo -F server dev","db:push":"turbo -F server db:push","db:studio":"turbo -F server db:studio"},n={dev:"pnpm -r dev",build:"pnpm -r build","check-types":"pnpm -r check-types","dev:native":"pnpm --filter native dev","dev:web":"pnpm --filter web dev","dev:server":"pnpm --filter server dev","db:push":"pnpm --filter server db:push","db:studio":"pnpm --filter server db:studio"},i={dev:"npm run dev --workspaces",build:"npm run build --workspaces","check-types":"npm run check-types --workspaces","dev:native":"npm run dev --workspace native","dev:web":"npm run dev --workspace web","dev:server":"npm run dev --workspace server","db:push":"npm run db:push --workspace server","db:studio":"npm run db:studio --workspace server"},s={dev:"bun run --filter '*' dev",build:"bun run --filter '*' build","check-types":"bun run --filter '*' check-types","dev:native":"bun run --filter native dev","dev:web":"bun run --filter web dev","dev:server":"bun run --filter server dev","db:push":"bun run --filter server db:push","db:studio":"bun run --filter server db:studio"};r.addons.includes("turborepo")?a.scripts=o:r.packageManager==="pnpm"?a.scripts=n:r.packageManager==="npm"?a.scripts=i:r.packageManager==="bun"?a.scripts=s:a.scripts={};let{stdout:c}=await Da(r.packageManager,["-v"],{cwd:e});a.packageManager=`${r.packageManager}@${c.trim()}`,await J.writeJson(t,a,{spaces:2})}}async function Ea(e,r){let t=wt.join(e,"apps/server/package.json");if(await J.pathExists(t)){let a=await J.readJson(t);r.database!=="none"&&(r.database==="sqlite"&&r.orm==="drizzle"&&(a.scripts["db:local"]="turso dev --db-file local.db"),r.orm==="prisma"?(a.scripts["db:push"]="prisma db push --schema ./prisma/schema",a.scripts["db:studio"]="prisma studio"):r.orm==="drizzle"&&(a.scripts["db:push"]="drizzle-kit push",a.scripts["db:studio"]="drizzle-kit studio")),await J.writeJson(t,a,{spaces:2})}}async function Pt(e,r){if(!r)return;if((await bt({cwd:e,reject:!1,stderr:"pipe"})`git --version`).exitCode!==0){Aa.warn($a.yellow("Git is not installed"));return}let a=await bt({cwd:e,reject:!1,stderr:"pipe"})`git init`;if(a.exitCode!==0)throw new Error(`Git initialization failed: ${a.stderr}`)}import me from"node:path";import fe from"fs-extra";async function jt(e){let{projectName:r,runtime:t,backend:a}=e,o=me.resolve(process.cwd(),r);if(a==="next")return;let n=me.join(o,"apps/server");t==="bun"?await Ta(n,a):t==="node"&&await Fa(n,a)}async function Ta(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"bun run --hot src/index.ts",start:"bun run dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await g({devDependencies:["@types/bun"],projectDir:e})}async function Fa(e,r){let t=me.join(e,"package.json"),a=await fe.readJson(t);a.scripts={...a.scripts,dev:"tsx watch src/index.ts",start:"node dist/src/index.js"},await fe.writeJson(t,a,{spaces:2}),await g({devDependencies:["tsx","@types/node"],projectDir:e}),r==="hono"?await g({dependencies:["@hono/node-server"],projectDir:e}):r==="elysia"&&await g({dependencies:["@elysiajs/node"],projectDir:e})}import l from"node:path";import A from"consola";import m from"fs-extra";import{globby as vt}from"globby";import C from"picocolors";import Ra from"node:path";import Fe from"fs-extra";import ge from"handlebars";async function Re(e,r,t){try{let a=await Fe.readFile(e,"utf-8"),n=ge.compile(a)(t);await Fe.ensureDir(Ra.dirname(r)),await Fe.writeFile(r,n)}catch(a){throw console.error(`Error processing template ${e}:`,a),new Error(`Failed to process template ${e}`)}}ge.registerHelper("or",(e,r)=>e||r);ge.registerHelper("eq",(e,r)=>e===r);ge.registerHelper("includes",(e,r)=>Array.isArray(e)&&e.includes(r));async function v(e,r,t,a,o=!0){let n=await vt(e,{cwd:r,dot:!0,onlyFiles:!0,absolute:!1});for(let i of n){let s=l.join(r,i),c=i;i.endsWith(".hbs")&&(c=i.slice(0,-4));let p=l.join(t,c);if(await m.ensureDir(l.dirname(p)),s.endsWith(".hbs"))await Re(s,p,a);else{if(!o&&await m.pathExists(p))continue;await m.copy(s,p,{overwrite:!0})}}}async function kt(e,r){let t=l.join(P,"templates/base");await v(["package.json","_gitignore"],t,e,r)}async function xt(e,r){let t=r.frontend.filter(o=>o==="tanstack-router"||o==="react-router"||o==="tanstack-start"||o==="next"),a=r.frontend.includes("native");if(t.length>0){let o=l.join(e,"apps/web");await m.ensureDir(o);let n=l.join(P,"templates/frontend/web-base");await m.pathExists(n)&&await v("**/*",n,o,r);for(let i of t){let s=l.join(P,`templates/frontend/${i}`);await m.pathExists(s)&&await v("**/*",s,o,r)}if(r.api!=="none"){let i=t[0],s=l.join(P,`templates/api/${r.api}/web/base`);await m.pathExists(s)&&await v("**/*",s,o,r);let c=l.join(P,`templates/api/${r.api}/web/${i}`);await m.pathExists(c)&&await v("**/*",c,o,r)}}if(a){let o=l.join(e,"apps/native");await m.ensureDir(o);let n=l.join(P,"templates/frontend/native");if(await m.pathExists(n)&&await v("**/*",n,o,r),r.api!=="none"){let i=l.join(P,`templates/api/${r.api}/native`);await m.pathExists(i)&&await v("**/*",i,o,r)}}}async function St(e,r){if(r.backend==="none")return;let t=l.join(e,"apps/server");await m.ensureDir(t);let a=l.join(P,"templates/backend/server-base");await m.pathExists(a)?await v("**/*",a,t,r):A.warn(C.yellow(`Warning: server-base template not found at ${a}`));let o=l.join(P,`templates/backend/${r.backend}`);if(await m.pathExists(o)?await v("**/*",o,t,r):A.warn(C.yellow(`Warning: Backend template directory not found, skipping: ${o}`)),r.api!=="none"){let n=l.join(P,`templates/api/${r.api}/server/base`);await m.pathExists(n)&&await v("**/*",n,t,r);let i=l.join(P,`templates/api/${r.api}/server/${r.backend}`);await m.pathExists(i)&&await v("**/*",i,t,r)}}async function At(e,r){if(r.orm==="none"||r.database==="none")return;let t=l.join(e,"apps/server");await m.ensureDir(t);let a=l.join(P,`templates/db/${r.orm}/${r.database}`);await m.pathExists(a)?await v("**/*",a,t,r):A.warn(C.yellow(`Warning: Database/ORM template directory not found, skipping: ${a}`))}async function Dt(e,r){if(!r.auth)return;let t=l.join(e,"apps/server"),a=l.join(e,"apps/web"),o=l.join(e,"apps/native"),n=r.frontend.filter(s=>s==="tanstack-router"||s==="react-router"||s==="tanstack-start"||s==="next"),i=r.frontend.includes("native");if(await m.pathExists(t)){let s=l.join(P,"templates/auth/server/base");await m.pathExists(s)?await v("**/*",s,t,r):A.warn(C.yellow(`Warning: Base auth server template not found at ${s}`));let c=l.join(P,"templates/auth/server/next");if(await m.pathExists(c)?await v("**/*",c,t,r):A.warn(C.yellow(`Warning: Next auth server template not found at ${c}`)),r.orm!=="none"&&r.database!=="none"){let p=r.orm,w=r.database,y="";p==="drizzle"?y=l.join(P,`templates/auth/server/db/drizzle/${w}`):p==="prisma"&&(y=l.join(P,`templates/auth/server/db/prisma/${w}`)),y&&await m.pathExists(y)?await v("**/*",y,t,r):A.warn(C.yellow(`Warning: Auth template for ${p}/${w} not found at ${y}`))}}else A.warn(C.yellow("Warning: apps/server directory does not exist, skipping server-side auth setup."));if(n.length>0&&await m.pathExists(a)){let s=l.join(P,"templates/auth/web/base");await m.pathExists(s)?await v("**/*",s,a,r):A.warn(C.yellow(`Warning: Base auth web template not found at ${s}`));for(let c of n){let p=l.join(P,`templates/auth/web/${c}`);await m.pathExists(p)?await v("**/*",p,a,r):A.warn(C.yellow(`Warning: Auth web template for ${c} not found at ${p}`))}}if(i&&await m.pathExists(o)){let s=l.join(P,"templates/auth/native");await m.pathExists(s)?await v("**/*",s,o,r):A.warn(C.yellow(`Warning: Auth native template not found at ${s}`))}}async function $t(e,r){if(r.addons.includes("turborepo")){let t=l.join(P,"templates/addons/turborepo");await m.pathExists(t)?await v("**/*",t,e,r):A.warn(C.yellow("Warning: Turborepo addon template not found."))}if(r.addons.includes("husky")){let t=l.join(P,"templates/addons/husky");await m.pathExists(t)?await v("**/*",t,e,r):A.warn(C.yellow("Warning: Husky addon template not found."))}if(r.addons.includes("biome")){let t=l.join(P,"templates/addons/biome");await m.pathExists(t)?await v("**/*",t,e,r):A.warn(C.yellow("Warning: Biome addon template not found."))}if(r.addons.includes("pwa")){let t=l.join(P,"templates/addons/pwa/apps/web"),a=l.join(e,"apps/web");await m.pathExists(t)?await m.pathExists(a)?await v("**/*",t,a,r):A.warn(C.yellow("Warning: apps/web directory not found, cannot setup PWA addon.")):A.warn(C.yellow("Warning: PWA addon template not found."))}}async function Ct(e,r){if(!r.examples||r.examples.length===0)return;let t=l.join(e,"apps/server"),a=l.join(e,"apps/web");for(let o of r.examples){let n=l.join(P,`templates/examples/${o}`);if(await m.pathExists(t)){let i=l.join(n,"server");if(await m.pathExists(i)&&r.orm!=="none"){let s=l.join(i,r.orm,"base");if(await m.pathExists(s)&&await v("**/*",s,t,r,!1),r.database!=="none"){let c=l.join(i,r.orm,r.database);await m.pathExists(c)&&await v("**/*",c,t,r,!1)}}}if(await m.pathExists(a)){let i=l.join(n,"web");if(await m.pathExists(i)){let s=r.frontend.filter(c=>["next","react-router","tanstack-router","tanstack-start"].includes(c));for(let c of s){let p=l.join(i,c);await m.pathExists(p)&&await v("**/*",p,a,r,!1)}}}}}async function Et(e,r){let t=await vt(["**/.gitignore.hbs","**/_gitignore"],{cwd:e,dot:!0,onlyFiles:!0,absolute:!0,ignore:["**/node_modules/**","**/.git/**"]});for(let a of t){let o=l.dirname(a),n=l.basename(a),i=l.join(o,".gitignore");try{n===".gitignore.hbs"?(await Re(a,i,r),await m.remove(a)):n==="_gitignore"&&await m.move(a,i,{overwrite:!0})}catch(s){A.error(`Error processing gitignore file ${a}:`,s)}}}async function Tt(e,r){if(r.packageManager==="pnpm"){let t=l.join(P,"templates/extras/pnpm-workspace.yaml"),a=l.join(e,"pnpm-workspace.yaml");await m.pathExists(t)?await m.copy(t,a):A.warn(C.yellow("Warning: pnpm-workspace.yaml template not found."))}}async function Rt(e){let r=Ma(),t=Ba.resolve(process.cwd(),e.projectName);try{return await La.ensureDir(t),await kt(t,e),await xt(t,e),await St(t,e),await Ke(e),await At(t,e),await lt(e),await Dt(t,e),await He(e),await $t(t,e),e.addons.length>0&&e.addons[0]!=="none"&&await qe(e),await Ct(t,e),await Tt(t,e),e.examples.length>0&&e.examples[0]!=="none"&&await ut(e),await Ve(e),await jt(e),await dt(e),await yt(t,e),await Xe(t,e),await Pt(t,e.git),await Et(t,e),Ia.success("Project template successfully scaffolded!"),e.install&&await gt({projectDir:t,packageManager:e.packageManager,addons:e.addons}),ht({...e,depsInstalled:e.install}),t}catch(a){throw r.stop(Ft.red("Failed")),a instanceof Error&&(Na(Ft.red(`Error during project creation: ${a.message}`)),console.error(a.stack),process.exit(1)),a}}import{cancel as _o,group as zo}from"@clack/prompts";import Wo from"picocolors";import{cancel as Oa,isCancel as Ua,multiselect as _a}from"@clack/prompts";import za from"picocolors";async function Bt(e,r){if(e!==void 0)return e;let t=r?.includes("react-router")||r?.includes("tanstack-router"),a=[{value:"starlight",label:"Starlight",hint:"Add Astro Starlight documentation site"},{value:"biome",label:"Biome",hint:"Add Biome for linting and formatting"},{value:"husky",label:"Husky",hint:"Add Git hooks with Husky, lint-staged (requires Biome)"},{value:"turborepo",label:"Turborepo",hint:"Optimize builds for monorepos"}],n=t?[...[{value:"pwa",label:"PWA (Progressive Web App)",hint:"Make your app installable and work offline"},{value:"tauri",label:"Tauri Desktop App",hint:"Build native desktop apps from your web frontend"}],...a]:a,i=h.addons.filter(c=>t||c!=="pwa"&&c!=="tauri"),s=await _a({message:"Select addons",options:n,initialValues:i,required:!1});return Ua(s)&&(Oa(za.red("Operation cancelled")),process.exit(0)),s.includes("husky")&&!s.includes("biome")&&s.push("biome"),s}import{cancel as Wa,isCancel as qa,select as Va}from"@clack/prompts";import Ga from"picocolors";async function Nt(e,r){if(e)return e;let t=r?.includes("native"),a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy"},{value:"orpc",label:"oRPC",hint:"End-to-end type-safe APIs that adhere to OpenAPI standards"},{value:"none",label:"None",hint:"No API integration (skip API setup)"}];t&&(a=[{value:"trpc",label:"tRPC",hint:"End-to-end typesafe APIs made easy (Required for Native frontend)"}]);let o=await Va({message:"Select API type",options:a,initialValue:t?"trpc":h.api});return qa(o)&&(Wa(Ga.red("Operation cancelled")),process.exit(0)),t&&o!=="trpc"?"trpc":o}import{cancel as Ja,confirm as Qa,isCancel as Ha}from"@clack/prompts";import Ya from"picocolors";async function It(e,r){if(!r)return!1;if(e!==void 0)return e;let t=await Qa({message:"Add authentication with Better-Auth?",initialValue:h.auth});return Ha(t)&&(Ja(Ya.red("Operation cancelled")),process.exit(0)),t}import{cancel as Ka,isCancel as Xa,select as Za}from"@clack/prompts";import eo from"picocolors";async function Mt(e){if(e!==void 0)return e;let r=await Za({message:"Select backend framework",options:[{value:"hono",label:"Hono",hint:"Lightweight, ultrafast web framework"},{value:"next",label:"Next.js",hint:"Full-stack framework with API routes"},{value:"express",label:"Express",hint:"Fast, unopinionated, minimalist web framework for Node.js"},{value:"elysia",label:"Elysia",hint:"Ergonomic web framework for building backend servers"}],initialValue:h.backend});return Xa(r)&&(Ka(eo.red("Operation cancelled")),process.exit(0)),r}import{cancel as to,isCancel as ro,select as ao}from"@clack/prompts";import oo from"picocolors";async function Lt(e){if(e!==void 0)return e;let r=await ao({message:"Select database",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"lightweight, server-less, embedded relational database management system"},{value:"postgres",label:"PostgreSQL",hint:"powerful, open source object-relational database system"},{value:"mysql",label:"MySQL",hint:"popular open-source relational database system"},{value:"mongodb",label:"MongoDB",hint:"open-source NoSQL database that stores data in JSON-like documents called BSON"}],initialValue:h.database});return ro(r)&&(to(oo.red("Operation cancelled")),process.exit(0)),r}import{cancel as no,isCancel as so,select as io}from"@clack/prompts";import co from"picocolors";async function Ot(e,r,t){if(r!==void 0)return r;if(e==="sqlite"&&t==="prisma")return"none";let a=[];if(e==="sqlite")a=[{value:"turso",label:"Turso",hint:"SQLite for Production. Powered by libSQL"},{value:"none",label:"None",hint:"Manual s