create-better-t-stack 2.13.1 → 2.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Follow the prompts to configure your project or use the `--yes` flag for default
|
|
|
27
27
|
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
28
28
|
| **TypeScript** | End-to-end type safety across all parts of your application |
|
|
29
29
|
| **Frontend** | • React with TanStack Router<br>• React with React Router<br>• React with TanStack Start (SSR)<br>• Next.js<br>• SvelteKit<br>• Nuxt (Vue)<br>• SolidJS<br>• React Native with NativeWind (via Expo)<br>• React Native with Unistyles (via Expo)<br>• None |
|
|
30
|
-
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• None |
|
|
30
|
+
| **Backend** | • Hono<br>• Express<br>• Elysia<br>• Next.js API routes<br>• Convex<br>• Fastify<br>• None |
|
|
31
31
|
| **API Layer** | • tRPC (type-safe APIs)<br>• oRPC (OpenAPI-compatible type-safe APIs)<br>• None |
|
|
32
32
|
| **Runtime** | • Bun<br>• Node.js |
|
|
33
33
|
| **Database** | • SQLite<br>• PostgreSQL<br>• MySQL<br>• MongoDB<br>• None |
|
|
@@ -60,7 +60,7 @@ Options:
|
|
|
60
60
|
--install Install dependencies
|
|
61
61
|
--no-install Skip installing dependencies
|
|
62
62
|
--db-setup <setup> Database setup (turso, neon, prisma-postgres, mongodb-atlas, none)
|
|
63
|
-
--backend <framework> Backend framework (hono, express, elysia, next, convex)
|
|
63
|
+
--backend <framework> Backend framework (hono, express, elysia, next, convex, fastify, none)
|
|
64
64
|
--runtime <runtime> Runtime (bun, node, none)
|
|
65
65
|
--api <type> API type (trpc, orpc, none)
|
|
66
66
|
-h, --help Display help
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import e from"node:path";import{cancel as t,confirm as n,group as r,intro as i,isCancel as a,log as o,multiselect as s,outro as c,password as l,select as u,spinner as d,text as f}from"@clack/prompts";import p,{consola as m}from"consola";import h from"fs-extra";import g from"picocolors";import _ from"yargs";import{hideBin as v}from"yargs/helpers";import{fileURLToPath as y}from"node:url";import{$ as b,execa as x}from"execa";import S from"node:os";import{globby as C}from"globby";import w from"handlebars";import T from"gradient-string";const E=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith(`pnpm`)?`pnpm`:e?.startsWith(`bun`)?`bun`:`npm`},D=y(import.meta.url),O=e.dirname(D),k=e.join(O,`../`),A={projectName:`my-better-t-app`,projectDir:e.resolve(process.cwd(),`my-better-t-app`),relativePath:`my-better-t-app`,frontend:[`tanstack-router`],database:`sqlite`,orm:`drizzle`,auth:!0,addons:[`turborepo`],examples:[],git:!0,packageManager:E(),install:!0,dbSetup:`none`,backend:`hono`,runtime:`bun`,api:`trpc`},j={"better-auth":`^1.2.7`,"@better-auth/expo":`^1.2.7`,"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.7.0`,prisma:`^6.7.0`,mongoose:`^8.14.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`,fastify:`^5.3.3`,"@fastify/cors":`^11.0.1`,turbo:`^2.4.2`,ai:`^4.3.16`,"@ai-sdk/google":`^1.2.3`,"@ai-sdk/vue":`^1.2.8`,"@ai-sdk/svelte":`^2.1.9`,"@ai-sdk/react":`^1.2.12`,"@prisma/extension-accelerate":`^1.3.0`,"@orpc/server":`^1.2.0`,"@orpc/client":`^1.2.0`,"@orpc/react-query":`^1.2.0`,"@orpc/solid-query":`^1.2.0`,"@orpc/vue-query":`^1.2.0`,"@orpc/svelte-query":`^1.2.0`,"@trpc/tanstack-react-query":`^11.0.0`,"@trpc/server":`^11.0.0`,"@trpc/client":`^11.0.0`,convex:`^1.23.0`,"@convex-dev/react-query":`^0.0.0-alpha.8`,"convex-svelte":`^0.0.11`,"@tanstack/svelte-query":`^5.74.4`,"@tanstack/react-query-devtools":`^5.69.0`,"@tanstack/react-query":`^5.69.0`,"@tanstack/solid-query":`^5.75.0`,"@tanstack/solid-query-devtools":`^5.75.0`},M=async t=>{let{dependencies:n=[],devDependencies:r=[],projectDir:i}=t,a=e.join(i,`package.json`),o=await h.readJson(a);o.dependencies||={},o.devDependencies||={};for(let e of n){let t=j[e];t?o.dependencies[e]=t:console.warn(`Warning: Dependency ${e} not found in version map.`)}for(let e of r){let t=j[e];t?o.devDependencies[e]=t:console.warn(`Warning: Dev dependency ${e} not found in version map.`)}await h.writeJson(a,o,{spaces:2})};function N(e,t){switch(e){case`pnpm`:return`pnpm dlx ${t}`;case`bun`:return`bunx ${t}`;default:return`npx ${t}`}}async function ee(t){let{packageManager:n,projectDir:r}=t,i=d();try{i.start(`Setting up Starlight docs...`);let t=[`docs`,`--template`,`starlight`,`--no-install`,`--add`,`tailwind`,`--no-git`,`--skip-houston`],a=t.join(` `),o=`create-astro@latest ${a}`,s=N(n,o);await x(s,{cwd:e.join(r,`apps`),env:{CI:`true`},shell:!0}),i.stop(`Starlight docs setup successfully!`)}catch(e){i.stop(g.red(`Failed to set up Starlight docs`)),e instanceof Error&&p.error(g.red(e.message))}}async function te(t){let{packageManager:n,frontend:r,projectDir:i}=t,a=d(),o=e.join(i,`apps/web`);if(await h.pathExists(o))try{a.start(`Setting up Tauri desktop app support...`),await M({devDependencies:[`@tauri-apps/cli`],projectDir:o});let t=e.join(o,`package.json`);if(await h.pathExists(t)){let e=await h.readJson(t);e.scripts={...e.scripts,tauri:`tauri`,"desktop:dev":`tauri dev`,"desktop:build":`tauri build`},await h.writeJson(t,e,{spaces:2})}let s=r.includes(`tanstack-router`),c=r.includes(`react-router`),l=r.includes(`nuxt`),u=r.includes(`svelte`),d=r.includes(`solid`),f=r.includes(`next`),p=c||u?`http://localhost:5173`:`http://localhost:3001`,m=l?`../.output/public`:u?`../build`:f?`../.next`:c?`../build/client`:`../dist`,g=[`init`,`--app-name=${e.basename(i)}`,`--window-title=${e.basename(i)}`,`--frontend-dist=${m}`,`--dev-url=${p}`,`--before-dev-command=\"${n} run dev\"`,`--before-build-command=\"${n} run build\"`],_=g.join(` `),v=`@tauri-apps/cli@latest ${_}`,y=N(n,v);await x(y,{cwd:o,env:{CI:`true`},shell:!0}),a.stop(`Tauri desktop app support configured successfully!`)}catch(e){a.stop(g.red(`Failed to set up Tauri`)),e instanceof Error&&m.error(g.red(e.message))}}async function ne(e){let{addons:t,frontend:n,projectDir:r}=e,i=n.includes(`react-router`)||n.includes(`tanstack-router`)||n.includes(`next`),a=n.includes(`nuxt`),o=n.includes(`svelte`),s=n.includes(`solid`),c=n.includes(`next`);t.includes(`turborepo`)&&await M({devDependencies:[`turbo`],projectDir:r}),t.includes(`pwa`)&&(i||s)&&await oe(r,n),t.includes(`tauri`)&&(i||a||o||s||c)&&await te(e),t.includes(`biome`)&&await ie(r),t.includes(`husky`)&&await ae(r),t.includes(`starlight`)&&await ee(e)}function re(t,n){return n.some(e=>[`react-router`,`tanstack-router`,`nuxt`,`svelte`,`solid`].includes(e)),e.join(t,`apps/web`)}async function ie(t){await M({devDependencies:[`@biomejs/biome`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,check:`biome check --write .`},await h.writeJson(n,e,{spaces:2})}}async function ae(t){await M({devDependencies:[`husky`,`lint-staged`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,prepare:`husky`},e[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]},await h.writeJson(n,e,{spaces:2})}}async function oe(t,n){let r=n.some(e=>[`react-router`,`tanstack-router`,`solid`].includes(e));if(!r)return;let i=re(t,n);if(!await h.pathExists(i))return;await M({dependencies:[`vite-plugin-pwa`],devDependencies:[`@vite-pwa/assets-generator`],projectDir:i});let a=e.join(i,`package.json`);if(await h.pathExists(a)){let e=await h.readJson(a);e.scripts={...e.scripts,"generate-pwa-assets":`pwa-assets-generator`},await h.writeJson(a,e,{spaces:2})}}async function se(t){let{api:n,projectName:r,frontend:i,backend:a,packageManager:o,projectDir:s}=t,c=a===`convex`,l=e.join(s,`apps/web`),u=e.join(s,`apps/native`),d=await h.pathExists(l),f=await h.pathExists(u),p=i.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),m=i.includes(`nuxt`),g=i.includes(`svelte`),_=i.includes(`solid`);if(!c&&n!==`none`){let r=e.join(s,`apps/server`),i=await h.pathExists(r);i&&(n===`orpc`?await M({dependencies:[`@orpc/server`,`@orpc/client`],projectDir:r}):n===`trpc`&&(await M({dependencies:[`@trpc/server`,`@trpc/client`],projectDir:r}),t.backend===`hono`?await M({dependencies:[`@hono/trpc-server`],projectDir:r}):t.backend===`elysia`&&await M({dependencies:[`@elysiajs/trpc`],projectDir:r}))),d&&(p?n===`orpc`?await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):n===`trpc`&&await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:l}):m?n===`orpc`&&await M({dependencies:[`@orpc/vue-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):g?n===`orpc`&&await M({dependencies:[`@orpc/svelte-query`,`@orpc/client`,`@orpc/server`,`@tanstack/svelte-query`],projectDir:l}):_&&n===`orpc`&&await M({dependencies:[`@orpc/solid-query`,`@orpc/client`,`@orpc/server`,`@tanstack/solid-query`],projectDir:l})),f&&(n===`trpc`?await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:u}):n===`orpc`&&await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:u}))}let v=[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`native-nativewind`,`native-unistyles`],y=i.includes(`solid`),b=i.some(e=>v.includes(e));if(b&&!c){let t=[`@tanstack/react-query`],n=[`@tanstack/react-query-devtools`],r=i.some(e=>e!==`native-nativewind`&&e!==`native-unistyles`&&v.includes(e)),a=i.includes(`native-nativewind`)||i.includes(`native-unistyles`);if(r&&d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}if(a&&f){let n=e.join(u,`package.json`);if(await h.pathExists(n))try{await M({dependencies:t,projectDir:u})}catch{}}}if(y&&!c){let t=[`@tanstack/solid-query`],n=[`@tanstack/solid-query-devtools`];if(d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}}if(c){if(d){let t=e.join(l,`package.json`);if(await h.pathExists(t))try{let e=[`convex`];i.includes(`tanstack-start`)&&e.push(`@convex-dev/react-query`),g&&e.push(`convex-svelte`),await M({dependencies:e,projectDir:l})}catch{}}if(f){let t=e.join(u,`package.json`);if(await h.pathExists(t))try{await M({dependencies:[`convex`],projectDir:u})}catch{}}let t=`@${r}/backend`,n=o===`npm`?`*`:`workspace:*`,a=async(e,t,n)=>{try{let r=await h.readJson(e);r.dependencies||={},r.dependencies[t]!==n&&(r.dependencies[t]=n,await h.writeJson(e,r,{spaces:2}))}catch{}};if(d){let r=e.join(l,`package.json`);await h.pathExists(r)&&await a(r,t,n)}if(f){let r=e.join(u,`package.json`);await h.pathExists(r)&&await a(r,t,n)}}}async function ce(t){let{auth:n,frontend:r,backend:i,projectDir:a}=t;if(i===`convex`||!n)return;let o=e.join(a,`apps/server`),s=e.join(a,`apps/web`),c=e.join(a,`apps/native`),l=await h.pathExists(s),u=await h.pathExists(c),d=await h.pathExists(o);try{d&&await M({dependencies:[`better-auth`],projectDir:o});let e=r.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`].includes(e));e&&l&&await M({dependencies:[`better-auth`],projectDir:s}),(r.includes(`native-nativewind`)||r.includes(`native-unistyles`))&&u&&(await M({dependencies:[`better-auth`,`@better-auth/expo`],projectDir:c}),d&&await M({dependencies:[`@better-auth/expo`],projectDir:o}))}catch(e){p.error(g.red(`Failed to configure authentication dependencies`)),e instanceof Error&&p.error(g.red(e.message))}}function le(e=32){let t=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`,n=``,r=t.length;for(let i=0;i<e;i++)n+=t.charAt(Math.floor(Math.random()*r));return n}async function ue(t){let{backend:n,runtime:r,api:i,projectDir:a}=t;if(n===`convex`)return;let o=n,s=e.join(a,`apps/server`),c=[],l=[];o===`hono`?(c.push(`hono`),i===`trpc`&&c.push(`@hono/trpc-server`),r===`node`&&(c.push(`@hono/node-server`),l.push(`tsx`,`@types/node`))):o===`elysia`?(c.push(`elysia`,`@elysiajs/cors`),i===`trpc`&&c.push(`@elysiajs/trpc`),r===`node`&&(c.push(`@elysiajs/node`),l.push(`tsx`,`@types/node`))):o===`express`?(c.push(`express`,`cors`),l.push(`@types/express`,`@types/cors`),r===`node`&&l.push(`tsx`,`@types/node`)):o===`fastify`&&(c.push(`fastify`,`@fastify/cors`),r===`node`&&l.push(`tsx`,`@types/node`)),r===`bun`&&l.push(`@types/bun`),(c.length>0||l.length>0)&&await M({dependencies:c,devDependencies:l,projectDir:s})}async function de(t,n){let r=e.join(t,`README.md`),i=fe(n);try{await h.writeFile(r,i)}catch(e){p.error(`Failed to create README.md file:`,e)}}function fe(e){let{projectName:t,packageManager:n,database:r,auth:i,addons:a=[],orm:o=`drizzle`,runtime:s=`bun`,frontend:c=[`tanstack-router`],backend:l=`hono`,api:u=`trpc`}=e,d=l===`convex`,f=c.includes(`react-router`),p=c.includes(`tanstack-router`),m=c.includes(`native-nativewind`)||c.includes(`native-unistyles`),h=c.includes(`next`),g=c.includes(`tanstack-start`),_=c.includes(`svelte`),v=c.includes(`solid`),y=c.includes(`nuxt`),b=n===`npm`?`npm run`:n,x=`3001`;return(f||_)&&(x=`5173`),`# ${t}
|
|
2
|
+
import e from"node:path";import{cancel as t,confirm as n,group as r,intro as i,isCancel as a,log as o,multiselect as s,outro as c,password as l,select as u,spinner as d,text as f}from"@clack/prompts";import p,{consola as m}from"consola";import h from"fs-extra";import g from"picocolors";import _ from"yargs";import{hideBin as v}from"yargs/helpers";import{fileURLToPath as y}from"node:url";import{$ as b,execa as x}from"execa";import S from"node:os";import{globby as C}from"globby";import w from"handlebars";import T from"gradient-string";const E=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith(`pnpm`)?`pnpm`:e?.startsWith(`bun`)?`bun`:`npm`},D=y(import.meta.url),O=e.dirname(D),k=e.join(O,`../`),A={projectName:`my-better-t-app`,projectDir:e.resolve(process.cwd(),`my-better-t-app`),relativePath:`my-better-t-app`,frontend:[`tanstack-router`],database:`sqlite`,orm:`drizzle`,auth:!0,addons:[`turborepo`],examples:[],git:!0,packageManager:E(),install:!0,dbSetup:`none`,backend:`hono`,runtime:`bun`,api:`trpc`},j={"better-auth":`^1.2.7`,"@better-auth/expo":`^1.2.7`,"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.7.0`,prisma:`^6.7.0`,mongoose:`^8.14.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`,fastify:`^5.3.3`,"@fastify/cors":`^11.0.1`,turbo:`^2.4.2`,ai:`^4.3.16`,"@ai-sdk/google":`^1.2.3`,"@ai-sdk/vue":`^1.2.8`,"@ai-sdk/svelte":`^2.1.9`,"@ai-sdk/react":`^1.2.12`,"@prisma/extension-accelerate":`^1.3.0`,"@orpc/server":`^1.2.0`,"@orpc/client":`^1.2.0`,"@orpc/react-query":`^1.2.0`,"@orpc/solid-query":`^1.2.0`,"@orpc/vue-query":`^1.2.0`,"@orpc/svelte-query":`^1.2.0`,"@trpc/tanstack-react-query":`^11.0.0`,"@trpc/server":`^11.0.0`,"@trpc/client":`^11.0.0`,convex:`^1.23.0`,"@convex-dev/react-query":`^0.0.0-alpha.8`,"convex-svelte":`^0.0.11`,"@tanstack/svelte-query":`^5.74.4`,"@tanstack/react-query-devtools":`^5.69.0`,"@tanstack/react-query":`^5.69.0`,"@tanstack/solid-query":`^5.75.0`,"@tanstack/solid-query-devtools":`^5.75.0`},ee=()=>{let t=e.join(k,`package.json`),n=h.readJSONSync(t);return n.version??`1.0.0`};async function te(){let e=await _(v(process.argv)).scriptName(`create-better-t-stack`).usage(`$0 [project-directory] [options]`,`Create a new Better-T Stack project`).positional(`project-directory`,{describe:`Project name/directory`,type:`string`}).option(`yes`,{alias:`y`,type:`boolean`,describe:`Use default configuration and skip prompts`,default:!1}).option(`database`,{type:`string`,describe:`Database type`,choices:[`none`,`sqlite`,`postgres`,`mysql`,`mongodb`]}).option(`orm`,{type:`string`,describe:`ORM type`,choices:[`drizzle`,`prisma`,`mongoose`,`none`]}).option(`auth`,{type:`boolean`,describe:`Include authentication (use --no-auth to exclude)`}).option(`frontend`,{type:`array`,string:!0,describe:`Frontend types`,choices:[`tanstack-router`,`react-router`,`tanstack-start`,`next`,`nuxt`,`native-nativewind`,`native-unistyles`,`svelte`,`solid`,`none`]}).option(`addons`,{type:`array`,string:!0,describe:`Additional addons`,choices:[`pwa`,`tauri`,`starlight`,`biome`,`husky`,`turborepo`,`none`]}).option(`examples`,{type:`array`,string:!0,describe:`Examples to include`,choices:[`todo`,`ai`,`none`]}).option(`git`,{type:`boolean`,describe:`Initialize git repository (use --no-git to skip)`}).option(`package-manager`,{alias:`pm`,type:`string`,describe:`Package manager`,choices:[`npm`,`pnpm`,`bun`]}).option(`install`,{type:`boolean`,describe:`Install dependencies (use --no-install to skip)`}).option(`db-setup`,{type:`string`,describe:`Database setup`,choices:[`turso`,`neon`,`prisma-postgres`,`mongodb-atlas`,`supabase`,`none`]}).option(`backend`,{type:`string`,describe:`Backend framework`,choices:[`hono`,`express`,`fastify`,`next`,`elysia`,`convex`,`none`]}).option(`runtime`,{type:`string`,describe:`Runtime`,choices:[`bun`,`node`,`none`]}).option(`api`,{type:`string`,describe:`API type`,choices:[`trpc`,`orpc`,`none`]}).completion().recommendCommands().version(ee()).alias(`version`,`v`).help().alias(`help`,`h`).strict().wrap(null).parse();return e}const M=async t=>{let{dependencies:n=[],devDependencies:r=[],projectDir:i}=t,a=e.join(i,`package.json`),o=await h.readJson(a);o.dependencies||={},o.devDependencies||={};for(let e of n){let t=j[e];t?o.dependencies[e]=t:console.warn(`Warning: Dependency ${e} not found in version map.`)}for(let e of r){let t=j[e];t?o.devDependencies[e]=t:console.warn(`Warning: Dev dependency ${e} not found in version map.`)}await h.writeJson(a,o,{spaces:2})};function N(e,t){switch(e){case`pnpm`:return`pnpm dlx ${t}`;case`bun`:return`bunx ${t}`;default:return`npx ${t}`}}async function ne(t){let{packageManager:n,projectDir:r}=t,i=d();try{i.start(`Setting up Starlight docs...`);let t=[`docs`,`--template`,`starlight`,`--no-install`,`--add`,`tailwind`,`--no-git`,`--skip-houston`],a=t.join(` `),o=`create-astro@latest ${a}`,s=N(n,o);await x(s,{cwd:e.join(r,`apps`),env:{CI:`true`},shell:!0}),i.stop(`Starlight docs setup successfully!`)}catch(e){i.stop(g.red(`Failed to set up Starlight docs`)),e instanceof Error&&p.error(g.red(e.message))}}async function re(t){let{packageManager:n,frontend:r,projectDir:i}=t,a=d(),o=e.join(i,`apps/web`);if(await h.pathExists(o))try{a.start(`Setting up Tauri desktop app support...`),await M({devDependencies:[`@tauri-apps/cli`],projectDir:o});let t=e.join(o,`package.json`);if(await h.pathExists(t)){let e=await h.readJson(t);e.scripts={...e.scripts,tauri:`tauri`,"desktop:dev":`tauri dev`,"desktop:build":`tauri build`},await h.writeJson(t,e,{spaces:2})}let s=r.includes(`tanstack-router`),c=r.includes(`react-router`),l=r.includes(`nuxt`),u=r.includes(`svelte`),d=r.includes(`solid`),f=r.includes(`next`),p=c||u?`http://localhost:5173`:`http://localhost:3001`,m=l?`../.output/public`:u?`../build`:f?`../.next`:c?`../build/client`:`../dist`,g=[`init`,`--app-name=${e.basename(i)}`,`--window-title=${e.basename(i)}`,`--frontend-dist=${m}`,`--dev-url=${p}`,`--before-dev-command=\"${n} run dev\"`,`--before-build-command=\"${n} run build\"`],_=g.join(` `),v=`@tauri-apps/cli@latest ${_}`,y=N(n,v);await x(y,{cwd:o,env:{CI:`true`},shell:!0}),a.stop(`Tauri desktop app support configured successfully!`)}catch(e){a.stop(g.red(`Failed to set up Tauri`)),e instanceof Error&&m.error(g.red(e.message))}}async function ie(e){let{addons:t,frontend:n,projectDir:r}=e,i=n.includes(`react-router`)||n.includes(`tanstack-router`)||n.includes(`next`),a=n.includes(`nuxt`),o=n.includes(`svelte`),s=n.includes(`solid`),c=n.includes(`next`);t.includes(`turborepo`)&&await M({devDependencies:[`turbo`],projectDir:r}),t.includes(`pwa`)&&(i||s)&&await ce(r,n),t.includes(`tauri`)&&(i||a||o||s||c)&&await re(e),t.includes(`biome`)&&await oe(r),t.includes(`husky`)&&await se(r),t.includes(`starlight`)&&await ne(e)}function ae(t,n){return n.some(e=>[`react-router`,`tanstack-router`,`nuxt`,`svelte`,`solid`].includes(e)),e.join(t,`apps/web`)}async function oe(t){await M({devDependencies:[`@biomejs/biome`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,check:`biome check --write .`},await h.writeJson(n,e,{spaces:2})}}async function se(t){await M({devDependencies:[`husky`,`lint-staged`],projectDir:t});let n=e.join(t,`package.json`);if(await h.pathExists(n)){let e=await h.readJson(n);e.scripts={...e.scripts,prepare:`husky`},e[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]},await h.writeJson(n,e,{spaces:2})}}async function ce(t,n){let r=n.some(e=>[`react-router`,`tanstack-router`,`solid`].includes(e));if(!r)return;let i=ae(t,n);if(!await h.pathExists(i))return;await M({dependencies:[`vite-plugin-pwa`],devDependencies:[`@vite-pwa/assets-generator`],projectDir:i});let a=e.join(i,`package.json`);if(await h.pathExists(a)){let e=await h.readJson(a);e.scripts={...e.scripts,"generate-pwa-assets":`pwa-assets-generator`},await h.writeJson(a,e,{spaces:2})}}async function le(t){let{api:n,projectName:r,frontend:i,backend:a,packageManager:o,projectDir:s}=t,c=a===`convex`,l=e.join(s,`apps/web`),u=e.join(s,`apps/native`),d=await h.pathExists(l),f=await h.pathExists(u),p=i.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),m=i.includes(`nuxt`),g=i.includes(`svelte`),_=i.includes(`solid`);if(!c&&n!==`none`){let r=e.join(s,`apps/server`),i=await h.pathExists(r);i&&(n===`orpc`?await M({dependencies:[`@orpc/server`,`@orpc/client`],projectDir:r}):n===`trpc`&&(await M({dependencies:[`@trpc/server`,`@trpc/client`],projectDir:r}),t.backend===`hono`?await M({dependencies:[`@hono/trpc-server`],projectDir:r}):t.backend===`elysia`&&await M({dependencies:[`@elysiajs/trpc`],projectDir:r}))),d&&(p?n===`orpc`?await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):n===`trpc`&&await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:l}):m?n===`orpc`&&await M({dependencies:[`@orpc/vue-query`,`@orpc/client`,`@orpc/server`],projectDir:l}):g?n===`orpc`&&await M({dependencies:[`@orpc/svelte-query`,`@orpc/client`,`@orpc/server`,`@tanstack/svelte-query`],projectDir:l}):_&&n===`orpc`&&await M({dependencies:[`@orpc/solid-query`,`@orpc/client`,`@orpc/server`,`@tanstack/solid-query`],projectDir:l})),f&&(n===`trpc`?await M({dependencies:[`@trpc/tanstack-react-query`,`@trpc/client`,`@trpc/server`],projectDir:u}):n===`orpc`&&await M({dependencies:[`@orpc/react-query`,`@orpc/client`,`@orpc/server`],projectDir:u}))}let v=[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`native-nativewind`,`native-unistyles`],y=i.includes(`solid`),b=i.some(e=>v.includes(e));if(b&&!c){let t=[`@tanstack/react-query`],n=[`@tanstack/react-query-devtools`],r=i.some(e=>e!==`native-nativewind`&&e!==`native-unistyles`&&v.includes(e)),a=i.includes(`native-nativewind`)||i.includes(`native-unistyles`);if(r&&d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}if(a&&f){let n=e.join(u,`package.json`);if(await h.pathExists(n))try{await M({dependencies:t,projectDir:u})}catch{}}}if(y&&!c){let t=[`@tanstack/solid-query`],n=[`@tanstack/solid-query-devtools`];if(d){let r=e.join(l,`package.json`);if(await h.pathExists(r))try{await M({dependencies:t,devDependencies:n,projectDir:l})}catch{}}}if(c){if(d){let t=e.join(l,`package.json`);if(await h.pathExists(t))try{let e=[`convex`];i.includes(`tanstack-start`)&&e.push(`@convex-dev/react-query`),g&&e.push(`convex-svelte`),await M({dependencies:e,projectDir:l})}catch{}}if(f){let t=e.join(u,`package.json`);if(await h.pathExists(t))try{await M({dependencies:[`convex`],projectDir:u})}catch{}}let t=`@${r}/backend`,n=o===`npm`?`*`:`workspace:*`,a=async(e,t,n)=>{try{let r=await h.readJson(e);r.dependencies||={},r.dependencies[t]!==n&&(r.dependencies[t]=n,await h.writeJson(e,r,{spaces:2}))}catch{}};if(d){let r=e.join(l,`package.json`);await h.pathExists(r)&&await a(r,t,n)}if(f){let r=e.join(u,`package.json`);await h.pathExists(r)&&await a(r,t,n)}}}async function ue(t){let{auth:n,frontend:r,backend:i,projectDir:a}=t;if(i===`convex`||!n)return;let o=e.join(a,`apps/server`),s=e.join(a,`apps/web`),c=e.join(a,`apps/native`),l=await h.pathExists(s),u=await h.pathExists(c),d=await h.pathExists(o);try{d&&await M({dependencies:[`better-auth`],projectDir:o});let e=r.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`].includes(e));e&&l&&await M({dependencies:[`better-auth`],projectDir:s}),(r.includes(`native-nativewind`)||r.includes(`native-unistyles`))&&u&&(await M({dependencies:[`better-auth`,`@better-auth/expo`],projectDir:c}),d&&await M({dependencies:[`@better-auth/expo`],projectDir:o}))}catch(e){p.error(g.red(`Failed to configure authentication dependencies`)),e instanceof Error&&p.error(g.red(e.message))}}function de(e=32){let t=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`,n=``,r=t.length;for(let i=0;i<e;i++)n+=t.charAt(Math.floor(Math.random()*r));return n}async function fe(t){let{backend:n,runtime:r,api:i,projectDir:a}=t;if(n===`convex`)return;let o=n,s=e.join(a,`apps/server`),c=[],l=[];o===`hono`?(c.push(`hono`),i===`trpc`&&c.push(`@hono/trpc-server`),r===`node`&&(c.push(`@hono/node-server`),l.push(`tsx`,`@types/node`))):o===`elysia`?(c.push(`elysia`,`@elysiajs/cors`),i===`trpc`&&c.push(`@elysiajs/trpc`),r===`node`&&(c.push(`@elysiajs/node`),l.push(`tsx`,`@types/node`))):o===`express`?(c.push(`express`,`cors`),l.push(`@types/express`,`@types/cors`),r===`node`&&l.push(`tsx`,`@types/node`)):o===`fastify`&&(c.push(`fastify`,`@fastify/cors`),r===`node`&&l.push(`tsx`,`@types/node`)),r===`bun`&&l.push(`@types/bun`),(c.length>0||l.length>0)&&await M({dependencies:c,devDependencies:l,projectDir:s})}async function P(e){try{let t=process.platform===`win32`;if(t){let t=await x(`where`,[e]);return t.exitCode===0}let n=await x(`which`,[e]);return n.exitCode===0}catch{return!1}}async function F(t,n){await h.ensureDir(e.dirname(t));let r=``;await h.pathExists(t)&&(r=await h.readFile(t,`utf8`));let i=!1,a=``,o=[];for(let{key:e,value:t,condition:s}of n)if(s){let n=RegExp(`^${e}=.*$`,`m`),s=t??``;if(o.push(`${e}=`),n.test(r)){let t=r.match(n);t&&t[0]!==`${e}=${s}`&&(r=r.replace(n,`${e}=${s}`),i=!0)}else a+=`${e}=${s}\n`,i=!0}a&&(r.length>0&&!r.endsWith(`
|
|
3
|
+
`)&&(r+=`
|
|
4
|
+
`),r+=a),i&&await h.writeFile(t,r.trimEnd());let s=t.replace(/\.env$/,`.env.example`),c=``;await h.pathExists(s)&&(c=await h.readFile(s,`utf8`));let l=!1,u=``;for(let e of o){let t=e.split(`=`)[0],n=RegExp(`^${t}=.*$`,`m`);n.test(c)||(u+=`${e}\n`,l=!0)}u&&(c.length>0&&!c.endsWith(`
|
|
5
|
+
`)&&(c+=`
|
|
6
|
+
`),c+=u),(l||!await h.pathExists(s))&&await h.writeFile(s,c.trimEnd())}async function pe(t){let{backend:n,frontend:r,database:i,auth:a,examples:o,dbSetup:s,projectDir:c}=t,l=r.includes(`react-router`),u=r.includes(`tanstack-router`),d=r.includes(`tanstack-start`),f=r.includes(`next`),p=r.includes(`nuxt`),m=r.includes(`svelte`),g=r.includes(`solid`),_=l||u||d||f||p||g||m;if(_){let t=e.join(c,`apps/web`);if(await h.pathExists(t)){let r=`VITE_SERVER_URL`,i=`http://localhost:3000`;f?r=`NEXT_PUBLIC_SERVER_URL`:p?r=`NUXT_PUBLIC_SERVER_URL`:m&&(r=`PUBLIC_SERVER_URL`),n===`convex`&&(r=f?`NEXT_PUBLIC_CONVEX_URL`:p?`NUXT_PUBLIC_CONVEX_URL`:m?`PUBLIC_CONVEX_URL`:`VITE_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:r,value:i,condition:!0}];await F(e.join(t,`.env`),a)}}if(r.includes(`native-nativewind`)||r.includes(`native-unistyles`)){let t=e.join(c,`apps/native`);if(await h.pathExists(t)){let r=`EXPO_PUBLIC_SERVER_URL`,i=`http://localhost:3000`;n===`convex`&&(r=`EXPO_PUBLIC_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:r,value:i,condition:!0}];await F(e.join(t,`.env`),a)}}if(n===`convex`)return;let v=e.join(c,`apps/server`);if(!await h.pathExists(v))return;let y=e.join(v,`.env`),b=`http://localhost:3001`;(l||m)&&(b=`http://localhost:5173`);let x=null,S=s===`turso`||s===`prisma-postgres`||s===`mongodb-atlas`||s===`neon`||s===`supabase`;if(i!==`none`&&!S)switch(i){case`postgres`:x=`postgresql://postgres:password@localhost:5432/postgres`;break;case`mysql`:x=`mysql://root:password@localhost:3306/mydb`;break;case`mongodb`:x=`mongodb://localhost:27017/mydatabase`;break;case`sqlite`:x=`file:./local.db`;break}let C=[{key:`CORS_ORIGIN`,value:b,condition:!0},{key:`BETTER_AUTH_SECRET`,value:de(),condition:!!a},{key:`BETTER_AUTH_URL`,value:`http://localhost:3000`,condition:!!a},{key:`DATABASE_URL`,value:x,condition:i!==`none`&&!S},{key:`GOOGLE_GENERATIVE_AI_API_KEY`,value:``,condition:o?.includes(`ai`)||!1}];await F(y,C)}async function me(){let e=d();e.start(`Checking for MongoDB Atlas CLI`);try{let t=await P(`atlas`);return e.stop(t?`MongoDB Atlas CLI found`:g.yellow(`MongoDB Atlas CLI not found`)),t}catch{return e.stop(g.red(`Error checking for MongoDB Atlas CLI`)),!1}}async function he(e){try{let n=await me();if(!n)return p.error(g.red(`MongoDB Atlas CLI not found.`)),o.info(g.yellow(`Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/`)),null;o.info(g.blue(`Running MongoDB Atlas setup...`)),await x(`atlas`,[`deployments`,`setup`],{cwd:e,stdio:`inherit`}),o.info(g.green(`Atlas setup complete!`));let r=await f({message:`Enter your MongoDB connection string:`,placeholder:`mongodb+srv://username:password@cluster.mongodb.net/database`,validate(e){if(!e)return`Please enter a connection string`;if(!e.startsWith(`mongodb`))return`URL should start with mongodb:// or mongodb+srv://`}});return a(r)?(t(`MongoDB setup cancelled`),null):{connectionString:r}}catch(e){return e instanceof Error&&p.error(g.red(e.message)),null}}async function I(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.connectionString??`mongodb://localhost:27017/mydb`,condition:!0}];await F(r,i)}catch{p.error(`Failed to update environment configuration`)}}function L(){o.info(`
|
|
7
|
+
${g.green(`MongoDB Atlas Manual Setup Instructions:`)}
|
|
8
|
+
|
|
9
|
+
1. Install Atlas CLI:
|
|
10
|
+
${g.blue(`https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/`)}
|
|
11
|
+
|
|
12
|
+
2. Run the following command and follow the prompts:
|
|
13
|
+
${g.blue(`atlas deployments setup`)}
|
|
14
|
+
|
|
15
|
+
3. Get your connection string from the Atlas dashboard:
|
|
16
|
+
Format: ${g.dim(`mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME`)}
|
|
17
|
+
|
|
18
|
+
4. Add the connection string to your .env file:
|
|
19
|
+
${g.dim(`DATABASE_URL="your_connection_string"`)}
|
|
20
|
+
`)}async function ge(t){let{projectDir:n}=t,r=d();r.start(`Setting up MongoDB Atlas`);let i=e.join(n,`apps/server`);try{await h.ensureDir(i),r.stop(`Starting MongoDB Atlas setup`);let e=await he(i);e?(await I(n,e),o.success(g.green(`MongoDB Atlas setup complete! Connection saved to .env file.`))):(o.warn(g.yellow(`Falling back to local MongoDB configuration`)),await I(n),L())}catch(e){r.stop(g.red(`MongoDB Atlas setup failed`)),p.error(g.red(`Error during MongoDB Atlas setup: ${e instanceof Error?e.message:String(e)}`));try{await I(n),L()}catch{}}}async function _e(n,r){let i=d();try{i.start(`Initializing Prisma PostgreSQL`);let s=e.join(n,`prisma`);await h.ensureDir(s),i.stop(`Initializing Prisma. Follow the prompts below:`);let c=N(r,`prisma init --db`);await x(c,{cwd:n,stdio:`inherit`,shell:!0}),o.info(g.yellow(`Please copy the Prisma Postgres URL from the output above.
|
|
21
|
+
It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let u=await l({message:`Paste your Prisma Postgres database URL:`,validate(e){if(!e)return`Please enter a database URL`;if(!e.startsWith(`prisma+postgres://`))return`URL should start with prisma+postgres://`}});return a(u)?(t(`Database setup cancelled`),null):{databaseUrl:u}}catch(e){return i.stop(g.red(`Failed to initialize Prisma PostgreSQL`)),e instanceof Error&&m.error(e.message),null}}async function R(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.databaseUrl??`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`,condition:!0}];await F(r,i)}catch{m.error(`Failed to update environment configuration`)}}function z(){o.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
22
|
+
|
|
23
|
+
1. Visit https://console.prisma.io and create an account
|
|
24
|
+
2. Create a new PostgreSQL database from the dashboard
|
|
25
|
+
3. Get your database URL
|
|
26
|
+
4. Add the database URL to the .env file in apps/server/.env
|
|
27
|
+
|
|
28
|
+
DATABASE_URL="your_database_url"`)}async function ve(t){try{await M({dependencies:[`@prisma/extension-accelerate`],projectDir:t});let n=e.join(t,`prisma/index.ts`),r=`
|
|
29
|
+
import { PrismaClient } from "./generated/client";
|
|
30
|
+
import { withAccelerate } from "@prisma/extension-accelerate";
|
|
31
|
+
|
|
32
|
+
const prisma = new PrismaClient().$extends(withAccelerate());
|
|
33
|
+
|
|
34
|
+
export default prisma;
|
|
35
|
+
`;await h.writeFile(n,r.trim());let i=e.join(t,`src/db/index.ts`);if(await h.pathExists(i)){let e=await h.readFile(i,`utf8`);e.includes(`@prisma/extension-accelerate`)||(e=`import { withAccelerate } from "@prisma/extension-accelerate";\n${e}`,e=e.replace(`export const db = new PrismaClient();`,`export const db = new PrismaClient().$extends(withAccelerate());`),await h.writeFile(i,e))}return!0}catch{return o.warn(g.yellow(`Could not add Prisma Accelerate extension automatically`)),!1}}async function ye(t){let{packageManager:n,projectDir:r}=t,i=e.join(r,`apps/server`),a=d();a.start(`Setting up Prisma PostgreSQL`);try{await h.ensureDir(i),a.stop(`Starting Prisma setup`);let e=await _e(i,n);if(e)await R(r,e),await ve(i),o.success(g.green(`Prisma PostgreSQL database configured successfully!`)),o.info(g.cyan('NOTE: Make sure to uncomment `import "dotenv/config";` in `apps/server/src/prisma.config.ts` to load environment variables.'));else{let e=d();e.start(`Setting up fallback configuration`),await R(r),e.stop(`Manual setup required`),z()}}catch(e){a.stop(g.red(`Prisma PostgreSQL setup failed`)),m.error(g.red(`Error during Prisma PostgreSQL setup: ${e instanceof Error?e.message:String(e)}`));try{await R(r),z()}catch{}o.info(`Setup completed with manual configuration required.`)}}async function be(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=n||`postgresql://postgres:postgres@127.0.0.1:54322/postgres`,a=[{key:`DATABASE_URL`,value:i,condition:!0},{key:`DIRECT_URL`,value:i,condition:!0}];return await F(r,a),!0}catch(e){return m.error(g.red(`Failed to update .env file for Supabase.`)),e instanceof Error&&m.error(e.message),!1}}function xe(e){let t=e.match(/DB URL:\s*(postgresql:\/\/[^\s]+)/),n=t?.[1];return n||null}async function Se(e,t){o.info(`Initializing Supabase project...`);try{let n=N(t,`supabase init`);return await x(n,{cwd:e,stdio:`inherit`,shell:!0}),o.success(`Supabase project initialized successfully.`),!0}catch(e){return m.error(g.red(`Failed to initialize Supabase project.`)),e instanceof Error?m.error(e.message):m.error(String(e)),e instanceof Error&&e.message.includes(`ENOENT`)&&(o.error(g.red(`Supabase CLI not found. Please install it globally or ensure it's in your PATH.`)),o.info(`You can install it using: npm install -g supabase`)),!1}}async function Ce(e,t){o.info(`Starting Supabase services (this may take a moment)...`);let n=N(t,`supabase start`);try{let t=x(n,{cwd:e,shell:!0}),r=``;return t.stdout&&t.stdout.on(`data`,e=>{let t=e.toString();process.stdout.write(t),r+=t}),t.stderr&&t.stderr.pipe(process.stderr),await t,await new Promise(e=>setTimeout(e,100)),r}catch(e){m.error(g.red(`Failed to start Supabase services.`));let t=e;return t?.message?(m.error(`Error details: ${t.message}`),t.message.includes(`Docker is not running`)&&o.error(g.red(`Docker is not running. Please start Docker and try again.`))):m.error(String(e)),null}}function B(e){o.info(`"Manual Supabase Setup Instructions:"
|
|
36
|
+
1. Ensure Docker is installed and running.
|
|
37
|
+
2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
|
|
38
|
+
3. Run \`supabase init\` in your project's \`apps/server\` directory.
|
|
39
|
+
4. Run \`supabase start\` in your project's \`apps/server\` directory.
|
|
40
|
+
5. Copy the 'DB URL' from the output.${e?`
|
|
41
|
+
${g.bold("Relevant output from `supabase start`:")}
|
|
42
|
+
${g.dim(e)}`:``}
|
|
43
|
+
6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
|
|
44
|
+
${g.gray(`DATABASE_URL="your_supabase_db_url"`)}`)}async function we(t){let{projectDir:n,packageManager:r}=t,i=e.join(n,`apps`,`server`);try{await h.ensureDir(i);let e=await Se(i,r);if(!e){B();return}let t=await Ce(i,r);if(!t){B();return}let a=xe(t);if(a){let e=await be(n,a);e?o.success(g.green(`Supabase local development setup complete!`)):(o.error(g.red(`Supabase setup completed, but failed to update .env automatically.`)),B(t))}else o.error(g.yellow(`Supabase started, but could not extract DB URL automatically.`)),B(t)}catch(e){e instanceof Error?m.error(g.red(`Error during Supabase setup: ${e.message}`)):m.error(g.red(`An unknown error occurred during Supabase setup: ${String(e)}`)),B()}}async function Te(){return P(`turso`)}async function Ee(){try{let e=await b`turso auth whoami`;return!e.stdout.includes(`You are not logged in`)}catch{return!1}}async function De(){let e=d();try{return e.start(`Logging in to Turso...`),await b`turso auth login`,e.stop(`Logged in to Turso successfully!`),!0}catch{e.stop(g.red(`Failed to log in to Turso`))}}async function Oe(e){let t=d();try{if(t.start(`Installing Turso CLI...`),e)await b`brew install tursodatabase/tap/turso`;else{let{stdout:e}=await b`curl -sSfL https://get.tur.so/install.sh`;await b`bash -c '${e}'`}return t.stop(`Turso CLI installed successfully!`),!0}catch(e){if(e instanceof Error&&e.message.includes(`User force closed`))throw t.stop(`Turso CLI installation cancelled`),o.warn(g.yellow(`Turso CLI installation cancelled by user`)),Error(`Installation cancelled`);t.stop(g.red(`Failed to install Turso CLI`))}}async function ke(){let e=d();try{e.start(`Fetching Turso groups...`);let{stdout:t}=await b`turso group list`,n=t.trim().split(`
|
|
45
|
+
`);if(n.length<=1)return e.stop(`No Turso groups found`),[];let r=n.slice(1).map(e=>{let[t,n,r,i]=e.trim().split(/\s{2,}/);return{name:t,locations:n,version:r,status:i}});return e.stop(`Found ${r.length} Turso groups`),r}catch(t){return e.stop(g.red(`Error fetching Turso groups`)),console.error(`Error fetching Turso groups:`,t),[]}}async function Ae(){let e=await ke();if(e.length===0)return null;if(e.length===1)return o.info(`Using the only available group: ${g.blue(e[0].name)}`),e[0].name;let n=e.map(e=>({value:e.name,label:`${e.name} (${e.locations})`})),r=await u({message:`Select a Turso database group:`,options:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function je(e,t){let n=d();try{n.start(`Creating Turso database "${e}"${t?` in group "${t}"`:``}...`),t?await b`turso db create ${e} --group ${t}`:await b`turso db create ${e}`,n.stop(`Created database "${e}"`)}catch(t){if(n.stop(g.red(`Failed to create database "${e}"`)),t instanceof Error&&t.message.includes(`already exists`))throw Error(`DATABASE_EXISTS`)}n.start(`Retrieving database connection details...`);try{let{stdout:t}=await b`turso db show ${e} --url`,{stdout:r}=await b`turso db tokens create ${e}`;return n.stop(`Retrieved database connection details`),{dbUrl:t.trim(),authToken:r.trim()}}catch{n.stop(g.red(`Failed to retrieve database connection details`))}}async function V(t,n){let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.dbUrl??``,condition:!0},{key:`DATABASE_AUTH_TOKEN`,value:n?.authToken??``,condition:!0}];await F(r,i)}function H(){o.info(`Manual Turso Setup Instructions:
|
|
46
|
+
|
|
47
|
+
1. Visit https://turso.tech and create an account
|
|
48
|
+
2. Create a new database from the dashboard
|
|
49
|
+
3. Get your database URL and authentication token
|
|
50
|
+
4. Add these credentials to the .env file in apps/server/.env
|
|
51
|
+
|
|
52
|
+
DATABASE_URL=your_database_url
|
|
53
|
+
DATABASE_AUTH_TOKEN=your_auth_token`)}async function Me(r){let{orm:i,projectDir:s}=r,c=i===`drizzle`,l=d();l.start(`Setting up Turso database`);try{let r=S.platform(),i=r===`darwin`,c=r===`linux`,u=r===`win32`;if(u){l.stop(g.yellow(`Turso setup not supported on Windows`)),o.warn(g.yellow(`Automatic Turso setup is not supported on Windows.`)),await V(s),H();return}l.stop(`Checking Turso CLI`);let p=await Te();if(!p){let e=await n({message:`Would you like to install Turso CLI?`,initialValue:!0});if(a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),!e){await V(s),H();return}await Oe(i)}let m=await Ee();m||await De();let h=await Ae(),_=!1,v=``,y=e.basename(s);for(;!_;){let e=await f({message:`Enter a name for your database:`,defaultValue:y,initialValue:y,placeholder:y});a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),v=e;try{let e=await je(v,h),t=d();t.start(`Writing configuration to .env file`),await V(s,e),t.stop(`Turso database configured successfully!`),_=!0}catch(e){e instanceof Error&&e.message===`DATABASE_EXISTS`&&(o.warn(g.yellow(`Database "${g.red(v)}" already exists`)),y=`${v}-${Math.floor(Math.random()*1e3)}`)}}}catch(e){l.stop(g.red(`Failed to set up Turso database`)),p.error(g.red(`Error during Turso setup: ${e instanceof Error?e.message:String(e)}`)),await V(s),H(),o.success(`Setup completed with manual configuration required.`)}}const U=[{label:`AWS US East (N. Virginia)`,value:`aws-us-east-1`},{label:`AWS US East (Ohio)`,value:`aws-us-east-2`},{label:`AWS US West (Oregon)`,value:`aws-us-west-2`},{label:`AWS Europe (Frankfurt)`,value:`aws-eu-central-1`},{label:`AWS Asia Pacific (Singapore)`,value:`aws-ap-southeast-1`},{label:`AWS Asia Pacific (Sydney)`,value:`aws-ap-southeast-2`},{label:`Azure East US 2 region (Virginia)`,value:`azure-eastus2`}];async function W(e,t,n){let r=d();try{let i=N(e,t);n&&r.start(n);let a=await x(i,{shell:!0});return n&&r.stop(g.green(`Completed`)),a}catch(e){throw r&&r.stop(g.red(`Failed: ${n}`)),e}}async function Ne(e){try{let t=`neonctl projects list`,n=await W(e,t);return!n.stdout.includes(`not authenticated`)&&!n.stdout.includes(`error`)}catch{return!1}}async function Pe(e){try{return await W(e,`neonctl auth`,`Authenticating with Neon...`),o.success(`Authenticated with Neon successfully!`),!0}catch{m.error(g.red(`Failed to authenticate with Neon`))}}async function Fe(e,t,n){try{let r=`neonctl projects create --name ${e} --region-id ${t} --output json`,{stdout:i}=await W(n,r,`Creating Neon project "${e}"...`),a=JSON.parse(i);if(a.project&&a.connection_uris&&a.connection_uris.length>0){let e=a.project.id,t=a.connection_uris[0].connection_uri,n=a.connection_uris[0].connection_parameters;return{connectionString:t,projectId:e,dbName:n.database,roleName:n.role}}return m.error(g.red(`Failed to extract connection information from response`)),null}catch{m.error(g.red(`Failed to create Neon project`))}}async function G(t,n){let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.connectionString??`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`,condition:!0}];return await F(r,i),!0}function Ie(){o.info(`Manual Neon PostgreSQL Setup Instructions:
|
|
54
|
+
|
|
55
|
+
1. Visit https://neon.tech and create an account
|
|
56
|
+
2. Create a new project from the dashboard
|
|
57
|
+
3. Get your connection string
|
|
58
|
+
4. Add the database URL to the .env file in apps/server/.env
|
|
59
|
+
|
|
60
|
+
DATABASE_URL="your_connection_string"`)}async function Le(n){let{packageManager:r,projectDir:i}=n,s=d();s.start(`Setting up Neon PostgreSQL`);try{let n=await Ne(r);s.stop(`Setting up Neon PostgreSQL`),n||(o.info(`Please authenticate with Neon to continue:`),await Pe(r));let c=e.basename(i),l=await f({message:`Enter a name for your Neon project:`,defaultValue:c,initialValue:c}),p=await u({message:`Select a region for your Neon project:`,options:U,initialValue:U[0].value});(a(l)||a(p))&&(t(g.red(`Operation cancelled`)),process.exit(0));let m=await Fe(l,p,r);if(!m)throw Error(`Failed to create project - couldn't get connection information`);let _=d();_.start(`Configuring database connection`),await h.ensureDir(e.join(i,`apps/server`)),await G(i,m),_.stop(`Neon database configured successfully!`)}catch(e){s.stop(g.red(`Neon PostgreSQL setup failed`)),e instanceof Error&&m.error(g.red(e.message)),await G(i),Ie()}}async function Re(t){let{database:n,orm:r,dbSetup:i,backend:a,projectDir:o}=t;if(a===`convex`||n===`none`){if(a!==`convex`){let t=e.join(o,`apps/server`),n=e.join(t,`src/db`);await h.pathExists(n)&&await h.remove(n)}return}let s=d(),c=e.join(o,`apps/server`);if(await h.pathExists(c))try{r===`prisma`?await M({dependencies:[`@prisma/client`],devDependencies:[`prisma`],projectDir:c}):r===`drizzle`?n===`sqlite`?await M({dependencies:[`drizzle-orm`,`@libsql/client`],devDependencies:[`drizzle-kit`],projectDir:c}):n===`postgres`?await M({dependencies:[`drizzle-orm`,`pg`],devDependencies:[`drizzle-kit`,`@types/pg`],projectDir:c}):n===`mysql`&&await M({dependencies:[`drizzle-orm`,`mysql2`],devDependencies:[`drizzle-kit`],projectDir:c}):r===`mongoose`&&await M({dependencies:[`mongoose`],devDependencies:[],projectDir:c}),n===`sqlite`&&i===`turso`?await Me(t):n===`postgres`?r===`prisma`&&i===`prisma-postgres`?await ye(t):i===`neon`?await Le(t):i===`supabase`&&await we(t):n===`mongodb`&&i===`mongodb-atlas`&&await ge(t)}catch(e){s.stop(g.red(`Failed to set up database`)),e instanceof Error&&p.error(g.red(e.message))}}async function ze(t){let{examples:n,frontend:r,backend:i,projectDir:a}=t;if(!(i===`convex`||!n||n.length===0||n[0]===`none`)&&n.includes(`ai`)){let t=e.join(a,`apps/web`),n=e.join(a,`apps/server`),o=await h.pathExists(t),s=await h.pathExists(n),c=r.includes(`nuxt`),l=r.includes(`svelte`),u=r.includes(`react-router`)||r.includes(`tanstack-router`)||r.includes(`next`)||r.includes(`tanstack-start`);if(o){let e=[`ai`];c?e.push(`@ai-sdk/vue`):l?e.push(`@ai-sdk/svelte`):u&&e.push(`@ai-sdk/react`),await M({dependencies:e,projectDir:t})}s&&i!==`none`&&await M({dependencies:[`ai`,`@ai-sdk/google`],projectDir:n})}}async function Be(t){let{runtime:n,backend:r,projectDir:i}=t;if(r===`convex`||r===`next`||n===`none`)return;let a=e.join(i,`apps/server`);await h.pathExists(a)&&(n===`bun`?await Ve(a,r):n===`node`&&await He(a,r))}async function Ve(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`bun run --hot src/index.ts`,start:`bun run dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`@types/bun`],projectDir:t})}async function He(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`tsx watch src/index.ts`,start:`node dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`tsx`,`@types/node`],projectDir:t}),n===`hono`?await M({dependencies:[`@hono/node-server`],projectDir:t}):n===`elysia`&&await M({dependencies:[`@elysiajs/node`],projectDir:t})}async function Ue(t,n){let r=e.join(t,`README.md`),i=We(n);try{await h.writeFile(r,i)}catch(e){p.error(`Failed to create README.md file:`,e)}}function We(e){let{projectName:t,packageManager:n,database:r,auth:i,addons:a=[],orm:o=`drizzle`,runtime:s=`bun`,frontend:c=[`tanstack-router`],backend:l=`hono`,api:u=`trpc`}=e,d=l===`convex`,f=c.includes(`react-router`),p=c.includes(`tanstack-router`),m=c.includes(`native-nativewind`)||c.includes(`native-unistyles`),h=c.includes(`next`),g=c.includes(`tanstack-start`),_=c.includes(`svelte`),v=c.includes(`solid`),y=c.includes(`nuxt`),b=n===`npm`?`npm run`:n,x=`3001`;return(f||_)&&(x=`5173`),`# ${t}
|
|
3
61
|
|
|
4
62
|
This project was created with [Better-T-Stack](https://github.com/AmanVarshney01/create-better-t-stack), a modern TypeScript stack that combines ${p?`React, TanStack Router`:f?`React, React Router`:h?`Next.js`:g?`React, TanStack Start`:_?`SvelteKit`:y?`Nuxt`:v?`SolidJS`:``}, ${l[0].toUpperCase()+l.slice(1)}${d?``:`, ${u.toUpperCase()}`}, and more.
|
|
5
63
|
|
|
6
64
|
## Features
|
|
7
65
|
|
|
8
|
-
${
|
|
66
|
+
${Ge(r,i,a,o,s,c,l,u)}
|
|
9
67
|
|
|
10
68
|
## Getting Started
|
|
11
69
|
|
|
@@ -23,7 +81,7 @@ This project uses Convex as a backend. You'll need to set up Convex before runni
|
|
|
23
81
|
${b} dev:setup
|
|
24
82
|
\`\`\`
|
|
25
83
|
|
|
26
|
-
Follow the prompts to create a new Convex project and connect it to your application.`:
|
|
84
|
+
Follow the prompts to create a new Convex project and connect it to your application.`:Ke(r,i,b,o)}
|
|
27
85
|
|
|
28
86
|
Then, run the development server:
|
|
29
87
|
|
|
@@ -57,9 +115,9 @@ ${p||f||h||g||_||y||v?`│ ├── web/ # Frontend application (${p?
|
|
|
57
115
|
|
|
58
116
|
## Available Scripts
|
|
59
117
|
|
|
60
|
-
${
|
|
61
|
-
`}function
|
|
62
|
-
`)}function
|
|
118
|
+
${qe(b,r,o,i,m,a,l)}
|
|
119
|
+
`}function Ge(e,t,n,r,i,a,o,s){let c=o===`convex`,l=a.includes(`tanstack-router`),u=a.includes(`react-router`),d=a.includes(`native-nativewind`)||a.includes(`native-unistyles`),f=a.includes(`next`),p=a.includes(`tanstack-start`),m=a.includes(`svelte`),h=a.includes(`nuxt`),g=a.includes(`solid`),_=[`- **TypeScript** - For type safety and improved developer experience`];l?_.push(`- **TanStack Router** - File-based routing with full type safety`):u?_.push(`- **React Router** - Declarative routing for React`):f?_.push(`- **Next.js** - Full-stack React framework`):p?_.push(`- **TanStack Start** - SSR framework with TanStack Router`):m?_.push(`- **SvelteKit** - Web framework for building Svelte apps`):h?_.push(`- **Nuxt** - The Intuitive Vue Framework`):g&&_.push(`- **SolidJS** - Simple and performant reactivity`),d&&(_.push(`- **React Native** - Build mobile apps using React`),_.push(`- **Expo** - Tools for React Native development`)),_.push(`- **TailwindCSS** - Utility-first CSS for rapid UI development`,`- **shadcn/ui** - Reusable UI components`),c?_.push(`- **Convex** - Reactive backend-as-a-service platform`):(o===`hono`?_.push(`- **Hono** - Lightweight, performant server framework`):o===`express`?_.push(`- **Express** - Fast, unopinionated web framework`):o===`fastify`?_.push(`- **Fastify** - Fast, low-overhead web framework`):o===`elysia`?_.push(`- **Elysia** - Type-safe, high-performance framework`):o===`next`&&_.push(`- **Next.js** - Full-stack React framework`),s===`trpc`?_.push(`- **tRPC** - End-to-end type-safe APIs`):s===`orpc`&&_.push(`- **oRPC** - End-to-end type-safe APIs with OpenAPI integration`),_.push(`- **${i===`bun`?`Bun`:`Node.js`}** - Runtime environment`)),e!==`none`&&!c&&_.push(`- **${r===`drizzle`?`Drizzle`:r===`prisma`?`Prisma`:`Mongoose`}** - TypeScript-first ORM`,`- **${e===`sqlite`?`SQLite/Turso`:e===`postgres`?`PostgreSQL`:e===`mysql`?`MySQL`:`MongoDB`}** - Database engine`),t&&!c&&_.push(`- **Authentication** - Email & password authentication with Better Auth`);for(let e of n)e===`pwa`?_.push(`- **PWA** - Progressive Web App support`):e===`tauri`?_.push(`- **Tauri** - Build native desktop applications`):e===`biome`?_.push(`- **Biome** - Linting and formatting`):e===`husky`?_.push(`- **Husky** - Git hooks for code quality`):e===`starlight`?_.push(`- **Starlight** - Documentation site with Astro`):e===`turborepo`&&_.push(`- **Turborepo** - Optimized monorepo build system`);return _.join(`
|
|
120
|
+
`)}function Ke(e,t,n,r){if(e===`none`)return``;let i=`## Database Setup
|
|
63
121
|
|
|
64
122
|
`;return e===`sqlite`?i+=`This project uses SQLite${r===`drizzle`?` with Drizzle ORM`:` with Prisma`}.
|
|
65
123
|
|
|
@@ -89,7 +147,7 @@ ${n} db:push
|
|
|
89
147
|
\`\`\`bash
|
|
90
148
|
${n} db:push
|
|
91
149
|
\`\`\``}
|
|
92
|
-
`,i}function
|
|
150
|
+
`,i}function qe(e,t,n,r,i,a,o){let s=o===`convex`,c=`- \`${e} dev\`: Start all applications in development mode
|
|
93
151
|
- \`${e} build\`: Build all applications
|
|
94
152
|
- \`${e} dev:web\`: Start only the web application`;return s?c+=`
|
|
95
153
|
- \`${e} dev:setup\`: Setup and configure your Convex project`:c+=`
|
|
@@ -104,68 +162,10 @@ ${n} db:push
|
|
|
104
162
|
- \`cd apps/web && ${e} desktop:dev\`: Start Tauri desktop app in development
|
|
105
163
|
- \`cd apps/web && ${e} desktop:build\`: Build Tauri desktop app`),a.includes(`starlight`)&&(c+=`
|
|
106
164
|
- \`cd apps/docs && ${e} dev\`: Start documentation site
|
|
107
|
-
- \`cd apps/docs && ${e} build\`: Build documentation site`),c}async function
|
|
108
|
-
`)&&(
|
|
109
|
-
`),r+=a),i&&await h.writeFile(t,r.trimEnd());let s=t.replace(/\.env$/,`.env.example`),c=``;await h.pathExists(s)&&(c=await h.readFile(s,`utf8`));let l=!1,u=``;for(let e of o){let t=e.split(`=`)[0],n=RegExp(`^${t}=.*$`,`m`);n.test(c)||(u+=`${e}\n`,l=!0)}u&&(c.length>0&&!c.endsWith(`
|
|
110
|
-
`)&&(
|
|
111
|
-
`),c+=u),(l||!await h.pathExists(s))&&await h.writeFile(s,c.trimEnd())}async function ge(t){let{backend:n,frontend:r,database:i,auth:a,examples:o,dbSetup:s,projectDir:c}=t,l=r.includes(`react-router`),u=r.includes(`tanstack-router`),d=r.includes(`tanstack-start`),f=r.includes(`next`),p=r.includes(`nuxt`),m=r.includes(`svelte`),g=r.includes(`solid`),_=l||u||d||f||p||g||m;if(_){let t=e.join(c,`apps/web`);if(await h.pathExists(t)){let r=`VITE_SERVER_URL`,i=`http://localhost:3000`;f?r=`NEXT_PUBLIC_SERVER_URL`:p?r=`NUXT_PUBLIC_SERVER_URL`:m&&(r=`PUBLIC_SERVER_URL`),n===`convex`&&(r=f?`NEXT_PUBLIC_CONVEX_URL`:p?`NUXT_PUBLIC_CONVEX_URL`:m?`PUBLIC_CONVEX_URL`:`VITE_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:r,value:i,condition:!0}];await F(e.join(t,`.env`),a)}}if(r.includes(`native-nativewind`)||r.includes(`native-unistyles`)){let t=e.join(c,`apps/native`);if(await h.pathExists(t)){let r=`EXPO_PUBLIC_SERVER_URL`,i=`http://localhost:3000`;n===`convex`&&(r=`EXPO_PUBLIC_CONVEX_URL`,i=`https://<YOUR_CONVEX_URL>`);let a=[{key:r,value:i,condition:!0}];await F(e.join(t,`.env`),a)}}if(n===`convex`)return;let v=e.join(c,`apps/server`);if(!await h.pathExists(v))return;let y=e.join(v,`.env`),b=`http://localhost:3001`;(l||m)&&(b=`http://localhost:5173`);let x=null,S=s===`turso`||s===`prisma-postgres`||s===`mongodb-atlas`||s===`neon`||s===`supabase`;if(i!==`none`&&!S)switch(i){case`postgres`:x=`postgresql://postgres:password@localhost:5432/postgres`;break;case`mysql`:x=`mysql://root:password@localhost:3306/mydb`;break;case`mongodb`:x=`mongodb://localhost:27017/mydatabase`;break;case`sqlite`:x=`file:./local.db`;break}let C=[{key:`CORS_ORIGIN`,value:b,condition:!0},{key:`BETTER_AUTH_SECRET`,value:le(),condition:!!a},{key:`BETTER_AUTH_URL`,value:`http://localhost:3000`,condition:!!a},{key:`DATABASE_URL`,value:x,condition:i!==`none`&&!S},{key:`GOOGLE_GENERATIVE_AI_API_KEY`,value:``,condition:o?.includes(`ai`)||!1}];await F(y,C)}async function _e(){let e=d();e.start(`Checking for MongoDB Atlas CLI`);try{let t=await P(`atlas`);return e.stop(t?`MongoDB Atlas CLI found`:g.yellow(`MongoDB Atlas CLI not found`)),t}catch{return e.stop(g.red(`Error checking for MongoDB Atlas CLI`)),!1}}async function ve(e){try{let n=await _e();if(!n)return p.error(g.red(`MongoDB Atlas CLI not found.`)),o.info(g.yellow(`Please install it from: https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/`)),null;o.info(g.blue(`Running MongoDB Atlas setup...`)),await x(`atlas`,[`deployments`,`setup`],{cwd:e,stdio:`inherit`}),o.info(g.green(`Atlas setup complete!`));let r=await f({message:`Enter your MongoDB connection string:`,placeholder:`mongodb+srv://username:password@cluster.mongodb.net/database`,validate(e){if(!e)return`Please enter a connection string`;if(!e.startsWith(`mongodb`))return`URL should start with mongodb:// or mongodb+srv://`}});return a(r)?(t(`MongoDB setup cancelled`),null):{connectionString:r}}catch(e){return e instanceof Error&&p.error(g.red(e.message)),null}}async function I(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.connectionString??`mongodb://localhost:27017/mydb`,condition:!0}];await F(r,i)}catch{p.error(`Failed to update environment configuration`)}}function L(){o.info(`
|
|
112
|
-
${g.green(`MongoDB Atlas Manual Setup Instructions:`)}
|
|
113
|
-
|
|
114
|
-
1. Install Atlas CLI:
|
|
115
|
-
${g.blue(`https://www.mongodb.com/docs/atlas/cli/stable/install-atlas-cli/`)}
|
|
116
|
-
|
|
117
|
-
2. Run the following command and follow the prompts:
|
|
118
|
-
${g.blue(`atlas deployments setup`)}
|
|
119
|
-
|
|
120
|
-
3. Get your connection string from the Atlas dashboard:
|
|
121
|
-
Format: ${g.dim(`mongodb+srv://USERNAME:PASSWORD@CLUSTER.mongodb.net/DATABASE_NAME`)}
|
|
122
|
-
|
|
123
|
-
4. Add the connection string to your .env file:
|
|
124
|
-
${g.dim(`DATABASE_URL="your_connection_string"`)}
|
|
125
|
-
`)}async function ye(t){let{projectDir:n}=t,r=d();r.start(`Setting up MongoDB Atlas`);let i=e.join(n,`apps/server`);try{await h.ensureDir(i),r.stop(`Starting MongoDB Atlas setup`);let e=await ve(i);e?(await I(n,e),o.success(g.green(`MongoDB Atlas setup complete! Connection saved to .env file.`))):(o.warn(g.yellow(`Falling back to local MongoDB configuration`)),await I(n),L())}catch(e){r.stop(g.red(`MongoDB Atlas setup failed`)),p.error(g.red(`Error during MongoDB Atlas setup: ${e instanceof Error?e.message:String(e)}`));try{await I(n),L()}catch{}}}async function be(n,r){let i=d();try{i.start(`Initializing Prisma PostgreSQL`);let s=e.join(n,`prisma`);await h.ensureDir(s),i.stop(`Initializing Prisma. Follow the prompts below:`);let c=N(r,`prisma init --db`);await x(c,{cwd:n,stdio:`inherit`,shell:!0}),o.info(g.yellow(`Please copy the Prisma Postgres URL from the output above.
|
|
126
|
-
It looks like: prisma+postgres://accelerate.prisma-data.net/?api_key=...`));let u=await l({message:`Paste your Prisma Postgres database URL:`,validate(e){if(!e)return`Please enter a database URL`;if(!e.startsWith(`prisma+postgres://`))return`URL should start with prisma+postgres://`}});return a(u)?(t(`Database setup cancelled`),null):{databaseUrl:u}}catch(e){return i.stop(g.red(`Failed to initialize Prisma PostgreSQL`)),e instanceof Error&&m.error(e.message),null}}async function R(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.databaseUrl??`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`,condition:!0}];await F(r,i)}catch{m.error(`Failed to update environment configuration`)}}function z(){o.info(`Manual Prisma PostgreSQL Setup Instructions:
|
|
127
|
-
|
|
128
|
-
1. Visit https://console.prisma.io and create an account
|
|
129
|
-
2. Create a new PostgreSQL database from the dashboard
|
|
130
|
-
3. Get your database URL
|
|
131
|
-
4. Add the database URL to the .env file in apps/server/.env
|
|
132
|
-
|
|
133
|
-
DATABASE_URL="your_database_url"`)}async function xe(t){try{await M({dependencies:[`@prisma/extension-accelerate`],projectDir:t});let n=e.join(t,`prisma/index.ts`),r=`
|
|
134
|
-
import { PrismaClient } from "./generated/client";
|
|
135
|
-
import { withAccelerate } from "@prisma/extension-accelerate";
|
|
136
|
-
|
|
137
|
-
const prisma = new PrismaClient().$extends(withAccelerate());
|
|
138
|
-
|
|
139
|
-
export default prisma;
|
|
140
|
-
`;await h.writeFile(n,r.trim());let i=e.join(t,`src/db/index.ts`);if(await h.pathExists(i)){let e=await h.readFile(i,`utf8`);e.includes(`@prisma/extension-accelerate`)||(e=`import { withAccelerate } from "@prisma/extension-accelerate";\n${e}`,e=e.replace(`export const db = new PrismaClient();`,`export const db = new PrismaClient().$extends(withAccelerate());`),await h.writeFile(i,e))}return!0}catch{return o.warn(g.yellow(`Could not add Prisma Accelerate extension automatically`)),!1}}async function Se(t){let{packageManager:n,projectDir:r}=t,i=e.join(r,`apps/server`),a=d();a.start(`Setting up Prisma PostgreSQL`);try{await h.ensureDir(i),a.stop(`Starting Prisma setup`);let e=await be(i,n);if(e)await R(r,e),await xe(i),o.success(g.green(`Prisma PostgreSQL database configured successfully!`)),o.info(g.cyan('NOTE: Make sure to uncomment `import "dotenv/config";` in `apps/server/src/prisma.config.ts` to load environment variables.'));else{let e=d();e.start(`Setting up fallback configuration`),await R(r),e.stop(`Manual setup required`),z()}}catch(e){a.stop(g.red(`Prisma PostgreSQL setup failed`)),m.error(g.red(`Error during Prisma PostgreSQL setup: ${e instanceof Error?e.message:String(e)}`));try{await R(r),z()}catch{}o.info(`Setup completed with manual configuration required.`)}}async function Ce(t,n){try{let r=e.join(t,`apps/server`,`.env`),i=n||`postgresql://postgres:postgres@127.0.0.1:54322/postgres`,a=[{key:`DATABASE_URL`,value:i,condition:!0},{key:`DIRECT_URL`,value:i,condition:!0}];return await F(r,a),!0}catch(e){return m.error(g.red(`Failed to update .env file for Supabase.`)),e instanceof Error&&m.error(e.message),!1}}function we(e){let t=e.match(/DB URL:\s*(postgresql:\/\/[^\s]+)/),n=t?.[1];return n||null}async function Te(e,t){o.info(`Initializing Supabase project...`);try{let n=N(t,`supabase init`);return await x(n,{cwd:e,stdio:`inherit`,shell:!0}),o.success(`Supabase project initialized successfully.`),!0}catch(e){return m.error(g.red(`Failed to initialize Supabase project.`)),e instanceof Error?m.error(e.message):m.error(String(e)),e instanceof Error&&e.message.includes(`ENOENT`)&&(o.error(g.red(`Supabase CLI not found. Please install it globally or ensure it's in your PATH.`)),o.info(`You can install it using: npm install -g supabase`)),!1}}async function Ee(e,t){o.info(`Starting Supabase services (this may take a moment)...`);let n=N(t,`supabase start`);try{let t=x(n,{cwd:e,shell:!0}),r=``;return t.stdout&&t.stdout.on(`data`,e=>{let t=e.toString();process.stdout.write(t),r+=t}),t.stderr&&t.stderr.pipe(process.stderr),await t,await new Promise(e=>setTimeout(e,100)),r}catch(e){m.error(g.red(`Failed to start Supabase services.`));let t=e;return t?.message?(m.error(`Error details: ${t.message}`),t.message.includes(`Docker is not running`)&&o.error(g.red(`Docker is not running. Please start Docker and try again.`))):m.error(String(e)),null}}function B(e){o.info(`"Manual Supabase Setup Instructions:"
|
|
141
|
-
1. Ensure Docker is installed and running.
|
|
142
|
-
2. Install the Supabase CLI (e.g., \`npm install -g supabase\`).
|
|
143
|
-
3. Run \`supabase init\` in your project's \`apps/server\` directory.
|
|
144
|
-
4. Run \`supabase start\` in your project's \`apps/server\` directory.
|
|
145
|
-
5. Copy the 'DB URL' from the output.${e?`
|
|
146
|
-
${g.bold("Relevant output from `supabase start`:")}
|
|
147
|
-
${g.dim(e)}`:``}
|
|
148
|
-
6. Add the DB URL to the .env file in \`apps/server/.env\` as \`DATABASE_URL\`:
|
|
149
|
-
${g.gray(`DATABASE_URL="your_supabase_db_url"`)}`)}async function De(t){let{projectDir:n,packageManager:r}=t,i=e.join(n,`apps`,`server`);try{await h.ensureDir(i);let e=await Te(i,r);if(!e){B();return}let t=await Ee(i,r);if(!t){B();return}let a=we(t);if(a){let e=await Ce(n,a);e?o.success(g.green(`Supabase local development setup complete!`)):(o.error(g.red(`Supabase setup completed, but failed to update .env automatically.`)),B(t))}else o.error(g.yellow(`Supabase started, but could not extract DB URL automatically.`)),B(t)}catch(e){e instanceof Error?m.error(g.red(`Error during Supabase setup: ${e.message}`)):m.error(g.red(`An unknown error occurred during Supabase setup: ${String(e)}`)),B()}}async function Oe(){return P(`turso`)}async function ke(){try{let e=await b`turso auth whoami`;return!e.stdout.includes(`You are not logged in`)}catch{return!1}}async function Ae(){let e=d();try{return e.start(`Logging in to Turso...`),await b`turso auth login`,e.stop(`Logged in to Turso successfully!`),!0}catch{e.stop(g.red(`Failed to log in to Turso`))}}async function je(e){let t=d();try{if(t.start(`Installing Turso CLI...`),e)await b`brew install tursodatabase/tap/turso`;else{let{stdout:e}=await b`curl -sSfL https://get.tur.so/install.sh`;await b`bash -c '${e}'`}return t.stop(`Turso CLI installed successfully!`),!0}catch(e){if(e instanceof Error&&e.message.includes(`User force closed`))throw t.stop(`Turso CLI installation cancelled`),o.warn(g.yellow(`Turso CLI installation cancelled by user`)),Error(`Installation cancelled`);t.stop(g.red(`Failed to install Turso CLI`))}}async function Me(){let e=d();try{e.start(`Fetching Turso groups...`);let{stdout:t}=await b`turso group list`,n=t.trim().split(`
|
|
150
|
-
`);if(n.length<=1)return e.stop(`No Turso groups found`),[];let r=n.slice(1).map(e=>{let[t,n,r,i]=e.trim().split(/\s{2,}/);return{name:t,locations:n,version:r,status:i}});return e.stop(`Found ${r.length} Turso groups`),r}catch(t){return e.stop(g.red(`Error fetching Turso groups`)),console.error(`Error fetching Turso groups:`,t),[]}}async function Ne(){let e=await Me();if(e.length===0)return null;if(e.length===1)return o.info(`Using the only available group: ${g.blue(e[0].name)}`),e[0].name;let n=e.map(e=>({value:e.name,label:`${e.name} (${e.locations})`})),r=await u({message:`Select a Turso database group:`,options:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Pe(e,t){let n=d();try{n.start(`Creating Turso database "${e}"${t?` in group "${t}"`:``}...`),t?await b`turso db create ${e} --group ${t}`:await b`turso db create ${e}`,n.stop(`Created database "${e}"`)}catch(t){if(n.stop(g.red(`Failed to create database "${e}"`)),t instanceof Error&&t.message.includes(`already exists`))throw Error(`DATABASE_EXISTS`)}n.start(`Retrieving database connection details...`);try{let{stdout:t}=await b`turso db show ${e} --url`,{stdout:r}=await b`turso db tokens create ${e}`;return n.stop(`Retrieved database connection details`),{dbUrl:t.trim(),authToken:r.trim()}}catch{n.stop(g.red(`Failed to retrieve database connection details`))}}async function V(t,n){let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.dbUrl??``,condition:!0},{key:`DATABASE_AUTH_TOKEN`,value:n?.authToken??``,condition:!0}];await F(r,i)}function H(){o.info(`Manual Turso Setup Instructions:
|
|
151
|
-
|
|
152
|
-
1. Visit https://turso.tech and create an account
|
|
153
|
-
2. Create a new database from the dashboard
|
|
154
|
-
3. Get your database URL and authentication token
|
|
155
|
-
4. Add these credentials to the .env file in apps/server/.env
|
|
156
|
-
|
|
157
|
-
DATABASE_URL=your_database_url
|
|
158
|
-
DATABASE_AUTH_TOKEN=your_auth_token`)}async function Fe(r){let{orm:i,projectDir:s}=r,c=i===`drizzle`,l=d();l.start(`Setting up Turso database`);try{let r=S.platform(),i=r===`darwin`,c=r===`linux`,u=r===`win32`;if(u){l.stop(g.yellow(`Turso setup not supported on Windows`)),o.warn(g.yellow(`Automatic Turso setup is not supported on Windows.`)),await V(s),H();return}l.stop(`Checking Turso CLI`);let p=await Oe();if(!p){let e=await n({message:`Would you like to install Turso CLI?`,initialValue:!0});if(a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),!e){await V(s),H();return}await je(i)}let m=await ke();m||await Ae();let h=await Ne(),_=!1,v=``,y=e.basename(s);for(;!_;){let e=await f({message:`Enter a name for your database:`,defaultValue:y,initialValue:y,placeholder:y});a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),v=e;try{let e=await Pe(v,h),t=d();t.start(`Writing configuration to .env file`),await V(s,e),t.stop(`Turso database configured successfully!`),_=!0}catch(e){e instanceof Error&&e.message===`DATABASE_EXISTS`&&(o.warn(g.yellow(`Database "${g.red(v)}" already exists`)),y=`${v}-${Math.floor(Math.random()*1e3)}`)}}}catch(e){l.stop(g.red(`Failed to set up Turso database`)),p.error(g.red(`Error during Turso setup: ${e instanceof Error?e.message:String(e)}`)),await V(s),H(),o.success(`Setup completed with manual configuration required.`)}}const U=[{label:`AWS US East (N. Virginia)`,value:`aws-us-east-1`},{label:`AWS US East (Ohio)`,value:`aws-us-east-2`},{label:`AWS US West (Oregon)`,value:`aws-us-west-2`},{label:`AWS Europe (Frankfurt)`,value:`aws-eu-central-1`},{label:`AWS Asia Pacific (Singapore)`,value:`aws-ap-southeast-1`},{label:`AWS Asia Pacific (Sydney)`,value:`aws-ap-southeast-2`},{label:`Azure East US 2 region (Virginia)`,value:`azure-eastus2`}];async function W(e,t,n){let r=d();try{let i=N(e,t);n&&r.start(n);let a=await x(i,{shell:!0});return n&&r.stop(g.green(`Completed`)),a}catch(e){throw r&&r.stop(g.red(`Failed: ${n}`)),e}}async function Ie(e){try{let t=`neonctl projects list`,n=await W(e,t);return!n.stdout.includes(`not authenticated`)&&!n.stdout.includes(`error`)}catch{return!1}}async function Le(e){try{return await W(e,`neonctl auth`,`Authenticating with Neon...`),o.success(`Authenticated with Neon successfully!`),!0}catch{m.error(g.red(`Failed to authenticate with Neon`))}}async function Re(e,t,n){try{let r=`neonctl projects create --name ${e} --region-id ${t} --output json`,{stdout:i}=await W(n,r,`Creating Neon project "${e}"...`),a=JSON.parse(i);if(a.project&&a.connection_uris&&a.connection_uris.length>0){let e=a.project.id,t=a.connection_uris[0].connection_uri,n=a.connection_uris[0].connection_parameters;return{connectionString:t,projectId:e,dbName:n.database,roleName:n.role}}return m.error(g.red(`Failed to extract connection information from response`)),null}catch{m.error(g.red(`Failed to create Neon project`))}}async function G(t,n){let r=e.join(t,`apps/server`,`.env`),i=[{key:`DATABASE_URL`,value:n?.connectionString??`postgresql://postgres:postgres@localhost:5432/mydb?schema=public`,condition:!0}];return await F(r,i),!0}function ze(){o.info(`Manual Neon PostgreSQL Setup Instructions:
|
|
159
|
-
|
|
160
|
-
1. Visit https://neon.tech and create an account
|
|
161
|
-
2. Create a new project from the dashboard
|
|
162
|
-
3. Get your connection string
|
|
163
|
-
4. Add the database URL to the .env file in apps/server/.env
|
|
164
|
-
|
|
165
|
-
DATABASE_URL="your_connection_string"`)}async function Be(n){let{packageManager:r,projectDir:i}=n,s=d();s.start(`Setting up Neon PostgreSQL`);try{let n=await Ie(r);s.stop(`Setting up Neon PostgreSQL`),n||(o.info(`Please authenticate with Neon to continue:`),await Le(r));let c=e.basename(i),l=await f({message:`Enter a name for your Neon project:`,defaultValue:c,initialValue:c}),p=await u({message:`Select a region for your Neon project:`,options:U,initialValue:U[0].value});(a(l)||a(p))&&(t(g.red(`Operation cancelled`)),process.exit(0));let m=await Re(l,p,r);if(!m)throw Error(`Failed to create project - couldn't get connection information`);let _=d();_.start(`Configuring database connection`),await h.ensureDir(e.join(i,`apps/server`)),await G(i,m),_.stop(`Neon database configured successfully!`)}catch(e){s.stop(g.red(`Neon PostgreSQL setup failed`)),e instanceof Error&&m.error(g.red(e.message)),await G(i),ze()}}async function Ve(t){let{database:n,orm:r,dbSetup:i,backend:a,projectDir:o}=t;if(a===`convex`||n===`none`){if(a!==`convex`){let t=e.join(o,`apps/server`),n=e.join(t,`src/db`);await h.pathExists(n)&&await h.remove(n)}return}let s=d(),c=e.join(o,`apps/server`);if(await h.pathExists(c))try{r===`prisma`?await M({dependencies:[`@prisma/client`],devDependencies:[`prisma`],projectDir:c}):r===`drizzle`?n===`sqlite`?await M({dependencies:[`drizzle-orm`,`@libsql/client`],devDependencies:[`drizzle-kit`],projectDir:c}):n===`postgres`?await M({dependencies:[`drizzle-orm`,`pg`],devDependencies:[`drizzle-kit`,`@types/pg`],projectDir:c}):n===`mysql`&&await M({dependencies:[`drizzle-orm`,`mysql2`],devDependencies:[`drizzle-kit`],projectDir:c}):r===`mongoose`&&await M({dependencies:[`mongoose`],devDependencies:[],projectDir:c}),n===`sqlite`&&i===`turso`?await Fe(t):n===`postgres`?r===`prisma`&&i===`prisma-postgres`?await Se(t):i===`neon`?await Be(t):i===`supabase`&&await De(t):n===`mongodb`&&i===`mongodb-atlas`&&await ye(t)}catch(e){s.stop(g.red(`Failed to set up database`)),e instanceof Error&&p.error(g.red(e.message))}}async function He(t){let{examples:n,frontend:r,backend:i,projectDir:a}=t;if(!(i===`convex`||!n||n.length===0||n[0]===`none`)&&n.includes(`ai`)){let t=e.join(a,`apps/web`),n=e.join(a,`apps/server`),o=await h.pathExists(t),s=await h.pathExists(n),c=r.includes(`nuxt`),l=r.includes(`svelte`),u=r.includes(`react-router`)||r.includes(`tanstack-router`)||r.includes(`next`)||r.includes(`tanstack-start`);if(o){let e=[`ai`];c?e.push(`@ai-sdk/vue`):l?e.push(`@ai-sdk/svelte`):u&&e.push(`@ai-sdk/react`),await M({dependencies:e,projectDir:t})}s&&i!==`none`&&await M({dependencies:[`ai`,`@ai-sdk/google`],projectDir:n})}}async function Ue({projectDir:e,packageManager:t,addons:n=[]}){let r=d();try{r.start(`Running ${t} install...`),await b({cwd:e,stderr:`inherit`})`${t} install`,r.stop(`Dependencies installed successfully`),(n.includes(`biome`)||n.includes(`husky`))&&await We(e,t)}catch(e){r.stop(g.red(`Failed to install dependencies`)),e instanceof Error&&p.error(g.red(`Installation error: ${e.message}`))}}async function We(e,t){let n=d();try{n.start(`Running Biome format check...`),await b({cwd:e,stderr:`inherit`})`${t} biome check --write .`,n.stop(`Biome check completed successfully`)}catch{n.stop(g.yellow(`Biome check encountered issues`)),o.warn(g.yellow(`Some files may need manual formatting`))}}function Ge(e){let{database:t,relativePath:n,packageManager:r,depsInstalled:i,orm:a,addons:o,runtime:s,frontend:c,backend:l}=e,u=l===`convex`,d=r===`npm`?`npm run`:r,f=`cd ${n}`,p=o?.includes(`husky`)||o?.includes(`biome`),h=!u&&t!==`none`?Je(t,a,d,s):``,_=o?.includes(`tauri`)?Ye(d):``,v=p?qe(d):``,y=c?.includes(`native-nativewind`)||c?.includes(`native-unistyles`)?Ke(u):``,b=o?.includes(`pwa`)&&(c?.includes(`react-router`)||c?.includes(`tanstack-router`))?Xe():``,x=o?.includes(`starlight`)?Ze(d):``,S=c?.some(e=>[`tanstack-router`,`react-router`,`next`,`tanstack-start`,`nuxt`,`svelte`,`solid`].includes(e)),C=c?.includes(`native-nativewind`)||c?.includes(`native-unistyles`),w=r===`bun`&&C&&S?$e():``,T=!u&&t!==`none`&&a===`none`?Qe():``,E=c?.includes(`react-router`),D=c?.includes(`svelte`),O=E||D?`5173`:`3001`,k=N(r,`taze -r`),A=`${g.bold(`Next steps`)}\n${g.cyan(`1.`)} ${f}\n`,j=2;i||(A+=`${g.cyan(`${j++}.`)} ${r} install\n`),u&&(A+=`${g.cyan(`${j++}.`)} ${d} dev:setup ${g.dim(`(this will guide you through Convex project setup)`)}\n`),A+=`${g.cyan(`${j++}.`)} ${d} dev\n\n`,A+=`${g.bold(`Your project will be available at:`)}\n`,S?A+=`${g.cyan(`•`)} Frontend: http://localhost:${O}\n`:!C&&!o?.includes(`starlight`)&&(A+=`${g.yellow(`NOTE:`)} You are creating a backend-only app (no frontend selected)\n`),u||(A+=`${g.cyan(`•`)} Backend API: http://localhost:3000\n`),o?.includes(`starlight`)&&(A+=`${g.cyan(`•`)} Docs: http://localhost:4321\n`),y&&(A+=`\n${y.trim()}\n`),h&&(A+=`\n${h.trim()}\n`),_&&(A+=`\n${_.trim()}\n`),v&&(A+=`\n${v.trim()}\n`),b&&(A+=`\n${b.trim()}\n`),x&&(A+=`\n${x.trim()}\n`),T&&(A+=`\n${T.trim()}\n`),w&&(A+=`\n${w.trim()}\n`),A+=`\n${g.bold(`Update all dependencies:
|
|
166
|
-
`)}${g.cyan(k)}\n\n`,A+=`${g.bold(`Like Better-T Stack?`)} Please consider giving us a star on GitHub:\n`,A+=g.cyan(`https://github.com/AmanVarshney01/create-better-t-stack`),m.box(A)}function Ke(e){let t=e?`EXPO_PUBLIC_CONVEX_URL`:`EXPO_PUBLIC_SERVER_URL`,n=e?`https://<YOUR_CONVEX_URL>`:`http://<YOUR_LOCAL_IP>:3000`,r=`.env`,i=e?`your Convex deployment URL (find after running 'dev:setup')`:`your local IP address`;return`${g.yellow(`NOTE:`)} For Expo connectivity issues, update apps/native/${r} \nwith ${i}:\n${`${t}=${n}`}\n`}function qe(e){return`${g.bold(`Linting and formatting:`)}\n${g.cyan(`•`)} Format and lint fix: ${`${e} check`}\n`}function Je(e,t,n,r){let i=[];return t===`prisma`?(e===`sqlite`&&i.push(`${g.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`),r===`bun`&&i.push(`${g.yellow(`NOTE:`)} Prisma with Bun may require additional configuration. If you encounter errors,\nfollow the guidance provided in the error messages`),i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`)):t===`drizzle`?(i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`),e===`sqlite`&&i.push(`${g.cyan(`•`)} Start local DB (if needed): ${`cd apps/server && ${n} db:local`}`)):t===`none`&&i.push(`${g.yellow(`NOTE:`)} Manual database schema setup required.`),i.length?`${g.bold(`Database commands:`)}\n${i.join(`
|
|
167
|
-
`)}`:``}function Ye(e){return`\n${g.bold(`Desktop app with Tauri:`)}\n${g.cyan(`•`)} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}\n${g.cyan(`•`)} Build desktop app: ${`cd apps/web && ${e} desktop:build`}\n${g.yellow(`NOTE:`)} Tauri requires Rust and platform-specific dependencies.\nSee: https://v2.tauri.app/start/prerequisites/`}function Xe(){return`\n${g.bold(`PWA with React Router v7:`)}\n${g.yellow(`NOTE:`)} There is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809`}function Ze(e){return`\n${g.bold(`Documentation with Starlight:`)}\n${g.cyan(`•`)} Start docs site: ${`cd apps/docs && ${e} dev`}\n${g.cyan(`•`)} Build docs site: ${`cd apps/docs && ${e} build`}`}function Qe(){return`\n${g.yellow(`WARNING:`)} Database selected without an ORM. Features requiring database access (e.g., examples, auth) need manual setup.`}function $e(){return`\n${g.yellow(`WARNING:`)} 'bun' might cause issues with web + native apps in a monorepo. Use 'pnpm' if problems arise.`}async function et(e,t){await tt(e,t),t.backend===`convex`?await rt(e,t):await nt(e,t)}async function tt(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=n.projectName,i.scripts||={};let a=i.scripts,s=n.backend===`convex`?`@${n.projectName}/backend`:`server`,c=``;n.addons.includes(`turborepo`)?c=`turbo -F ${s} dev`:n.packageManager===`bun`?c=`bun run --filter ${s} dev`:n.packageManager===`pnpm`?c=`pnpm --filter ${s} dev`:n.packageManager===`npm`&&(c=`npm run dev --workspace ${s}`);let l=``;n.packageManager===`pnpm`?l=`pnpm -r dev`:n.packageManager===`npm`?l=`npm run dev --workspaces`:n.packageManager===`bun`&&(l=`bun run --filter '*' dev`);let u=n.backend!==`convex`&&n.database!==`none`&&n.orm!==`none`&&n.orm!==`mongoose`;n.addons.includes(`turborepo`)?(a.dev=`turbo dev`,a.build=`turbo build`,a[`check-types`]=`turbo check-types`,a[`dev:native`]=`turbo -F native dev`,a[`dev:web`]=`turbo -F web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`turbo -F ${s} setup`),u&&(a[`db:push`]=`turbo -F ${s} db:push`,a[`db:studio`]=`turbo -F ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`turbo -F ${s} db:generate`,a[`db:migrate`]=`turbo -F ${s} db:migrate`))):n.packageManager===`pnpm`?(a.dev=l,a.build=`pnpm -r build`,a[`check-types`]=`pnpm -r check-types`,a[`dev:native`]=`pnpm --filter native dev`,a[`dev:web`]=`pnpm --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`pnpm --filter ${s} setup`),u&&(a[`db:push`]=`pnpm --filter ${s} db:push`,a[`db:studio`]=`pnpm --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`pnpm --filter ${s} db:generate`,a[`db:migrate`]=`pnpm --filter ${s} db:migrate`))):n.packageManager===`npm`?(a.dev=l,a.build=`npm run build --workspaces`,a[`check-types`]=`npm run check-types --workspaces`,a[`dev:native`]=`npm run dev --workspace native`,a[`dev:web`]=`npm run dev --workspace web`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`npm run setup --workspace ${s}`),u&&(a[`db:push`]=`npm run db:push --workspace ${s}`,a[`db:studio`]=`npm run db:studio --workspace ${s}`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`npm run db:generate --workspace ${s}`,a[`db:migrate`]=`npm run db:migrate --workspace ${s}`))):n.packageManager===`bun`&&(a.dev=l,a.build=`bun run --filter '*' build`,a[`check-types`]=`bun run --filter '*' check-types`,a[`dev:native`]=`bun run --filter native dev`,a[`dev:web`]=`bun run --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`bun run --filter ${s} setup`),u&&(a[`db:push`]=`bun run --filter ${s} db:push`,a[`db:studio`]=`bun run --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`bun run --filter ${s} db:generate`,a[`db:migrate`]=`bun run --filter ${s} db:migrate`))),n.addons.includes(`biome`)&&(a.check=`biome check --write .`),n.addons.includes(`husky`)&&(a.prepare=`husky`,i[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]});try{let{stdout:e}=await x(n.packageManager,[`-v`],{cwd:t});i.packageManager=`${n.packageManager}@${e.trim()}`}catch{o.warn(`Could not determine ${n.packageManager} version.`)}i.workspaces||=[];let d=i.workspaces;if(n.backend===`convex`){d.includes(`packages/*`)||d.push(`packages/*`);let e=n.frontend.length>0||n.addons.includes(`starlight`);e&&!d.includes(`apps/*`)&&d.push(`apps/*`)}else d.includes(`apps/*`)||d.push(`apps/*`),d.includes(`packages/*`)||d.push(`packages/*`);await h.writeJson(r,i,{spaces:2})}async function nt(t,n){let r=e.join(t,`apps/server/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts||={};let a=i.scripts;n.database!==`none`&&(n.database===`sqlite`&&n.orm===`drizzle`&&(a[`db:local`]=`turso dev --db-file local.db`),n.orm===`prisma`?(a[`db:push`]=`prisma db push --schema ./prisma/schema`,a[`db:studio`]=`prisma studio`,a[`db:generate`]=`prisma generate --schema ./prisma/schema`,a[`db:migrate`]=`prisma migrate dev`):n.orm===`drizzle`&&(a[`db:push`]=`drizzle-kit push`,a[`db:studio`]=`drizzle-kit studio`,a[`db:generate`]=`drizzle-kit generate`,a[`db:migrate`]=`drizzle-kit migrate`)),await h.writeJson(r,i,{spaces:2})}async function rt(t,n){let r=e.join(t,`packages/backend/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=`@${n.projectName}/backend`,i.scripts||={},await h.writeJson(r,i,{spaces:2})}async function it(e,t){if(!t)return;let n=await b({cwd:e,reject:!1,stderr:`pipe`})`git --version`;if(n.exitCode!==0){o.warn(g.yellow(`Git is not installed`));return}let r=await b({cwd:e,reject:!1,stderr:`pipe`})`git init`;if(r.exitCode!==0)throw Error(`Git initialization failed: ${r.stderr}`)}async function at(t){let{runtime:n,backend:r,projectDir:i}=t;if(r===`convex`||r===`next`||n===`none`)return;let a=e.join(i,`apps/server`);await h.pathExists(a)&&(n===`bun`?await ot(a,r):n===`node`&&await st(a,r))}async function ot(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`bun run --hot src/index.ts`,start:`bun run dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`@types/bun`],projectDir:t})}async function st(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts={...i.scripts,dev:`tsx watch src/index.ts`,start:`node dist/src/index.js`},await h.writeJson(r,i,{spaces:2}),await M({devDependencies:[`tsx`,`@types/node`],projectDir:t}),n===`hono`?await M({dependencies:[`@hono/node-server`],projectDir:t}):n===`elysia`&&await M({dependencies:[`@elysiajs/node`],projectDir:t})}async function K(t,n,r){try{let i=await h.readFile(t,`utf-8`),a=w.compile(i),o=a(r);await h.ensureDir(e.dirname(n)),await h.writeFile(n,o)}catch(e){throw p.error(`Error processing template ${t}:`,e),Error(`Failed to process template ${t}`)}}w.registerHelper(`or`,(e,t)=>e||t),w.registerHelper(`eq`,(e,t)=>e===t),w.registerHelper(`includes`,(e,t)=>Array.isArray(e)&&e.includes(t));async function q(t,n,r,i,a=!0){let o=await C(t,{cwd:n,dot:!0,onlyFiles:!0,absolute:!1});for(let t of o){let o=e.join(n,t),s=t;t.endsWith(`.hbs`)&&(s=t.slice(0,-4));let c=e.basename(t);c===`_gitignore`?s=e.join(e.dirname(t),`.gitignore`):c===`_npmrc`&&(s=e.join(e.dirname(t),`.npmrc`));let l=e.join(r,s);try{if(await h.ensureDir(e.dirname(l)),!a&&await h.pathExists(l))continue;o.endsWith(`.hbs`)?await K(o,l,i):await h.copy(o,l,{overwrite:!0})}catch{}}}async function ct(t,n){let r=e.join(k,`templates/base`);await q([`**/*`],r,t,n),await h.ensureDir(e.join(t,`packages`))}async function lt(t,n){let r=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),i=n.frontend.includes(`nuxt`),a=n.frontend.includes(`svelte`),o=n.frontend.includes(`solid`),s=n.frontend.includes(`native-nativewind`),c=n.frontend.includes(`native-unistyles`),l=s||c,u=n.backend===`convex`;if(r||i||a||o){let s=e.join(t,`apps/web`);if(await h.ensureDir(s),r){let t=e.join(k,`templates/frontend/react/web-base`);await h.pathExists(t)&&await q(`**/*`,t,s,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/frontend/react/${r}`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/web/react/base`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}}else if(i){let t=e.join(k,`templates/frontend/nuxt`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}else if(a){let t=e.join(k,`templates/frontend/svelte`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}else if(o){let t=e.join(k,`templates/frontend/solid`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/solid`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}}if(s||c){let r=e.join(t,`apps/native`);await h.ensureDir(r);let i=e.join(k,`templates/frontend/native/native-base`);await h.pathExists(i)&&await q(`**/*`,i,r,n);let a=``;s?a=`nativewind`:c&&(a=`unistyles`);let o=e.join(k,`templates/frontend/native/${a}`);if(await h.pathExists(o)&&await q(`**/*`,o,r,n,!0),!u&&(n.api===`trpc`||n.api===`orpc`)){let t=e.join(k,`templates/api/${n.api}/native`);await h.pathExists(t)&&await q(`**/*`,t,r,n)}}}async function ut(t,n){if(n.backend===`none`)return;let r=e.join(t,`apps/server`);if(n.backend===`convex`){await h.pathExists(r)&&await h.remove(r);let i=e.join(t,`packages/backend`),a=e.join(k,`templates/backend/convex/packages/backend`);await h.ensureDir(i),await h.pathExists(a)&&await q(`**/*`,a,i,n);return}await h.ensureDir(r);let i=e.join(k,`templates/backend/server/server-base`);await h.pathExists(i)&&await q(`**/*`,i,r,n);let a=e.join(k,`templates/backend/server/${n.backend}`);if(await h.pathExists(a)&&await q(`**/*`,a,r,n,!0),n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/server/base`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!0);let i=e.join(k,`templates/api/${n.api}/server/${n.backend}`);await h.pathExists(i)&&await q(`**/*`,i,r,n,!0)}}async function dt(t,n){if(n.backend===`convex`||n.orm===`none`||n.database===`none`)return;let r=e.join(t,`apps/server`);await h.ensureDir(r);let i=e.join(k,`templates/db/${n.orm}/${n.database}`);await h.pathExists(i)&&await q(`**/*`,i,r,n)}async function ft(t,n){if(n.backend===`convex`||!n.auth)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=e.join(t,`apps/native`),o=await h.pathExists(r),s=await h.pathExists(i),c=await h.pathExists(a),l=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),u=n.frontend.includes(`nuxt`),d=n.frontend.includes(`svelte`),f=n.frontend.includes(`solid`),p=n.frontend.includes(`native-nativewind`),m=n.frontend.includes(`native-unistyles`),g=p||m;if(o){let t=e.join(k,`templates/auth/server/base`);if(await h.pathExists(t)&&await q(`**/*`,t,r,n),n.backend===`next`){let t=e.join(k,`templates/auth/server/next`);await h.pathExists(t)&&await q(`**/*`,t,r,n)}if(n.orm!==`none`&&n.database!==`none`){let t=n.orm,i=n.database,a=``;t===`drizzle`?a=e.join(k,`templates/auth/server/db/drizzle/${i}`):t===`prisma`?a=e.join(k,`templates/auth/server/db/prisma/${i}`):t===`mongoose`&&(a=e.join(k,`templates/auth/server/db/mongoose/${i}`)),a&&await h.pathExists(a)&&await q(`**/*`,a,r,n)}}if((l||u||d||f)&&s){if(l){let t=e.join(k,`templates/auth/web/react/base`);await h.pathExists(t)&&await q(`**/*`,t,i,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/auth/web/react/${r}`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}else if(u){let t=e.join(k,`templates/auth/web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}else if(d){if(n.api===`orpc`){let t=e.join(k,`templates/auth/web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}else if(f&&n.api===`orpc`){let t=e.join(k,`templates/auth/web/solid`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}if(g&&c){let t=e.join(k,`templates/auth/native/native-base`);await h.pathExists(t)&&await q(`**/*`,t,a,n);let r=``;if(p?r=`nativewind`:m&&(r=`unistyles`),r){let t=e.join(k,`templates/auth/native/${r}`);await h.pathExists(t)&&await q(`**/*`,t,a,n)}}}async function pt(t,n){if(!(!n.addons||n.addons.length===0))for(let r of n.addons){if(r===`none`)continue;let i=e.join(k,`templates/addons/${r}`),a=t;if(r===`pwa`){let r=e.join(t,`apps/web`);if(!await h.pathExists(r))continue;if(a=r,n.frontend.includes(`next`))i=e.join(k,`templates/addons/pwa/apps/web/next`);else if(n.frontend.some(e=>[`tanstack-router`,`react-router`,`solid`].includes(e)))i=e.join(k,`templates/addons/pwa/apps/web/vite`);else continue}await h.pathExists(i)&&await q(`**/*`,i,a,n)}}async function mt(t,n){if(!n.examples||n.examples.length===0||n.examples[0]===`none`)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=await h.pathExists(r),o=await h.pathExists(i),s=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),c=n.frontend.includes(`nuxt`),l=n.frontend.includes(`svelte`),u=n.frontend.includes(`solid`);for(let t of n.examples){if(t===`none`)continue;let d=e.join(k,`templates/examples/${t}`);if(a&&n.backend!==`convex`&&n.backend!==`none`){let i=e.join(d,`server`);if(t===`ai`&&n.backend===`next`){let t=e.join(i,`next`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!1)}if(n.orm!==`none`&&n.database!==`none`){let t=e.join(i,n.orm,`base`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!1);let a=e.join(i,n.orm,n.database);await h.pathExists(a)&&await q(`**/*`,a,r,n,!1)}let a=[`${n.orm}/**`];t===`ai`&&n.backend===`next`&&a.push(`next/**`);let o=await C([`**/*.ts`,`**/*.hbs`],{cwd:i,onlyFiles:!0,deep:1,ignore:a});for(let t of o){let a=e.join(i,t),o=e.join(r,t.replace(`.hbs`,``));try{a.endsWith(`.hbs`)?await K(a,o,n):await h.pathExists(o)||await h.copy(a,o,{overwrite:!1})}catch{}}}if(o){if(s){let t=e.join(d,`web/react`);if(await h.pathExists(t)){let r=n.frontend.find(e=>[`next`,`react-router`,`tanstack-router`,`tanstack-start`].includes(e));if(r){let a=e.join(t,r);await h.pathExists(a)&&await q(`**/*`,a,i,n,!1)}}}else if(c){let t=e.join(d,`web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}else if(l){let t=e.join(d,`web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}else if(u){let t=e.join(d,`web/solid`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}}}}async function ht(t,n){let r=e.join(k,`templates/extras`),i=n.frontend.includes(`native-nativewind`),a=n.frontend.includes(`native-unistyles`),o=i||a;if(n.packageManager===`pnpm`){let n=e.join(r,`pnpm-workspace.yaml`),i=e.join(t,`pnpm-workspace.yaml`);await h.pathExists(n)&&await h.copy(n,i)}if(n.packageManager===`pnpm`&&(o||n.frontend.includes(`nuxt`))){let i=e.join(r,`_npmrc.hbs`),a=e.join(t,`.npmrc`);await h.pathExists(i)&&await K(i,a,n)}}async function gt(e){let n=e.projectDir,r=e.backend===`convex`;try{return await h.ensureDir(n),await ct(n,e),await lt(n,e),await ut(n,e),r||(await dt(n,e),await ft(n,e)),e.examples.length>0&&e.examples[0]!==`none`&&await mt(n,e),await pt(n,e),await se(e),r||(await ue(e),await Ve(e),await at(e),e.examples.length>0&&e.examples[0]!==`none`&&await He(e)),e.addons.length>0&&e.addons[0]!==`none`&&await ne(e),!r&&e.auth&&await ce(e),await ht(n,e),await ge(e),await et(n,e),await de(n,e),await it(n,e.git),o.success(`Project template successfully scaffolded!`),e.install&&await Ue({projectDir:n,packageManager:e.packageManager,addons:e.addons}),Ge({...e,depsInstalled:e.install}),n}catch(e){e instanceof Error?(t(g.red(`Error during project creation: ${e.message}`)),console.error(e.stack),process.exit(1)):(t(g.red(`An unexpected error occurred: ${String(e)}`)),console.error(e),process.exit(1))}}async function _t(e,n){if(e!==void 0)return e;let r=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`solid`)||n?.includes(`next`),i=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`nuxt`)||n?.includes(`svelte`)||n?.includes(`solid`)||n?.includes(`next`),o=[{value:`turborepo`,label:`Turborepo (Recommended)`,hint:`Optimize builds for monorepos`},{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:`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`}],c=o.filter(e=>e.value===`pwa`?r:e.value===`tauri`?i:!0),l=A.addons.filter(e=>c.some(t=>t.value===e)),u=await s({message:`Select addons`,options:c,initialValues:l,required:!1});return a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u.includes(`husky`)&&!u.includes(`biome`)&&u.push(`biome`),u}async function vt(e,n,r){if(r===`convex`||r===`none`)return`none`;if(e)return e;let i=n?.includes(`nuxt`),o=n?.includes(`svelte`),s=n?.includes(`solid`),c=[{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 layer (e.g. for full-stack frameworks like Next.js with Route Handlers)`}];(i||o||s)&&(c=[{value:`orpc`,label:`oRPC`,hint:`End-to-end type-safe APIs (Recommended for ${i?`Nuxt`:o?`Svelte`:`Solid`} frontend)`},{value:`none`,label:`None`,hint:`No API layer`}]);let l=await u({message:`Select API type`,options:c,initialValue:c[0].value});return a(l)&&(t(g.red(`Operation cancelled`)),process.exit(0)),l}async function yt(e,r,i){if(i===`convex`||!r)return!1;if(e!==void 0)return e;let o=await n({message:`Add authentication with Better-Auth?`,initialValue:A.auth});return a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),o}async function bt(e,n){if(e!==void 0)return e;let r=n?.some(e=>e===`nuxt`||e===`solid`),i=[{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:`fastify`,label:`Fastify`,hint:`Fast, low-overhead web framework for Node.js`},{value:`elysia`,label:`Elysia`,hint:`Ergonomic web framework for building backend servers`}];r||i.push({value:`convex`,label:`Convex`,hint:`Reactive backend-as-a-service platform`}),i.push({value:`none`,label:`None`,hint:`No backend server (e.g., for a static site or client-only app)`});let o=A.backend;r&&o===`convex`&&(o=`hono`);let s=await u({message:`Select backend framework`,options:i,initialValue:o});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function xt(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;let r=await u({message:`Select database`,options:[{value:`none`,label:`None`,hint:`No database setup`},{value:`sqlite`,label:`SQLite`,hint:`lightweight, server-less, embedded relational database`},{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:A.database});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function St(e,n,r,i){if(i===`convex`)return`none`;if(n!==void 0)return n;if(e===`none`||e===`sqlite`&&r===`prisma`)return`none`;let o=[];if(e===`sqlite`)o=[{value:`turso`,label:`Turso`,hint:`SQLite for Production. Powered by libSQL`},{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`postgres`)o=[{value:`neon`,label:`Neon Postgres`,hint:`Serverless Postgres with branching capability`},{value:`supabase`,label:`Supabase`,hint:`Local Supabase stack (requires Docker)`},...r===`prisma`?[{value:`prisma-postgres`,label:`Prisma Postgres`,hint:`Instant Postgres for Global Applications`}]:[],{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`mongodb`)o=[{value:`mongodb-atlas`,label:`MongoDB Atlas`,hint:`The most effective way to deploy MongoDB`},{value:`none`,label:`None`,hint:`Manual setup`}];else return`none`;let s=await u({message:`Select ${e} setup option`,options:o,initialValue:`none`});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Ct(e,n,r,i,o){if(o===`none`)return[];if(e!==void 0)return e;if(i===`convex`)return[`todo`];if(i===`none`||n===`none`)return[];let c=r&&r.length===1&&(r[0]===`native-nativewind`||r[0]===`native-unistyles`);if(c)return[];let l=r?.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`,`solid`].includes(e))??!1,u=!r||r.length===0;if(!l&&!u)return[];let d=[],f=[{value:`todo`,label:`Todo App`,hint:`A simple CRUD example app`}];return i!==`elysia`&&!r?.includes(`solid`)&&f.push({value:`ai`,label:`AI Chat`,hint:`A simple AI chat interface using AI SDK`}),d=await s({message:`Include examples`,options:f,required:!1,initialValues:A.examples}),a(d)&&(t(g.red(`Operation cancelled`)),process.exit(0)),d}async function wt(e,n){if(e!==void 0)return e;let r=await s({message:`Select platforms to develop for`,options:[{value:`web`,label:`Web`,hint:`React, Vue or Svelte Web Application`},{value:`native`,label:`Native`,hint:`Create a React Native/Expo app`}],required:!1,initialValues:[`web`]});a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0));let i=[];if(r.includes(`web`)){let e=[{value:`tanstack-router`,label:`TanStack Router`,hint:`Modern and scalable routing for React Applications`},{value:`react-router`,label:`React Router`,hint:`A user‑obsessed, standards‑focused, multi‑strategy router`},{value:`next`,label:`Next.js`,hint:`The React Framework for the Web`},{value:`nuxt`,label:`Nuxt`,hint:`The Progressive Web Framework for Vue.js`},{value:`svelte`,label:`Svelte`,hint:`web development for the rest of us`},{value:`solid`,label:`Solid`,hint:`Simple and performant reactivity for building user interfaces`},{value:`tanstack-start`,label:`TanStack Start (beta)`,hint:`SSR, Server Functions, API Routes and more with TanStack Router`}],r=e.filter(e=>n===`convex`?e.value!==`nuxt`&&e.value!==`solid`:!0),o=await u({message:`Choose frontend framework`,options:r,initialValue:A.frontend[0]});a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(o)}if(r.includes(`native`)){let e=await u({message:`Choose native framework`,options:[{value:`native-nativewind`,label:`NativeWind`,hint:`Use Tailwind CSS for React Native`},{value:`native-unistyles`,label:`Unistyles`,hint:`Consistent styling for React Native`}],initialValue:`native-nativewind`});a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(e)}return i}async function Tt(e){if(e!==void 0)return e;let r=await n({message:`Initialize git repository?`,initialValue:A.git});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Et(e){if(e!==void 0)return e;let r=await n({message:`Install dependencies?`,initialValue:A.install});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}const J={prisma:{value:`prisma`,label:`Prisma`,hint:`Powerful, feature-rich ORM`},mongoose:{value:`mongoose`,label:`Mongoose`,hint:`Elegant object modeling tool`},drizzle:{value:`drizzle`,label:`Drizzle`,hint:`Lightweight and performant TypeScript ORM`}};async function Dt(e,n,r,i){if(i===`convex`||!n)return`none`;if(e!==void 0)return e;let o=[...r===`mongodb`?[J.prisma,J.mongoose]:[J.drizzle,J.prisma]],s=await u({message:`Select ORM`,options:o,initialValue:r===`mongodb`?`prisma`:A.orm});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Ot(e){if(e!==void 0)return e;let n=E(),r=await u({message:`Choose package manager`,options:[{value:`npm`,label:`npm`,hint:`Node Package Manager`},{value:`pnpm`,label:`pnpm`,hint:`Fast, disk space efficient package manager`},{value:`bun`,label:`bun`,hint:`All-in-one JavaScript runtime & toolkit`}],initialValue:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function kt(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;if(n===`next`)return`node`;let r=await u({message:`Select runtime`,options:[{value:`bun`,label:`Bun`,hint:`Fast all-in-one JavaScript runtime`},{value:`node`,label:`Node.js`,hint:`Traditional Node.js runtime`}],initialValue:A.runtime});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function At(e,n,i,a){let o=await r({frontend:()=>wt(e.frontend,e.backend),backend:({results:t})=>bt(e.backend,t.frontend),runtime:({results:t})=>kt(e.runtime,t.backend),database:({results:t})=>xt(e.database,t.backend),orm:({results:t})=>Dt(e.orm,t.database!==`none`,t.database,t.backend),api:({results:t})=>vt(e.api,t.frontend,t.backend),auth:({results:t})=>yt(e.auth,t.database!==`none`,t.backend),addons:({results:t})=>_t(e.addons,t.frontend),examples:({results:t})=>Ct(e.examples,t.database,t.frontend,t.backend,t.api),dbSetup:({results:t})=>St(t.database??`none`,e.dbSetup,t.orm,t.backend),git:()=>Tt(e.git),packageManager:()=>Ot(e.packageManager),install:()=>Et(e.install)},{onCancel:()=>{t(g.red(`Operation cancelled`)),process.exit(0)}});return o.backend===`convex`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`),o.backend===`none`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`,o.examples=[]),{projectName:n,projectDir:i,relativePath:a,frontend:o.frontend,backend:o.backend,runtime:o.runtime,database:o.database,orm:o.orm,auth:o.auth,addons:o.addons,examples:o.examples,git:o.git,packageManager:o.packageManager,install:o.install,dbSetup:o.dbSetup,api:o.api}}const jt=[`<`,`>`,`:`,`"`,`|`,`?`,`*`],Y=255;function X(e){if(e!==`.`){if(!e)return`Project name cannot be empty`;if(e.length>Y)return`Project name must be less than ${Y} characters`;if(jt.some(t=>e.includes(t)))return`Project name contains invalid characters`;if(e.startsWith(`.`)||e.startsWith(`-`))return`Project name cannot start with a dot or dash`;if(e.toLowerCase()===`node_modules`)return`Project name is reserved`}}async function Z(n){if(n){if(n===`.`)return n;let t=e.basename(n),r=X(t);if(!r)return n}let r=!1,i=``,o=A.projectName,s=1;for(;h.pathExistsSync(e.resolve(process.cwd(),o))&&h.readdirSync(e.resolve(process.cwd(),o)).length>0;)o=`${A.projectName}-${s}`,s++;for(;!r;){let s=await f({message:`Enter your project name or path (relative to current directory)`,placeholder:o,initialValue:n,defaultValue:o,validate:t=>{let n=t.trim()||o,r=e.basename(n),i=X(r);if(i)return i;if(n!==`.`){let t=e.resolve(process.cwd(),n);if(!t.startsWith(process.cwd()))return`Project path must be within current directory`}}});a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),i=s||o,r=!0}return i}function Q(e){let t=[];if(e.projectName&&t.push(`${g.blue(`Project Name:`)} ${e.projectName}`),e.frontend!==void 0){let n=Array.isArray(e.frontend)?e.frontend:[e.frontend],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Frontend:`)} ${r}`)}if(e.backend!==void 0&&t.push(`${g.blue(`Backend:`)} ${String(e.backend)}`),e.runtime!==void 0&&t.push(`${g.blue(`Runtime:`)} ${String(e.runtime)}`),e.api!==void 0&&t.push(`${g.blue(`API:`)} ${String(e.api)}`),e.database!==void 0&&t.push(`${g.blue(`Database:`)} ${String(e.database)}`),e.orm!==void 0&&t.push(`${g.blue(`ORM:`)} ${String(e.orm)}`),e.auth!==void 0){let n=typeof e.auth==`boolean`?e.auth?`Yes`:`No`:String(e.auth);t.push(`${g.blue(`Authentication:`)} ${n}`)}if(e.addons!==void 0){let n=Array.isArray(e.addons)?e.addons:[e.addons],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Addons:`)} ${r}`)}if(e.examples!==void 0){let n=Array.isArray(e.examples)?e.examples:[e.examples],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Examples:`)} ${r}`)}if(e.git!==void 0){let n=typeof e.git==`boolean`?e.git?`Yes`:`No`:String(e.git);t.push(`${g.blue(`Git Init:`)} ${n}`)}if(e.packageManager!==void 0&&t.push(`${g.blue(`Package Manager:`)} ${String(e.packageManager)}`),e.install!==void 0){let n=typeof e.install==`boolean`?e.install?`Yes`:`No`:String(e.install);t.push(`${g.blue(`Install Dependencies:`)} ${n}`)}return e.dbSetup!==void 0&&t.push(`${g.blue(`Database Setup:`)} ${String(e.dbSetup)}`),t.length===0?g.yellow(`No configuration selected.`):t.join(`
|
|
168
|
-
`)}function Mt(e){let t=[];e.frontend&&e.frontend.length>0?t.push(`--frontend ${e.frontend.join(` `)}`):t.push(`--frontend none`),t.push(`--backend ${e.backend}`),t.push(`--runtime ${e.runtime}`),t.push(`--database ${e.database}`),t.push(`--orm ${e.orm}`),t.push(`--api ${e.api}`),t.push(e.auth?`--auth`:`--no-auth`),e.addons&&e.addons.length>0?t.push(`--addons ${e.addons.join(` `)}`):t.push(`--addons none`),e.examples&&e.examples.length>0?t.push(`--examples ${e.examples.join(` `)}`):t.push(`--examples none`),t.push(`--db-setup ${e.dbSetup}`),t.push(e.git?`--git`:`--no-git`),t.push(`--package-manager ${e.packageManager}`),t.push(e.install?`--install`:`--no-install`);let n=``,r=e.packageManager;r===`npm`?n=`npx create-better-t-stack@latest`:r===`pnpm`?n=`pnpm create better-t-stack@latest`:r===`bun`&&(n=`bun create better-t-stack@latest`);let i=e.relativePath?` ${e.relativePath}`:``;return`${n}${i} ${t.join(` `)}`}const Nt=()=>{let t=e.join(k,`package.json`),n=h.readJSONSync(t);return n.version??`1.0.0`},$=`
|
|
165
|
+
- \`cd apps/docs && ${e} build\`: Build documentation site`),c}async function Je({projectDir:e,packageManager:t,addons:n=[]}){let r=d();try{r.start(`Running ${t} install...`),await b({cwd:e,stderr:`inherit`})`${t} install`,r.stop(`Dependencies installed successfully`),(n.includes(`biome`)||n.includes(`husky`))&&await Ye(e,t)}catch(e){r.stop(g.red(`Failed to install dependencies`)),e instanceof Error&&p.error(g.red(`Installation error: ${e.message}`))}}async function Ye(e,t){let n=d();try{n.start(`Running Biome format check...`),await b({cwd:e,stderr:`inherit`})`${t} biome check --write .`,n.stop(`Biome check completed successfully`)}catch{n.stop(g.yellow(`Biome check encountered issues`)),o.warn(g.yellow(`Some files may need manual formatting`))}}function Xe(e){let{database:t,relativePath:n,packageManager:r,depsInstalled:i,orm:a,addons:o,runtime:s,frontend:c,backend:l}=e,u=l===`convex`,d=r===`npm`?`npm run`:r,f=`cd ${n}`,p=o?.includes(`husky`)||o?.includes(`biome`),h=!u&&t!==`none`?$e(t,a,d,s):``,_=o?.includes(`tauri`)?et(d):``,v=p?Qe(d):``,y=c?.includes(`native-nativewind`)||c?.includes(`native-unistyles`)?Ze(u):``,b=o?.includes(`pwa`)&&(c?.includes(`react-router`)||c?.includes(`tanstack-router`))?tt():``,x=o?.includes(`starlight`)?nt(d):``,S=c?.some(e=>[`tanstack-router`,`react-router`,`next`,`tanstack-start`,`nuxt`,`svelte`,`solid`].includes(e)),C=c?.includes(`native-nativewind`)||c?.includes(`native-unistyles`),w=r===`bun`&&C&&S?it():``,T=!u&&t!==`none`&&a===`none`?rt():``,E=c?.includes(`react-router`),D=c?.includes(`svelte`),O=E||D?`5173`:`3001`,k=N(r,`taze -r`),A=`${g.bold(`Next steps`)}\n${g.cyan(`1.`)} ${f}\n`,j=2;i||(A+=`${g.cyan(`${j++}.`)} ${r} install\n`),u&&(A+=`${g.cyan(`${j++}.`)} ${d} dev:setup ${g.dim(`(this will guide you through Convex project setup)`)}\n`),A+=`${g.cyan(`${j++}.`)} ${d} dev\n\n`,A+=`${g.bold(`Your project will be available at:`)}\n`,S?A+=`${g.cyan(`•`)} Frontend: http://localhost:${O}\n`:!C&&!o?.includes(`starlight`)&&(A+=`${g.yellow(`NOTE:`)} You are creating a backend-only app (no frontend selected)\n`),u||(A+=`${g.cyan(`•`)} Backend API: http://localhost:3000\n`),o?.includes(`starlight`)&&(A+=`${g.cyan(`•`)} Docs: http://localhost:4321\n`),y&&(A+=`\n${y.trim()}\n`),h&&(A+=`\n${h.trim()}\n`),_&&(A+=`\n${_.trim()}\n`),v&&(A+=`\n${v.trim()}\n`),b&&(A+=`\n${b.trim()}\n`),x&&(A+=`\n${x.trim()}\n`),T&&(A+=`\n${T.trim()}\n`),w&&(A+=`\n${w.trim()}\n`),A+=`\n${g.bold(`Update all dependencies:
|
|
166
|
+
`)}${g.cyan(k)}\n\n`,A+=`${g.bold(`Like Better-T Stack?`)} Please consider giving us a star on GitHub:\n`,A+=g.cyan(`https://github.com/AmanVarshney01/create-better-t-stack`),m.box(A)}function Ze(e){let t=e?`EXPO_PUBLIC_CONVEX_URL`:`EXPO_PUBLIC_SERVER_URL`,n=e?`https://<YOUR_CONVEX_URL>`:`http://<YOUR_LOCAL_IP>:3000`,r=`.env`,i=e?`your Convex deployment URL (find after running 'dev:setup')`:`your local IP address`;return`${g.yellow(`NOTE:`)} For Expo connectivity issues, update apps/native/${r} \nwith ${i}:\n${`${t}=${n}`}\n`}function Qe(e){return`${g.bold(`Linting and formatting:`)}\n${g.cyan(`•`)} Format and lint fix: ${`${e} check`}\n`}function $e(e,t,n,r){let i=[];return t===`prisma`?(e===`sqlite`&&i.push(`${g.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`),r===`bun`&&i.push(`${g.yellow(`NOTE:`)} Prisma with Bun may require additional configuration. If you encounter errors,\nfollow the guidance provided in the error messages`),i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`)):t===`drizzle`?(i.push(`${g.cyan(`•`)} Apply schema: ${`${n} db:push`}`),i.push(`${g.cyan(`•`)} Database UI: ${`${n} db:studio`}`),e===`sqlite`&&i.push(`${g.cyan(`•`)} Start local DB (if needed): ${`cd apps/server && ${n} db:local`}`)):t===`none`&&i.push(`${g.yellow(`NOTE:`)} Manual database schema setup required.`),i.length?`${g.bold(`Database commands:`)}\n${i.join(`
|
|
167
|
+
`)}`:``}function et(e){return`\n${g.bold(`Desktop app with Tauri:`)}\n${g.cyan(`•`)} Start desktop app: ${`cd apps/web && ${e} desktop:dev`}\n${g.cyan(`•`)} Build desktop app: ${`cd apps/web && ${e} desktop:build`}\n${g.yellow(`NOTE:`)} Tauri requires Rust and platform-specific dependencies.\nSee: https://v2.tauri.app/start/prerequisites/`}function tt(){return`\n${g.bold(`PWA with React Router v7:`)}\n${g.yellow(`NOTE:`)} There is a known compatibility issue between VitePWA and React Router v7.\nSee: https://github.com/vite-pwa/vite-plugin-pwa/issues/809`}function nt(e){return`\n${g.bold(`Documentation with Starlight:`)}\n${g.cyan(`•`)} Start docs site: ${`cd apps/docs && ${e} dev`}\n${g.cyan(`•`)} Build docs site: ${`cd apps/docs && ${e} build`}`}function rt(){return`\n${g.yellow(`WARNING:`)} Database selected without an ORM. Features requiring database access (e.g., examples, auth) need manual setup.`}function it(){return`\n${g.yellow(`WARNING:`)} 'bun' might cause issues with web + native apps in a monorepo. Use 'pnpm' if problems arise.`}async function at(e,t){await ot(e,t),t.backend===`convex`?await ct(e,t):await st(e,t)}async function ot(t,n){let r=e.join(t,`package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=n.projectName,i.scripts||={};let a=i.scripts,s=n.backend===`convex`?`@${n.projectName}/backend`:`server`,c=``;n.addons.includes(`turborepo`)?c=`turbo -F ${s} dev`:n.packageManager===`bun`?c=`bun run --filter ${s} dev`:n.packageManager===`pnpm`?c=`pnpm --filter ${s} dev`:n.packageManager===`npm`&&(c=`npm run dev --workspace ${s}`);let l=``;n.packageManager===`pnpm`?l=`pnpm -r dev`:n.packageManager===`npm`?l=`npm run dev --workspaces`:n.packageManager===`bun`&&(l=`bun run --filter '*' dev`);let u=n.backend!==`convex`&&n.database!==`none`&&n.orm!==`none`&&n.orm!==`mongoose`;n.addons.includes(`turborepo`)?(a.dev=`turbo dev`,a.build=`turbo build`,a[`check-types`]=`turbo check-types`,a[`dev:native`]=`turbo -F native dev`,a[`dev:web`]=`turbo -F web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`turbo -F ${s} setup`),u&&(a[`db:push`]=`turbo -F ${s} db:push`,a[`db:studio`]=`turbo -F ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`turbo -F ${s} db:generate`,a[`db:migrate`]=`turbo -F ${s} db:migrate`))):n.packageManager===`pnpm`?(a.dev=l,a.build=`pnpm -r build`,a[`check-types`]=`pnpm -r check-types`,a[`dev:native`]=`pnpm --filter native dev`,a[`dev:web`]=`pnpm --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`pnpm --filter ${s} setup`),u&&(a[`db:push`]=`pnpm --filter ${s} db:push`,a[`db:studio`]=`pnpm --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`pnpm --filter ${s} db:generate`,a[`db:migrate`]=`pnpm --filter ${s} db:migrate`))):n.packageManager===`npm`?(a.dev=l,a.build=`npm run build --workspaces`,a[`check-types`]=`npm run check-types --workspaces`,a[`dev:native`]=`npm run dev --workspace native`,a[`dev:web`]=`npm run dev --workspace web`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`npm run setup --workspace ${s}`),u&&(a[`db:push`]=`npm run db:push --workspace ${s}`,a[`db:studio`]=`npm run db:studio --workspace ${s}`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`npm run db:generate --workspace ${s}`,a[`db:migrate`]=`npm run db:migrate --workspace ${s}`))):n.packageManager===`bun`&&(a.dev=l,a.build=`bun run --filter '*' build`,a[`check-types`]=`bun run --filter '*' check-types`,a[`dev:native`]=`bun run --filter native dev`,a[`dev:web`]=`bun run --filter web dev`,a[`dev:server`]=c,n.backend===`convex`&&(a[`dev:setup`]=`bun run --filter ${s} setup`),u&&(a[`db:push`]=`bun run --filter ${s} db:push`,a[`db:studio`]=`bun run --filter ${s} db:studio`,(n.orm===`prisma`||n.orm===`drizzle`)&&(a[`db:generate`]=`bun run --filter ${s} db:generate`,a[`db:migrate`]=`bun run --filter ${s} db:migrate`))),n.addons.includes(`biome`)&&(a.check=`biome check --write .`),n.addons.includes(`husky`)&&(a.prepare=`husky`,i[`lint-staged`]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":[`biome check --write .`]});try{let{stdout:e}=await x(n.packageManager,[`-v`],{cwd:t});i.packageManager=`${n.packageManager}@${e.trim()}`}catch{o.warn(`Could not determine ${n.packageManager} version.`)}i.workspaces||=[];let d=i.workspaces;if(n.backend===`convex`){d.includes(`packages/*`)||d.push(`packages/*`);let e=n.frontend.length>0||n.addons.includes(`starlight`);e&&!d.includes(`apps/*`)&&d.push(`apps/*`)}else d.includes(`apps/*`)||d.push(`apps/*`),d.includes(`packages/*`)||d.push(`packages/*`);await h.writeJson(r,i,{spaces:2})}async function st(t,n){let r=e.join(t,`apps/server/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.scripts||={};let a=i.scripts;n.database!==`none`&&(n.database===`sqlite`&&n.orm===`drizzle`&&(a[`db:local`]=`turso dev --db-file local.db`),n.orm===`prisma`?(a[`db:push`]=`prisma db push --schema ./prisma/schema`,a[`db:studio`]=`prisma studio`,a[`db:generate`]=`prisma generate --schema ./prisma/schema`,a[`db:migrate`]=`prisma migrate dev`):n.orm===`drizzle`&&(a[`db:push`]=`drizzle-kit push`,a[`db:studio`]=`drizzle-kit studio`,a[`db:generate`]=`drizzle-kit generate`,a[`db:migrate`]=`drizzle-kit migrate`)),await h.writeJson(r,i,{spaces:2})}async function ct(t,n){let r=e.join(t,`packages/backend/package.json`);if(!await h.pathExists(r))return;let i=await h.readJson(r);i.name=`@${n.projectName}/backend`,i.scripts||={},await h.writeJson(r,i,{spaces:2})}async function lt(e,t){if(!t)return;let n=await b({cwd:e,reject:!1,stderr:`pipe`})`git --version`;if(n.exitCode!==0){o.warn(g.yellow(`Git is not installed`));return}let r=await b({cwd:e,reject:!1,stderr:`pipe`})`git init`;if(r.exitCode!==0)throw Error(`Git initialization failed: ${r.stderr}`)}async function K(t,n,r){try{let i=await h.readFile(t,`utf-8`),a=w.compile(i),o=a(r);await h.ensureDir(e.dirname(n)),await h.writeFile(n,o)}catch(e){throw p.error(`Error processing template ${t}:`,e),Error(`Failed to process template ${t}`)}}w.registerHelper(`or`,(e,t)=>e||t),w.registerHelper(`eq`,(e,t)=>e===t),w.registerHelper(`includes`,(e,t)=>Array.isArray(e)&&e.includes(t));async function q(t,n,r,i,a=!0){let o=await C(t,{cwd:n,dot:!0,onlyFiles:!0,absolute:!1});for(let t of o){let o=e.join(n,t),s=t;t.endsWith(`.hbs`)&&(s=t.slice(0,-4));let c=e.basename(t);c===`_gitignore`?s=e.join(e.dirname(t),`.gitignore`):c===`_npmrc`&&(s=e.join(e.dirname(t),`.npmrc`));let l=e.join(r,s);try{if(await h.ensureDir(e.dirname(l)),!a&&await h.pathExists(l))continue;o.endsWith(`.hbs`)?await K(o,l,i):await h.copy(o,l,{overwrite:!0})}catch{}}}async function ut(t,n){let r=e.join(k,`templates/base`);await q([`**/*`],r,t,n),await h.ensureDir(e.join(t,`packages`))}async function dt(t,n){let r=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),i=n.frontend.includes(`nuxt`),a=n.frontend.includes(`svelte`),o=n.frontend.includes(`solid`),s=n.frontend.includes(`native-nativewind`),c=n.frontend.includes(`native-unistyles`),l=s||c,u=n.backend===`convex`;if(r||i||a||o){let s=e.join(t,`apps/web`);if(await h.ensureDir(s),r){let t=e.join(k,`templates/frontend/react/web-base`);await h.pathExists(t)&&await q(`**/*`,t,s,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/frontend/react/${r}`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/web/react/base`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}}else if(i){let t=e.join(k,`templates/frontend/nuxt`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}else if(a){let t=e.join(k,`templates/frontend/svelte`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}else if(o){let t=e.join(k,`templates/frontend/solid`);if(await h.pathExists(t)&&await q(`**/*`,t,s,n),!u&&n.api===`orpc`){let t=e.join(k,`templates/api/${n.api}/web/solid`);await h.pathExists(t)&&await q(`**/*`,t,s,n)}}}if(s||c){let r=e.join(t,`apps/native`);await h.ensureDir(r);let i=e.join(k,`templates/frontend/native/native-base`);await h.pathExists(i)&&await q(`**/*`,i,r,n);let a=``;s?a=`nativewind`:c&&(a=`unistyles`);let o=e.join(k,`templates/frontend/native/${a}`);if(await h.pathExists(o)&&await q(`**/*`,o,r,n,!0),!u&&(n.api===`trpc`||n.api===`orpc`)){let t=e.join(k,`templates/api/${n.api}/native`);await h.pathExists(t)&&await q(`**/*`,t,r,n)}}}async function ft(t,n){if(n.backend===`none`)return;let r=e.join(t,`apps/server`);if(n.backend===`convex`){await h.pathExists(r)&&await h.remove(r);let i=e.join(t,`packages/backend`),a=e.join(k,`templates/backend/convex/packages/backend`);await h.ensureDir(i),await h.pathExists(a)&&await q(`**/*`,a,i,n);return}await h.ensureDir(r);let i=e.join(k,`templates/backend/server/server-base`);await h.pathExists(i)&&await q(`**/*`,i,r,n);let a=e.join(k,`templates/backend/server/${n.backend}`);if(await h.pathExists(a)&&await q(`**/*`,a,r,n,!0),n.api!==`none`){let t=e.join(k,`templates/api/${n.api}/server/base`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!0);let i=e.join(k,`templates/api/${n.api}/server/${n.backend}`);await h.pathExists(i)&&await q(`**/*`,i,r,n,!0)}}async function pt(t,n){if(n.backend===`convex`||n.orm===`none`||n.database===`none`)return;let r=e.join(t,`apps/server`);await h.ensureDir(r);let i=e.join(k,`templates/db/${n.orm}/${n.database}`);await h.pathExists(i)&&await q(`**/*`,i,r,n)}async function mt(t,n){if(n.backend===`convex`||!n.auth)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=e.join(t,`apps/native`),o=await h.pathExists(r),s=await h.pathExists(i),c=await h.pathExists(a),l=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),u=n.frontend.includes(`nuxt`),d=n.frontend.includes(`svelte`),f=n.frontend.includes(`solid`),p=n.frontend.includes(`native-nativewind`),m=n.frontend.includes(`native-unistyles`),g=p||m;if(o){let t=e.join(k,`templates/auth/server/base`);if(await h.pathExists(t)&&await q(`**/*`,t,r,n),n.backend===`next`){let t=e.join(k,`templates/auth/server/next`);await h.pathExists(t)&&await q(`**/*`,t,r,n)}if(n.orm!==`none`&&n.database!==`none`){let t=n.orm,i=n.database,a=``;t===`drizzle`?a=e.join(k,`templates/auth/server/db/drizzle/${i}`):t===`prisma`?a=e.join(k,`templates/auth/server/db/prisma/${i}`):t===`mongoose`&&(a=e.join(k,`templates/auth/server/db/mongoose/${i}`)),a&&await h.pathExists(a)&&await q(`**/*`,a,r,n)}}if((l||u||d||f)&&s){if(l){let t=e.join(k,`templates/auth/web/react/base`);await h.pathExists(t)&&await q(`**/*`,t,i,n);let r=n.frontend.find(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e));if(r){let t=e.join(k,`templates/auth/web/react/${r}`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}else if(u){let t=e.join(k,`templates/auth/web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}else if(d){if(n.api===`orpc`){let t=e.join(k,`templates/auth/web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}else if(f&&n.api===`orpc`){let t=e.join(k,`templates/auth/web/solid`);await h.pathExists(t)&&await q(`**/*`,t,i,n)}}if(g&&c){let t=e.join(k,`templates/auth/native/native-base`);await h.pathExists(t)&&await q(`**/*`,t,a,n);let r=``;if(p?r=`nativewind`:m&&(r=`unistyles`),r){let t=e.join(k,`templates/auth/native/${r}`);await h.pathExists(t)&&await q(`**/*`,t,a,n)}}}async function ht(t,n){if(!(!n.addons||n.addons.length===0))for(let r of n.addons){if(r===`none`)continue;let i=e.join(k,`templates/addons/${r}`),a=t;if(r===`pwa`){let r=e.join(t,`apps/web`);if(!await h.pathExists(r))continue;if(a=r,n.frontend.includes(`next`))i=e.join(k,`templates/addons/pwa/apps/web/next`);else if(n.frontend.some(e=>[`tanstack-router`,`react-router`,`solid`].includes(e)))i=e.join(k,`templates/addons/pwa/apps/web/vite`);else continue}await h.pathExists(i)&&await q(`**/*`,i,a,n)}}async function gt(t,n){if(!n.examples||n.examples.length===0||n.examples[0]===`none`)return;let r=e.join(t,`apps/server`),i=e.join(t,`apps/web`),a=await h.pathExists(r),o=await h.pathExists(i),s=n.frontend.some(e=>[`tanstack-router`,`react-router`,`tanstack-start`,`next`].includes(e)),c=n.frontend.includes(`nuxt`),l=n.frontend.includes(`svelte`),u=n.frontend.includes(`solid`);for(let t of n.examples){if(t===`none`)continue;let d=e.join(k,`templates/examples/${t}`);if(a&&n.backend!==`convex`&&n.backend!==`none`){let i=e.join(d,`server`);if(t===`ai`&&n.backend===`next`){let t=e.join(i,`next`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!1)}if(n.orm!==`none`&&n.database!==`none`){let t=e.join(i,n.orm,`base`);await h.pathExists(t)&&await q(`**/*`,t,r,n,!1);let a=e.join(i,n.orm,n.database);await h.pathExists(a)&&await q(`**/*`,a,r,n,!1)}let a=[`${n.orm}/**`];t===`ai`&&n.backend===`next`&&a.push(`next/**`);let o=await C([`**/*.ts`,`**/*.hbs`],{cwd:i,onlyFiles:!0,deep:1,ignore:a});for(let t of o){let a=e.join(i,t),o=e.join(r,t.replace(`.hbs`,``));try{a.endsWith(`.hbs`)?await K(a,o,n):await h.pathExists(o)||await h.copy(a,o,{overwrite:!1})}catch{}}}if(o){if(s){let t=e.join(d,`web/react`);if(await h.pathExists(t)){let r=n.frontend.find(e=>[`next`,`react-router`,`tanstack-router`,`tanstack-start`].includes(e));if(r){let a=e.join(t,r);await h.pathExists(a)&&await q(`**/*`,a,i,n,!1)}}}else if(c){let t=e.join(d,`web/nuxt`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}else if(l){let t=e.join(d,`web/svelte`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}else if(u){let t=e.join(d,`web/solid`);await h.pathExists(t)&&await q(`**/*`,t,i,n,!1)}}}}async function _t(t,n){let r=e.join(k,`templates/extras`),i=n.frontend.includes(`native-nativewind`),a=n.frontend.includes(`native-unistyles`),o=i||a;if(n.packageManager===`pnpm`){let n=e.join(r,`pnpm-workspace.yaml`),i=e.join(t,`pnpm-workspace.yaml`);await h.pathExists(n)&&await h.copy(n,i)}if(n.packageManager===`pnpm`&&(o||n.frontend.includes(`nuxt`))){let i=e.join(r,`_npmrc.hbs`),a=e.join(t,`.npmrc`);await h.pathExists(i)&&await K(i,a,n)}}async function vt(e){let n=e.projectDir,r=e.backend===`convex`;try{return await h.ensureDir(n),await ut(n,e),await dt(n,e),await ft(n,e),r||(await pt(n,e),await mt(n,e)),e.examples.length>0&&e.examples[0]!==`none`&&await gt(n,e),await ht(n,e),await le(e),r||(await fe(e),await Re(e),await Be(e),e.examples.length>0&&e.examples[0]!==`none`&&await ze(e)),e.addons.length>0&&e.addons[0]!==`none`&&await ie(e),!r&&e.auth&&await ue(e),await _t(n,e),await pe(e),await at(n,e),await Ue(n,e),await lt(n,e.git),o.success(`Project template successfully scaffolded!`),e.install&&await Je({projectDir:n,packageManager:e.packageManager,addons:e.addons}),Xe({...e,depsInstalled:e.install}),n}catch(e){e instanceof Error?(t(g.red(`Error during project creation: ${e.message}`)),console.error(e.stack),process.exit(1)):(t(g.red(`An unexpected error occurred: ${String(e)}`)),console.error(e),process.exit(1))}}async function yt(e,n){if(e!==void 0)return e;let r=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`solid`)||n?.includes(`next`),i=n?.includes(`react-router`)||n?.includes(`tanstack-router`)||n?.includes(`nuxt`)||n?.includes(`svelte`)||n?.includes(`solid`)||n?.includes(`next`),o=[{value:`turborepo`,label:`Turborepo (Recommended)`,hint:`Optimize builds for monorepos`},{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:`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`}],c=o.filter(e=>e.value===`pwa`?r:e.value===`tauri`?i:!0),l=A.addons.filter(e=>c.some(t=>t.value===e)),u=await s({message:`Select addons`,options:c,initialValues:l,required:!1});return a(u)&&(t(g.red(`Operation cancelled`)),process.exit(0)),u.includes(`husky`)&&!u.includes(`biome`)&&u.push(`biome`),u}async function bt(e,n,r){if(r===`convex`||r===`none`)return`none`;if(e)return e;let i=n?.includes(`nuxt`),o=n?.includes(`svelte`),s=n?.includes(`solid`),c=[{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 layer (e.g. for full-stack frameworks like Next.js with Route Handlers)`}];(i||o||s)&&(c=[{value:`orpc`,label:`oRPC`,hint:`End-to-end type-safe APIs (Recommended for ${i?`Nuxt`:o?`Svelte`:`Solid`} frontend)`},{value:`none`,label:`None`,hint:`No API layer`}]);let l=await u({message:`Select API type`,options:c,initialValue:c[0].value});return a(l)&&(t(g.red(`Operation cancelled`)),process.exit(0)),l}async function xt(e,r,i){if(i===`convex`||!r)return!1;if(e!==void 0)return e;let o=await n({message:`Add authentication with Better-Auth?`,initialValue:A.auth});return a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),o}async function St(e,n){if(e!==void 0)return e;let r=n?.some(e=>e===`nuxt`||e===`solid`),i=[{value:`hono`,label:`Hono`,hint:`Lightweight, ultrafast web framework`},{value:`next`,label:`Next.js`,hint:`Nextjs API routes`},{value:`express`,label:`Express`,hint:`Fast, unopinionated, minimalist web framework for Node.js`},{value:`fastify`,label:`Fastify`,hint:`Fast, low-overhead web framework for Node.js`},{value:`elysia`,label:`Elysia`,hint:`Ergonomic web framework for building backend servers`}];r||i.push({value:`convex`,label:`Convex`,hint:`Reactive backend-as-a-service platform`}),i.push({value:`none`,label:`None`,hint:`No backend server`});let o=A.backend;r&&o===`convex`&&(o=`hono`);let s=await u({message:`Select backend`,options:i,initialValue:o});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Ct(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;let r=await u({message:`Select database`,options:[{value:`none`,label:`None`,hint:`No database setup`},{value:`sqlite`,label:`SQLite`,hint:`lightweight, server-less, embedded relational database`},{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:A.database});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function wt(e,n,r,i){if(i===`convex`)return`none`;if(n!==void 0)return n;if(e===`none`||e===`sqlite`&&r===`prisma`)return`none`;let o=[];if(e===`sqlite`)o=[{value:`turso`,label:`Turso`,hint:`SQLite for Production. Powered by libSQL`},{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`postgres`)o=[{value:`neon`,label:`Neon Postgres`,hint:`Serverless Postgres with branching capability`},{value:`supabase`,label:`Supabase`,hint:`Local Supabase stack (requires Docker)`},...r===`prisma`?[{value:`prisma-postgres`,label:`Prisma Postgres`,hint:`Instant Postgres for Global Applications`}]:[],{value:`none`,label:`None`,hint:`Manual setup`}];else if(e===`mongodb`)o=[{value:`mongodb-atlas`,label:`MongoDB Atlas`,hint:`The most effective way to deploy MongoDB`},{value:`none`,label:`None`,hint:`Manual setup`}];else return`none`;let s=await u({message:`Select ${e} setup option`,options:o,initialValue:`none`});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function Tt(e,n,r,i,o){if(o===`none`)return[];if(e!==void 0)return e;if(i===`convex`)return[`todo`];if(i===`none`||n===`none`)return[];let c=r&&r.length===1&&(r[0]===`native-nativewind`||r[0]===`native-unistyles`);if(c)return[];let l=r?.some(e=>[`react-router`,`tanstack-router`,`tanstack-start`,`next`,`nuxt`,`svelte`,`solid`].includes(e))??!1,u=!r||r.length===0;if(!l&&!u)return[];let d=[],f=[{value:`todo`,label:`Todo App`,hint:`A simple CRUD example app`}];return i!==`elysia`&&!r?.includes(`solid`)&&f.push({value:`ai`,label:`AI Chat`,hint:`A simple AI chat interface using AI SDK`}),d=await s({message:`Include examples`,options:f,required:!1,initialValues:A.examples}),a(d)&&(t(g.red(`Operation cancelled`)),process.exit(0)),d}async function Et(e,n){if(e!==void 0)return e;let r=await s({message:`Select platforms to develop for`,options:[{value:`web`,label:`Web`,hint:`React, Vue or Svelte Web Application`},{value:`native`,label:`Native`,hint:`Create a React Native/Expo app`}],required:!1,initialValues:[`web`]});a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0));let i=[];if(r.includes(`web`)){let e=[{value:`tanstack-router`,label:`TanStack Router`,hint:`Modern and scalable routing for React Applications`},{value:`react-router`,label:`React Router`,hint:`A user‑obsessed, standards‑focused, multi‑strategy router`},{value:`next`,label:`Next.js`,hint:`The React Framework for the Web`},{value:`nuxt`,label:`Nuxt`,hint:`The Progressive Web Framework for Vue.js`},{value:`svelte`,label:`Svelte`,hint:`web development for the rest of us`},{value:`solid`,label:`Solid`,hint:`Simple and performant reactivity for building user interfaces`},{value:`tanstack-start`,label:`TanStack Start (beta)`,hint:`SSR, Server Functions, API Routes and more with TanStack Router`}],r=e.filter(e=>n===`convex`?e.value!==`nuxt`&&e.value!==`solid`:!0),o=await u({message:`Choose frontend`,options:r,initialValue:A.frontend[0]});a(o)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(o)}if(r.includes(`native`)){let e=await u({message:`Choose native framework`,options:[{value:`native-nativewind`,label:`NativeWind`,hint:`Use Tailwind CSS for React Native`},{value:`native-unistyles`,label:`Unistyles`,hint:`Consistent styling for React Native`}],initialValue:`native-nativewind`});a(e)&&(t(g.red(`Operation cancelled`)),process.exit(0)),i.push(e)}return i}async function Dt(e){if(e!==void 0)return e;let r=await n({message:`Initialize git repository?`,initialValue:A.git});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Ot(e){if(e!==void 0)return e;let r=await n({message:`Install dependencies?`,initialValue:A.install});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}const J={prisma:{value:`prisma`,label:`Prisma`,hint:`Powerful, feature-rich ORM`},mongoose:{value:`mongoose`,label:`Mongoose`,hint:`Elegant object modeling tool`},drizzle:{value:`drizzle`,label:`Drizzle`,hint:`Lightweight and performant TypeScript ORM`}};async function kt(e,n,r,i){if(i===`convex`||!n)return`none`;if(e!==void 0)return e;let o=[...r===`mongodb`?[J.prisma,J.mongoose]:[J.drizzle,J.prisma]],s=await u({message:`Select ORM`,options:o,initialValue:r===`mongodb`?`prisma`:A.orm});return a(s)&&(t(g.red(`Operation cancelled`)),process.exit(0)),s}async function At(e){if(e!==void 0)return e;let n=E(),r=await u({message:`Choose package manager`,options:[{value:`npm`,label:`npm`,hint:`Node Package Manager`},{value:`pnpm`,label:`pnpm`,hint:`Fast, disk space efficient package manager`},{value:`bun`,label:`bun`,hint:`All-in-one JavaScript runtime & toolkit`}],initialValue:n});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function jt(e,n){if(n===`convex`||n===`none`)return`none`;if(e!==void 0)return e;if(n===`next`)return`node`;let r=await u({message:`Select runtime`,options:[{value:`bun`,label:`Bun`,hint:`Fast all-in-one JavaScript runtime`},{value:`node`,label:`Node.js`,hint:`Traditional Node.js runtime`}],initialValue:A.runtime});return a(r)&&(t(g.red(`Operation cancelled`)),process.exit(0)),r}async function Mt(e,n,i,a){let o=await r({frontend:()=>Et(e.frontend,e.backend),backend:({results:t})=>St(e.backend,t.frontend),runtime:({results:t})=>jt(e.runtime,t.backend),database:({results:t})=>Ct(e.database,t.backend),orm:({results:t})=>kt(e.orm,t.database!==`none`,t.database,t.backend),api:({results:t})=>bt(e.api,t.frontend,t.backend),auth:({results:t})=>xt(e.auth,t.database!==`none`,t.backend),addons:({results:t})=>yt(e.addons,t.frontend),examples:({results:t})=>Tt(e.examples,t.database,t.frontend,t.backend,t.api),dbSetup:({results:t})=>wt(t.database??`none`,e.dbSetup,t.orm,t.backend),git:()=>Dt(e.git),packageManager:()=>At(e.packageManager),install:()=>Ot(e.install)},{onCancel:()=>{t(g.red(`Operation cancelled`)),process.exit(0)}});return o.backend===`convex`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`,o.examples=[`todo`]),o.backend===`none`&&(o.runtime=`none`,o.database=`none`,o.orm=`none`,o.api=`none`,o.auth=!1,o.dbSetup=`none`,o.examples=[]),{projectName:n,projectDir:i,relativePath:a,frontend:o.frontend,backend:o.backend,runtime:o.runtime,database:o.database,orm:o.orm,auth:o.auth,addons:o.addons,examples:o.examples,git:o.git,packageManager:o.packageManager,install:o.install,dbSetup:o.dbSetup,api:o.api}}const Nt=[`<`,`>`,`:`,`"`,`|`,`?`,`*`],Y=255;function X(e){if(e!==`.`){if(!e)return`Project name cannot be empty`;if(e.length>Y)return`Project name must be less than ${Y} characters`;if(Nt.some(t=>e.includes(t)))return`Project name contains invalid characters`;if(e.startsWith(`.`)||e.startsWith(`-`))return`Project name cannot start with a dot or dash`;if(e.toLowerCase()===`node_modules`)return`Project name is reserved`}}async function Z(n){if(n){if(n===`.`)return n;let t=e.basename(n),r=X(t);if(!r)return n}let r=!1,i=``,o=A.projectName,s=1;for(;h.pathExistsSync(e.resolve(process.cwd(),o))&&h.readdirSync(e.resolve(process.cwd(),o)).length>0;)o=`${A.projectName}-${s}`,s++;for(;!r;){let s=await f({message:`Enter your project name or path (relative to current directory)`,placeholder:o,initialValue:n,defaultValue:o,validate:t=>{let n=t.trim()||o,r=e.basename(n),i=X(r);if(i)return i;if(n!==`.`){let t=e.resolve(process.cwd(),n);if(!t.startsWith(process.cwd()))return`Project path must be within current directory`}}});a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),i=s||o,r=!0}return i}function Q(e){let t=[];if(e.projectName&&t.push(`${g.blue(`Project Name:`)} ${e.projectName}`),e.frontend!==void 0){let n=Array.isArray(e.frontend)?e.frontend:[e.frontend],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Frontend:`)} ${r}`)}if(e.backend!==void 0&&t.push(`${g.blue(`Backend:`)} ${String(e.backend)}`),e.runtime!==void 0&&t.push(`${g.blue(`Runtime:`)} ${String(e.runtime)}`),e.api!==void 0&&t.push(`${g.blue(`API:`)} ${String(e.api)}`),e.database!==void 0&&t.push(`${g.blue(`Database:`)} ${String(e.database)}`),e.orm!==void 0&&t.push(`${g.blue(`ORM:`)} ${String(e.orm)}`),e.auth!==void 0){let n=typeof e.auth==`boolean`?e.auth?`Yes`:`No`:String(e.auth);t.push(`${g.blue(`Authentication:`)} ${n}`)}if(e.addons!==void 0){let n=Array.isArray(e.addons)?e.addons:[e.addons],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Addons:`)} ${r}`)}if(e.examples!==void 0){let n=Array.isArray(e.examples)?e.examples:[e.examples],r=n.length>0&&n[0]!==void 0?n.join(`, `):`none`;t.push(`${g.blue(`Examples:`)} ${r}`)}if(e.git!==void 0){let n=typeof e.git==`boolean`?e.git?`Yes`:`No`:String(e.git);t.push(`${g.blue(`Git Init:`)} ${n}`)}if(e.packageManager!==void 0&&t.push(`${g.blue(`Package Manager:`)} ${String(e.packageManager)}`),e.install!==void 0){let n=typeof e.install==`boolean`?e.install?`Yes`:`No`:String(e.install);t.push(`${g.blue(`Install Dependencies:`)} ${n}`)}return e.dbSetup!==void 0&&t.push(`${g.blue(`Database Setup:`)} ${String(e.dbSetup)}`),t.length===0?g.yellow(`No configuration selected.`):t.join(`
|
|
168
|
+
`)}function Pt(e){let t=[];e.frontend&&e.frontend.length>0?t.push(`--frontend ${e.frontend.join(` `)}`):t.push(`--frontend none`),t.push(`--backend ${e.backend}`),t.push(`--runtime ${e.runtime}`),t.push(`--database ${e.database}`),t.push(`--orm ${e.orm}`),t.push(`--api ${e.api}`),t.push(e.auth?`--auth`:`--no-auth`),e.addons&&e.addons.length>0?t.push(`--addons ${e.addons.join(` `)}`):t.push(`--addons none`),e.examples&&e.examples.length>0?t.push(`--examples ${e.examples.join(` `)}`):t.push(`--examples none`),t.push(`--db-setup ${e.dbSetup}`),t.push(e.git?`--git`:`--no-git`),t.push(`--package-manager ${e.packageManager}`),t.push(e.install?`--install`:`--no-install`);let n=``,r=e.packageManager;r===`npm`?n=`npx create-better-t-stack@latest`:r===`pnpm`?n=`pnpm create better-t-stack@latest`:r===`bun`&&(n=`bun create better-t-stack@latest`);let i=e.relativePath?` ${e.relativePath}`:``;return`${n}${i} ${t.join(` `)}`}const $=`
|
|
169
169
|
██████╗ ███████╗████████╗████████╗███████╗██████╗
|
|
170
170
|
██╔══██╗██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗
|
|
171
171
|
██████╔╝█████╗ ██║ ██║ █████╗ ██████╔╝
|
|
@@ -179,9 +179,9 @@ DATABASE_URL="your_connection_string"`)}async function Be(n){let{packageManager:
|
|
|
179
179
|
██║ ╚════██║ ██║ ██╔══██║██║ ██╔═██╗
|
|
180
180
|
██║ ███████║ ██║ ██║ ██║╚██████╗██║ ██╗
|
|
181
181
|
╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
|
|
182
|
-
`,
|
|
182
|
+
`,Ft={pink:`#F5C2E7`,mauve:`#CBA6F7`,red:`#F38BA8`,maroon:`#E78284`,peach:`#FAB387`,yellow:`#F9E2AF`,green:`#A6E3A1`,teal:`#94E2D5`,sky:`#89DCEB`,sapphire:`#74C7EC`,lavender:`#B4BEFE`},It=()=>{let e=process.stdout.columns||80,t=$.split(`
|
|
183
183
|
`),n=Math.max(...t.map(e=>e.length));if(e<n){let e=`
|
|
184
184
|
╔══════════════════╗
|
|
185
185
|
║ Better T Stack ║
|
|
186
186
|
╚══════════════════╝
|
|
187
|
-
`;console.log(T(Object.values(Pt)).multiline(e))}else console.log(T(Object.values(Pt)).multiline($))},It=()=>process.exit(0);process.on(`SIGINT`,It),process.on(`SIGTERM`,It);async function Lt(){let n=Date.now();try{let r=await _(v(process.argv)).scriptName(`create-better-t-stack`).usage(`$0 [project-directory] [options]`,`Create a new Better-T Stack project`).positional(`project-directory`,{describe:`Project name/directory`,type:`string`}).option(`yes`,{alias:`y`,type:`boolean`,describe:`Use default configuration and skip prompts`,default:!1}).option(`database`,{type:`string`,describe:`Database type`,choices:[`none`,`sqlite`,`postgres`,`mysql`,`mongodb`]}).option(`orm`,{type:`string`,describe:`ORM type`,choices:[`drizzle`,`prisma`,`mongoose`,`none`]}).option(`auth`,{type:`boolean`,describe:`Include authentication (use --no-auth to exclude)`}).option(`frontend`,{type:`array`,string:!0,describe:`Frontend types`,choices:[`tanstack-router`,`react-router`,`tanstack-start`,`next`,`nuxt`,`native-nativewind`,`native-unistyles`,`svelte`,`solid`,`none`]}).option(`addons`,{type:`array`,string:!0,describe:`Additional addons`,choices:[`pwa`,`tauri`,`starlight`,`biome`,`husky`,`turborepo`,`none`]}).option(`examples`,{type:`array`,string:!0,describe:`Examples to include`,choices:[`todo`,`ai`,`none`]}).option(`git`,{type:`boolean`,describe:`Initialize git repository (use --no-git to skip)`}).option(`package-manager`,{alias:`pm`,type:`string`,describe:`Package manager`,choices:[`npm`,`pnpm`,`bun`]}).option(`install`,{type:`boolean`,describe:`Install dependencies (use --no-install to skip)`}).option(`db-setup`,{type:`string`,describe:`Database setup`,choices:[`turso`,`neon`,`prisma-postgres`,`mongodb-atlas`,`supabase`,`none`]}).option(`backend`,{type:`string`,describe:`Backend framework`,choices:[`hono`,`express`,`fastify`,`next`,`elysia`,`convex`,`none`]}).option(`runtime`,{type:`string`,describe:`Runtime`,choices:[`bun`,`node`,`none`]}).option(`api`,{type:`string`,describe:`API type`,choices:[`trpc`,`orpc`,`none`]}).completion().recommendCommands().version(Nt()).alias(`version`,`v`).help().alias(`help`,`h`).strict().wrap(null).parse(),s=r,l=s.projectDirectory;Ft(),i(g.magenta(`Creating a new Better-T-Stack project`));let f,p,y,b,x=!1;if(s.yes&&l)f=l;else if(s.yes){let t=A.relativePath,n=1;for(;h.pathExistsSync(e.resolve(process.cwd(),t))&&h.readdirSync(e.resolve(process.cwd(),t)).length>0;)t=`${A.projectName}-${n}`,n++;f=t}else f=await Z(l);for(;;){let n=e.resolve(process.cwd(),f),r=h.pathExistsSync(n),i=r&&h.readdirSync(n).length>0;if(!i){p=f,x=!1;break}o.warn(`Directory "${g.yellow(f)}" already exists and is not empty.`);let s=await u({message:`What would you like to do?`,options:[{value:`overwrite`,label:`Overwrite`,hint:`Empty the directory and create the project`},{value:`merge`,label:`Merge`,hint:`Create project files inside, potentially overwriting conflicts`},{value:`rename`,label:`Choose a different name/path`,hint:`Keep the existing directory and create a new one`},{value:`cancel`,label:`Cancel`,hint:`Abort the process`}],initialValue:`rename`});if(a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),s===`overwrite`){p=f,x=!0;break}if(s===`merge`){p=f,x=!1,o.info(`Proceeding into existing directory "${g.yellow(f)}". Files may be overwritten.`);break}s===`rename`?(o.info(`Please choose a different project name or path.`),f=await Z(void 0)):s===`cancel`&&(t(g.red(`Operation cancelled.`)),process.exit(0))}if(p===`.`?(y=process.cwd(),b=e.basename(y)):(y=e.resolve(process.cwd(),p),b=e.basename(y)),x){let e=d();e.start(`Clearing directory "${y}"...`);try{await h.emptyDir(y),e.stop(`Directory "${y}" cleared.`)}catch(t){e.stop(g.red(`Failed to clear directory "${y}".`)),m.error(t),process.exit(1)}}else await h.ensureDir(y);let S=Rt(s,b),{projectName:C,...w}=S;!s.yes&&Object.keys(w).length>0&&(o.info(g.yellow(`Using these pre-selected options:`)),o.message(Q(w)),o.message(``));let T;s.yes?(T={...A,...S,projectName:b,projectDir:y,relativePath:p},T.backend===`convex`?(T.auth=!1,T.database=`none`,T.orm=`none`,T.api=`none`,T.runtime=`none`,T.dbSetup=`none`,T.examples=[`todo`],o.info(`Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`)):T.backend===`none`?(T.auth=!1,T.database=`none`,T.orm=`none`,T.api=`none`,T.runtime=`none`,T.dbSetup=`none`,T.examples=[],o.info(`Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none`)):T.database===`none`&&(T.orm=`none`,o.info(`Due to '--database none', '--orm' has been automatically set to 'none'.`),T.auth=!1,o.info(`Due to '--database none', '--auth' has been automatically set to 'false'.`),T.dbSetup=`none`,o.info(`Due to '--database none', '--db-setup' has been automatically set to 'none'.`)),o.info(g.yellow(`Using default/flag options (config prompts skipped):`)),o.message(Q(T)),o.message(``)):T=await At(S,b,y,p),await gt(T),o.success(g.blue(`You can reproduce this setup with the following command:\n${Mt(T)}`));let E=((Date.now()-n)/1e3).toFixed(2);c(g.magenta(`Project created successfully in ${g.bold(E)} seconds!`))}catch(e){e instanceof Error?(e.name===`YError`?t(g.red(`Invalid arguments: ${e.message}`)):(m.error(`An unexpected error occurred: ${e.message}`),e.message.includes(`is only supported with`)||m.error(e.stack)),process.exit(1)):(m.error(`An unexpected error occurred.`),console.error(e),process.exit(1))}}function Rt(t,n){let r={},i=new Set(Object.keys(t).filter(e=>e!==`_`&&e!==`$0`));if(t.api&&(r.api=t.api,t.api===`none`&&t.examples&&!(t.examples.length===1&&t.examples[0]===`none`)&&(m.fatal(`Cannot use '--examples' when '--api' is set to 'none'. Please remove the --examples flag or choose an API type.`),process.exit(1))),t.backend&&(r.backend=t.backend),i.has(`backend`)&&r.backend&&r.backend!==`convex`&&r.backend!==`none`&&i.has(`runtime`)&&t.runtime===`none`&&(m.fatal(`'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.`),process.exit(1)),t.database&&(r.database=t.database),t.orm&&(r.orm=t.orm),t.auth!==void 0&&(r.auth=t.auth),t.git!==void 0&&(r.git=t.git),t.install!==void 0&&(r.install=t.install),t.runtime&&(r.runtime=t.runtime),t.dbSetup&&(r.dbSetup=t.dbSetup),t.packageManager&&(r.packageManager=t.packageManager),n?r.projectName=n:t.projectDirectory&&(r.projectName=e.basename(e.resolve(process.cwd(),t.projectDirectory))),t.frontend&&t.frontend.length>0)if(t.frontend.includes(`none`))t.frontend.length>1&&(m.fatal(`Cannot combine 'none' with other frontend options.`),process.exit(1)),r.frontend=[];else{let e=t.frontend.filter(e=>e!==`none`),n=e.filter(e=>e===`tanstack-router`||e===`react-router`||e===`tanstack-start`||e===`next`||e===`nuxt`||e===`svelte`||e===`solid`),i=e.filter(e=>e===`native-nativewind`||e===`native-unistyles`);n.length>1&&(m.fatal(`Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid`),process.exit(1)),i.length>1&&(m.fatal(`Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles`),process.exit(1)),r.frontend=e}if(t.addons&&t.addons.length>0&&(t.addons.includes(`none`)?(t.addons.length>1&&(m.fatal(`Cannot combine 'none' with other addons.`),process.exit(1)),r.addons=[]):r.addons=t.addons.filter(e=>e!==`none`)),t.examples&&t.examples.length>0&&(t.examples.includes(`none`)?(t.examples.length>1&&(m.fatal(`Cannot combine 'none' with other examples.`),process.exit(1)),r.examples=[]):(r.examples=t.examples.filter(e=>e!==`none`),t.examples.includes(`none`)&&r.backend!==`convex`&&(r.examples=[]))),r.backend===`convex`){let e=[];if(i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend convex': ${e.join(`, `)}. Please remove them.`),process.exit(1)),i.has(`frontend`)&&t.frontend){let e=t.frontend.filter(e=>e===`nuxt`||e===`solid`);e.length>0&&(m.fatal(`The following frontends are not compatible with '--backend convex': ${e.join(`, `)}. Please choose a different frontend or backend.`),process.exit(1))}r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,r.examples=[`todo`]}else if(r.backend===`none`){let e=[];i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend none': ${e.join(`, `)}. Please remove them.`),process.exit(1)),r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,t.examples&&!t.examples.includes(`none`)&&t.examples.length>0&&(m.fatal(`Cannot select examples when backend is 'none'. Please remove the --examples flag or set --examples none.`),process.exit(1)),r.examples=[]}else{let e=r.database??(t.yes?A.database:void 0),n=r.orm??(t.yes?A.orm:void 0),a=r.auth??(t.yes?A.auth:void 0),s=r.dbSetup??(t.yes?A.dbSetup:void 0),c=r.examples??(t.yes?A.examples:void 0),l=r.frontend??(t.yes?A.frontend:void 0),u=r.api??(t.yes?A.api:void 0),d=r.backend??(t.yes?A.backend:void 0);if(e===`none`&&(i.has(`orm`)&&t.orm!==`none`&&(m.fatal(`Cannot use ORM '--orm ${t.orm}' when database is 'none'.`),process.exit(1)),r.orm=`none`,o.info(`Due to '--database none', '--orm' has been automatically set to 'none'.`),i.has(`auth`)&&t.auth===!0&&(m.fatal(`Authentication requires a database. Cannot use --auth when database is 'none'.`),process.exit(1)),r.auth=!1,o.info(`Due to '--database none', '--auth' has been automatically set to 'false'.`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&(m.fatal(`Database setup '--db-setup ${t.dbSetup}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),r.dbSetup=`none`,o.info(`Due to '--database none', '--db-setup' has been automatically set to 'none'.`)),r.orm===`mongoose`&&!i.has(`database`)&&(e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.database=`mongodb`),e===`mongodb`&&n===`drizzle`&&(m.fatal(`Drizzle ORM is not compatible with MongoDB. Please use --orm prisma or --orm mongoose.`),process.exit(1)),n===`mongoose`&&e&&e!==`mongodb`&&(m.fatal(`Mongoose ORM requires MongoDB. Cannot use --orm mongoose with --database ${e}.`),process.exit(1)),r.dbSetup&&r.dbSetup!==`none`){let t=r.dbSetup;(!e||e===`none`)&&(m.fatal(`Database setup '--db-setup ${t}' requires a database. Cannot use when database is 'none'.`),process.exit(1)),t===`turso`?(e&&e!==`sqlite`&&(m.fatal(`Turso setup requires SQLite. Cannot use --db-setup turso with --database ${e}`),process.exit(1)),n!==`drizzle`&&(m.fatal(`Turso setup requires Drizzle ORM. Cannot use --db-setup turso with --orm ${n??`none`}.`),process.exit(1))):t===`supabase`?e!==`postgres`&&(m.fatal(`Supabase setup requires PostgreSQL. Cannot use --db-setup supabase with --database ${e}.`),process.exit(1)):t===`prisma-postgres`?(e!==`postgres`&&(m.fatal(`Prisma PostgreSQL setup requires PostgreSQL. Cannot use --db-setup prisma-postgres with --database ${e}.`),process.exit(1)),n!==`prisma`&&(m.fatal(`Prisma PostgreSQL setup requires Prisma ORM. Cannot use --db-setup prisma-postgres with --orm ${n}.`),process.exit(1))):t===`mongodb-atlas`?(e!==`mongodb`&&(m.fatal(`MongoDB Atlas setup requires MongoDB. Cannot use --db-setup mongodb-atlas with --database ${e}.`),process.exit(1)),n!==`prisma`&&n!==`mongoose`&&(m.fatal(`MongoDB Atlas setup requires Prisma or Mongoose ORM. Cannot use --db-setup mongodb-atlas with --orm ${n}.`),process.exit(1))):t===`neon`&&e!==`postgres`&&(m.fatal(`Neon PostgreSQL setup requires PostgreSQL. Cannot use --db-setup neon with --database ${e}.`),process.exit(1))}let f=l?.includes(`nuxt`),p=l?.includes(`svelte`),h=l?.includes(`solid`);if((f||p||h)&&u===`trpc`&&(m.fatal(`tRPC API is not supported with '${f?`nuxt`:p?`svelte`:`solid`}' frontend. Please use --api orpc or --api none or remove '${f?`nuxt`:p?`svelte`:`solid`}' from --frontend.`),process.exit(1)),r.addons&&r.addons.length>0){let e=[`pwa`,`tauri`],t=r.addons.some(t=>e.includes(t)),n=l?.some(e=>{let t=e===`tanstack-router`||e===`react-router`||e===`solid`||e===`next`,n=e===`tanstack-router`||e===`react-router`||e===`nuxt`||e===`svelte`||e===`solid`||e===`next`;return r.addons?.includes(`pwa`)&&r.addons?.includes(`tauri`)?t&&n:r.addons?.includes(`pwa`)?t:r.addons?.includes(`tauri`)?n:!0});if(t&&!n){let e=`Selected frontend is not compatible.`;r.addons.includes(`pwa`)&&(e=`PWA requires tanstack-router, react-router, next, or solid.`),r.addons.includes(`tauri`)&&(e=`Tauri requires tanstack-router, react-router, nuxt, svelte, solid, or next.`),m.fatal(`Incompatible addon/frontend combination: ${e}`),process.exit(1)}r.addons.includes(`husky`)&&!r.addons.includes(`biome`)&&m.warn(`Husky addon is recommended to be used with Biome for lint-staged configuration.`),r.addons=[...new Set(r.addons)]}let g=l&&l.length===1&&(l[0]===`native-nativewind`||l[0]===`native-unistyles`);g&&r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(m.fatal(`Examples are not supported when only a native frontend (NativeWind or Unistyles) is selected.`),process.exit(1)),r.examples&&r.examples.length>0&&!r.examples.includes(`none`)&&(r.examples.includes(`todo`)&&d!==`convex`&&d!==`none`&&e===`none`&&(m.fatal(`The 'todo' example requires a database if a backend (other than Convex) is present. Cannot use --examples todo when database is 'none' and a backend is selected.`),process.exit(1)),r.examples.includes(`ai`)&&d===`elysia`&&(m.fatal(`The 'ai' example is not compatible with the Elysia backend.`),process.exit(1)),r.examples.includes(`ai`)&&h&&(m.fatal(`The 'ai' example is not compatible with the Solid frontend.`),process.exit(1)))}return r}Lt().catch(e=>{m.error(`Aborting installation due to unexpected error...`),e instanceof Error?!e.message.includes(`is only supported with`)&&!e.message.includes(`incompatible with`)&&!e.message.includes(`requires`)&&!e.message.includes(`Cannot use`)&&!e.message.includes(`Cannot select multiple`)&&!e.message.includes(`Cannot combine`)&&!e.message.includes(`not supported`)&&(m.error(e.message),m.error(e.stack)):console.error(e),process.exit(1)});
|
|
187
|
+
`;console.log(T(Object.values(Ft)).multiline(e))}else console.log(T(Object.values(Ft)).multiline($))};function Lt(t,n){let r={},i=new Set(Object.keys(t).filter(e=>e!==`_`&&e!==`$0`));if(t.api&&(r.api=t.api,t.api===`none`&&t.examples&&!(t.examples.length===1&&t.examples[0]===`none`)&&(m.fatal(`Cannot use '--examples' when '--api' is set to 'none'. Please remove the --examples flag or choose an API type.`),process.exit(1))),t.backend&&(r.backend=t.backend),i.has(`backend`)&&r.backend&&r.backend!==`convex`&&r.backend!==`none`&&i.has(`runtime`)&&t.runtime===`none`&&(m.fatal(`'--runtime none' is only supported with '--backend convex' or '--backend none'. Please choose 'bun', 'node', or remove the --runtime flag.`),process.exit(1)),t.database&&(r.database=t.database),t.orm&&(r.orm=t.orm),t.auth!==void 0&&(r.auth=t.auth),t.git!==void 0&&(r.git=t.git),t.install!==void 0&&(r.install=t.install),t.runtime&&(r.runtime=t.runtime),t.dbSetup&&(r.dbSetup=t.dbSetup),t.packageManager&&(r.packageManager=t.packageManager),n?r.projectName=n:t.projectDirectory&&(r.projectName=e.basename(e.resolve(process.cwd(),t.projectDirectory))),t.frontend&&t.frontend.length>0)if(t.frontend.includes(`none`))t.frontend.length>1&&(m.fatal(`Cannot combine 'none' with other frontend options.`),process.exit(1)),r.frontend=[];else{let e=t.frontend.filter(e=>e!==`none`),n=e.filter(e=>e===`tanstack-router`||e===`react-router`||e===`tanstack-start`||e===`next`||e===`nuxt`||e===`svelte`||e===`solid`),i=e.filter(e=>e===`native-nativewind`||e===`native-unistyles`);n.length>1&&(m.fatal(`Cannot select multiple web frameworks. Choose only one of: tanstack-router, tanstack-start, react-router, next, nuxt, svelte, solid`),process.exit(1)),i.length>1&&(m.fatal(`Cannot select multiple native frameworks. Choose only one of: native-nativewind, native-unistyles`),process.exit(1)),r.frontend=e}if(t.addons&&t.addons.length>0&&(t.addons.includes(`none`)?(t.addons.length>1&&(m.fatal(`Cannot combine 'none' with other addons.`),process.exit(1)),r.addons=[]):r.addons=t.addons.filter(e=>e!==`none`)),t.examples&&t.examples.length>0&&(t.examples.includes(`none`)?(t.examples.length>1&&(m.fatal(`Cannot combine 'none' with other examples.`),process.exit(1)),r.examples=[]):(r.examples=t.examples.filter(e=>e!==`none`),t.examples.includes(`none`)&&r.backend!==`convex`&&(r.examples=[]))),r.backend===`convex`){let e=[];if(i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend convex': ${e.join(`, `)}. Please remove them.`),process.exit(1)),i.has(`frontend`)&&t.frontend){let e=t.frontend.filter(e=>e===`nuxt`||e===`solid`);e.length>0&&(m.fatal(`The following frontends are not compatible with '--backend convex': ${e.join(`, `)}. Please choose a different frontend or backend.`),process.exit(1))}r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,r.examples=[`todo`]}else if(r.backend===`none`){let e=[];i.has(`auth`)&&t.auth===!0&&e.push(`--auth`),i.has(`database`)&&t.database!==`none`&&e.push(`--database ${t.database}`),i.has(`orm`)&&t.orm!==`none`&&e.push(`--orm ${t.orm}`),i.has(`api`)&&t.api!==`none`&&e.push(`--api ${t.api}`),i.has(`runtime`)&&t.runtime!==`none`&&e.push(`--runtime ${t.runtime}`),i.has(`dbSetup`)&&t.dbSetup!==`none`&&e.push(`--db-setup ${t.dbSetup}`),e.length>0&&(m.fatal(`The following flags are incompatible with '--backend none': ${e.join(`, `)}. Please remove them.`),process.exit(1)),r.auth=!1,r.database=`none`,r.orm=`none`,r.api=`none`,r.runtime=`none`,r.dbSetup=`none`,r.examples=[]}else r.database===`none`&&(i.has(`orm`)&&t.orm!==`none`&&(m.fatal(`'--orm ${t.orm}' is incompatible with '--database none'. Please use '--orm none' or choose a database.`),process.exit(1)),i.has(`auth`)&&t.auth===!0&&(m.fatal(`'--auth' requires a database. Cannot use '--auth' with '--database none'.`),process.exit(1)),i.has(`dbSetup`)&&t.dbSetup!==`none`&&(m.fatal(`'--db-setup ${t.dbSetup}' requires a database. Cannot use with '--database none'.`),process.exit(1)),r.orm=`none`,r.auth=!1,r.dbSetup=`none`,o.info(`Due to '--database none', '--orm' has been automatically set to 'none'.`),o.info(`Due to '--database none', '--auth' has been automatically set to 'false'.`),o.info(`Due to '--database none', '--db-setup' has been automatically set to 'none'.`)),r.orm===`mongoose`&&(i.has(`database`)?r.database!==`mongodb`&&(m.fatal(`'--orm mongoose' requires '--database mongodb'. Cannot use '--orm mongoose' with '--database ${r.database}'.`),process.exit(1)):(r.database=`mongodb`,o.info(`Due to '--orm mongoose', '--database' has been automatically set to 'mongodb'.`))),r.dbSetup&&(r.dbSetup===`turso`?(i.has(`database`)?r.database!==`sqlite`&&(m.fatal(`'--db-setup turso' requires '--database sqlite'. Cannot use with '--database ${r.database}'.`),process.exit(1)):(r.database=`sqlite`,o.info(`Due to '--db-setup turso', '--database' has been automatically set to 'sqlite'.`)),i.has(`orm`)?r.orm!==`drizzle`&&(m.fatal(`'--db-setup turso' requires '--orm drizzle'. Cannot use with '--orm ${r.orm}'.`),process.exit(1)):(r.orm=`drizzle`,o.info(`Due to '--db-setup turso', '--orm' has been automatically set to 'drizzle'.`))):r.dbSetup===`prisma-postgres`?(i.has(`database`)?r.database!==`postgres`&&(m.fatal(`'--db-setup prisma-postgres' requires '--database postgres'. Cannot use with '--database ${r.database}'.`),process.exit(1)):(r.database=`postgres`,o.info(`Due to '--db-setup prisma-postgres', '--database' has been automatically set to 'postgres'.`)),i.has(`orm`)?r.orm!==`prisma`&&(m.fatal(`'--db-setup prisma-postgres' requires '--orm prisma'. Cannot use with '--orm ${r.orm}'.`),process.exit(1)):(r.orm=`prisma`,o.info(`Due to '--db-setup prisma-postgres', '--orm' has been automatically set to 'prisma'.`))):r.dbSetup===`supabase`?i.has(`database`)?r.database!==`postgres`&&(m.fatal(`'--db-setup supabase' requires '--database postgres'. Cannot use with '--database ${r.database}'.`),process.exit(1)):(r.database=`postgres`,o.info(`Due to '--db-setup supabase', '--database' has been automatically set to 'postgres'.`)):r.dbSetup===`neon`?i.has(`database`)?r.database!==`postgres`&&(m.fatal(`'--db-setup neon' requires '--database postgres'. Cannot use with '--database ${r.database}'.`),process.exit(1)):(r.database=`postgres`,o.info(`Due to '--db-setup neon', '--database' has been automatically set to 'postgres'.`)):r.dbSetup===`mongodb-atlas`&&(i.has(`database`)?r.database!==`mongodb`&&(m.fatal(`'--db-setup mongodb-atlas' requires '--database mongodb'. Cannot use with '--database ${r.database}'.`),process.exit(1)):(r.database=`mongodb`,o.info(`Due to '--db-setup mongodb-atlas', '--database' has been automatically set to 'mongodb'.`)))),r.database===`mongodb`&&r.orm===`drizzle`&&(m.fatal(`'--database mongodb' is incompatible with '--orm drizzle'. Use '--orm mongoose' or '--orm prisma' with MongoDB.`),process.exit(1));return r}const Rt=()=>process.exit(0);process.on(`SIGINT`,Rt),process.on(`SIGTERM`,Rt);async function zt(){let n=Date.now();try{let r=await te(),s=r.projectDirectory;It(),i(g.magenta(`Creating a new Better-T-Stack project`));let l,f,p,_,v=!1;if(r.yes&&s)l=s;else if(r.yes){let t=A.relativePath,n=1;for(;h.pathExistsSync(e.resolve(process.cwd(),t))&&h.readdirSync(e.resolve(process.cwd(),t)).length>0;)t=`${A.projectName}-${n}`,n++;l=t}else l=await Z(s);for(;;){let n=e.resolve(process.cwd(),l),r=h.pathExistsSync(n),i=r&&h.readdirSync(n).length>0;if(!i){f=l,v=!1;break}o.warn(`Directory "${g.yellow(l)}" already exists and is not empty.`);let s=await u({message:`What would you like to do?`,options:[{value:`overwrite`,label:`Overwrite`,hint:`Empty the directory and create the project`},{value:`merge`,label:`Merge`,hint:`Create project files inside, potentially overwriting conflicts`},{value:`rename`,label:`Choose a different name/path`,hint:`Keep the existing directory and create a new one`},{value:`cancel`,label:`Cancel`,hint:`Abort the process`}],initialValue:`rename`});if(a(s)&&(t(g.red(`Operation cancelled.`)),process.exit(0)),s===`overwrite`){f=l,v=!0;break}if(s===`merge`){f=l,v=!1,o.info(`Proceeding into existing directory "${g.yellow(l)}". Files may be overwritten.`);break}s===`rename`?(o.info(`Please choose a different project name or path.`),l=await Z(void 0)):s===`cancel`&&(t(g.red(`Operation cancelled.`)),process.exit(0))}if(f===`.`?(p=process.cwd(),_=e.basename(p)):(p=e.resolve(process.cwd(),f),_=e.basename(p)),v){let e=d();e.start(`Clearing directory "${p}"...`);try{await h.emptyDir(p),e.stop(`Directory "${p}" cleared.`)}catch(t){e.stop(g.red(`Failed to clear directory "${p}".`)),m.error(t),process.exit(1)}}else await h.ensureDir(p);let y=Lt(r,_),{projectName:b,...x}=y;!r.yes&&Object.keys(x).length>0&&(o.info(g.yellow(`Using these pre-selected options:`)),o.message(Q(x)),o.message(``));let S;r.yes?(S={...A,...y,projectName:_,projectDir:p,relativePath:f},S.backend===`convex`?o.info(`Due to '--backend convex' flag, the following options have been automatically set: auth=false, database=none, orm=none, api=none, runtime=none, dbSetup=none, examples=todo`):S.backend===`none`&&o.info(`Due to '--backend none', the following options have been automatically set: --auth=false, --database=none, --orm=none, --api=none, --runtime=none, --db-setup=none, --examples=none`),o.info(g.yellow(`Using default/flag options (config prompts skipped):`)),o.message(Q(S)),o.message(``)):S=await Mt(y,_,p,f),await vt(S),o.success(g.blue(`You can reproduce this setup with the following command:\n${Pt(S)}`));let C=((Date.now()-n)/1e3).toFixed(2);c(g.magenta(`Project created successfully in ${g.bold(C)} seconds!`))}catch(e){e instanceof Error?(e.name===`YError`?t(g.red(`Invalid arguments: ${e.message}`)):(m.error(`An unexpected error occurred: ${e.message}`),e.message.includes(`is only supported with`)||m.error(e.stack)),process.exit(1)):(m.error(`An unexpected error occurred.`),console.error(e),process.exit(1))}}zt().catch(e=>{m.error(`Aborting installation due to unexpected error...`),e instanceof Error?!e.message.includes(`is only supported with`)&&!e.message.includes(`incompatible with`)&&!e.message.includes(`requires`)&&!e.message.includes(`Cannot use`)&&!e.message.includes(`Cannot select multiple`)&&!e.message.includes(`Cannot combine`)&&!e.message.includes(`not supported`)&&(m.error(e.message),m.error(e.stack)):console.error(e),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "2.13.
|
|
3
|
+
"version": "2.13.3",
|
|
4
4
|
"description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|