create-better-t-stack 1.0.9 → 1.0.10
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/dist/index.js +8 -8
- package/package.json +5 -5
- package/template/base/package.json +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{cancel as
|
|
2
|
+
import{cancel as Ra,intro as za,log as k,outro as La,spinner as Na}from"@clack/prompts";import{Command as Fa}from"commander";import $ from"picocolors";import K from"node:path";import{fileURLToPath as Ve}from"node:url";var He=Ve(import.meta.url),We=K.dirname(He),f=K.join(We,"../"),d={projectName:"my-better-t-app",database:"sqlite",orm:"drizzle",auth:!0,addons:[],git:!0,packageManager:"npm",noInstall:!1,examples:["todo"]},B={"better-auth":"^1.2.4","drizzle-orm":"^0.38.4","drizzle-kit":"^0.30.5","@libsql/client":"^0.14.0",postgres:"^3.4.5","@prisma/client":"^6.5.0",prisma:"^6.5.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"};import Nt from"node:path";import{cancel as Ft,spinner as Ut}from"@clack/prompts";import _t from"fs-extra";import je from"picocolors";import S from"node:path";import m from"fs-extra";import Qe from"node:path";import X from"fs-extra";var h=e=>{let{dependencies:t=[],devDependencies:a=[],projectDir:o}=e,r=Qe.join(o,"package.json"),s=X.readJSONSync(r);s.dependencies||(s.dependencies={}),s.devDependencies||(s.devDependencies={});for(let i of t){let n=B[i];s.dependencies[i]=n}for(let i of a){let n=B[i];s.devDependencies[i]=n}X.writeJSONSync(r,s,{spaces:2})};import N from"node:path";import{log as Ke,spinner as Xe}from"@clack/prompts";import{execa as Ye}from"execa";import G from"fs-extra";import Y from"picocolors";async function Z(e,t){let a=Xe(),o=N.join(e,"apps/client");try{a.start("Setting up Tauri desktop app support..."),h({devDependencies:["@tauri-apps/cli"],projectDir:o});let r=N.join(o,"package.json");if(await G.pathExists(r)){let n=await G.readJson(r);n.scripts={...n.scripts,tauri:"tauri","desktop:dev":"tauri dev","desktop:build":"tauri build"},await G.writeJson(r,n,{spaces:2})}let s,i;switch(t){case"npm":s="npx",i=["@tauri-apps/cli@latest"];break;case"pnpm":s="pnpm",i=["dlx","@tauri-apps/cli@latest"];break;case"bun":s="bunx",i=["@tauri-apps/cli@latest"];break;default:s="npx",i=["@tauri-apps/cli@latest"]}i=[...i,"init",`--app-name=${N.basename(e)}`,`--window-title=${N.basename(e)}`,"--frontend-dist=dist","--dev-url=http://localhost:3001",`--before-dev-command=${t} run dev`,`--before-build-command=${t} run build`],await Ye(s,i,{cwd:o,env:{CI:"true"}}),a.stop("Tauri desktop app support configured successfully!")}catch(r){throw a.stop(Y.red("Failed to set up Tauri")),r instanceof Error&&Ke.error(Y.red(r.message)),r}}async function ee(e,t,a){t.includes("pwa")&&await tt(e),t.includes("tauri")&&await Z(e,a),t.includes("biome")&&await Ze(e),t.includes("husky")&&await et(e)}async function Ze(e){let t=S.join(f,"template/with-biome");await m.pathExists(t)&&await m.copy(t,e,{overwrite:!0}),h({devDependencies:["@biomejs/biome"],projectDir:e});let a=S.join(e,"package.json");if(await m.pathExists(a)){let o=await m.readJson(a);o.scripts={...o.scripts,check:"biome check --write ."},await m.writeJson(a,o,{spaces:2})}}async function et(e){let t=S.join(f,"template/with-husky");await m.pathExists(t)&&await m.copy(t,e,{overwrite:!0}),h({devDependencies:["husky","lint-staged"],projectDir:e});let a=S.join(e,"package.json");if(await m.pathExists(a)){let o=await m.readJson(a);o.scripts={...o.scripts,prepare:"husky"},o["lint-staged"]={"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}":["biome check --write ."]},await m.writeJson(a,o,{spaces:2})}}async function tt(e){let t=S.join(f,"template/with-pwa");await m.pathExists(t)&&await m.copy(t,e,{overwrite:!0});let a=S.join(e,"apps/client");h({dependencies:["vite-plugin-pwa"],devDependencies:["@vite-pwa/assets-generator"],projectDir:a});let o=S.join(a,"package.json");if(await m.pathExists(o)){let r=await m.readJson(o);r.scripts={...r.scripts,"generate-pwa-assets":"pwa-assets-generator"},await m.writeJson(o,r,{spaces:2})}}import te from"node:path";import{log as ae}from"@clack/prompts";import oe from"picocolors";function re(e=32){let t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",a="",o=t.length;for(let r=0;r<e;r++)a+=t.charAt(Math.floor(Math.random()*o));return a}async function se(e,t){if(!t)return;let a=te.join(e,"apps/server"),o=te.join(e,"apps/client");try{h({dependencies:["better-auth"],projectDir:a}),h({dependencies:["better-auth"],projectDir:o})}catch(r){throw ae.error(oe.red("Failed to configure authentication")),r instanceof Error&&ae.error(oe.red(r.message)),r}}import at from"node:path";import ot from"fs-extra";async function ie(e,t){let a=at.join(e,"README.md"),o=rt(t);try{await ot.writeFile(a,o)}catch(r){console.error("Failed to create README.md file:",r)}}function rt(e){let{projectName:t,packageManager:a,database:o,auth:r,addons:s=[],orm:i="drizzle"}=e,n=a==="npm"?"npm run":a;return`# ${t}
|
|
3
3
|
|
|
4
4
|
This project was created with [Better-T-Stack](https://github.com/better-t-stack/Better-T-Stack), a modern TypeScript stack that combines React, TanStack Router, Hono, tRPC, and more.
|
|
5
5
|
|
|
@@ -79,13 +79,13 @@ TURSO_AUTH_TOKEN=`;await ut.writeFile(a,o)}function F(){A.info(`Manual Turso Set
|
|
|
79
79
|
4. Add these credentials to the .env file in apps/server/.env
|
|
80
80
|
|
|
81
81
|
TURSO_CONNECTION_URL=your_database_url
|
|
82
|
-
TURSO_AUTH_TOKEN=your_auth_token`)}async function ce(e,t){if(!t){await R(e),A.info(w.blue("Skipping Turso setup. Setting up empty configuration.")),F();return}let a=ct.platform(),o=a==="darwin";if(!(a!=="win32")){A.warn(w.yellow("Automatic Turso setup is not supported on Windows.")),await R(e),F();return}try{if(!await mt()){let x=await pt({message:"Would you like to install Turso CLI?",initialValue:!0});if(J(x)&&(q(w.red("Operation cancelled")),process.exit(0)),!x){await R(e),F();return}await gt(o)}await ft()||await ht();let n=await bt(),g=!1,v="",T=ne.basename(e);for(;!g;){let x=await dt({message:"Enter a name for your database:",defaultValue:T,initialValue:T,placeholder:T});J(x)&&(q(w.red("Operation cancelled")),process.exit(0)),v=x;let L=V();try{L.start(`Creating Turso database "${v}"${n?` in group "${n}"`:""}...`);let
|
|
82
|
+
TURSO_AUTH_TOKEN=your_auth_token`)}async function ce(e,t){if(!t){await R(e),A.info(w.blue("Skipping Turso setup. Setting up empty configuration.")),F();return}let a=ct.platform(),o=a==="darwin";if(!(a!=="win32")){A.warn(w.yellow("Automatic Turso setup is not supported on Windows.")),await R(e),F();return}try{if(!await mt()){let x=await pt({message:"Would you like to install Turso CLI?",initialValue:!0});if(J(x)&&(q(w.red("Operation cancelled")),process.exit(0)),!x){await R(e),F();return}await gt(o)}await ft()||await ht();let n=await bt(),g=!1,v="",T=ne.basename(e);for(;!g;){let x=await dt({message:"Enter a name for your database:",defaultValue:T,initialValue:T,placeholder:T});J(x)&&(q(w.red("Operation cancelled")),process.exit(0)),v=x;let L=V();try{L.start(`Creating Turso database "${v}"${n?` in group "${n}"`:""}...`);let D=await yt(v,n);await R(e,D),L.stop("Turso database configured successfully!"),g=!0}catch(D){if(D instanceof Error&&D.message==="DATABASE_EXISTS")L.stop(w.yellow(`Database "${w.red(v)}" already exists`)),T=`${v}-${Math.floor(Math.random()*1e3)}`;else throw L.stop(w.red("Failed to create Turso database")),D}}}catch(s){A.error(w.red(`Error during Turso setup: ${s}`)),await R(e),F(),A.success("Setup completed with manual configuration required.")}}async function de(e,t,a,o=!0){let r=jt(),s=pe.join(e,"apps/server");if(t==="none"){await kt.remove(pe.join(s,"src/db"));return}try{t==="sqlite"?(a==="drizzle"?h({dependencies:["drizzle-orm","@libsql/client"],devDependencies:["drizzle-kit"],projectDir:s}):a==="prisma"&&h({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:s}),o&&await ce(e,!0)):t==="postgres"&&(a==="drizzle"?h({dependencies:["drizzle-orm","postgres"],devDependencies:["drizzle-kit"],projectDir:s}):a==="prisma"&&h({dependencies:["@prisma/client"],devDependencies:["prisma"],projectDir:s}))}catch(i){throw r.stop(le.red("Failed to set up database")),i instanceof Error&&Pt.error(le.red(i.message)),i}}import U from"node:path";import I from"fs-extra";async function ue(e,t){let a=U.join(e,"apps/server"),o=U.join(e,"apps/client"),r=U.join(a,".env"),s="";if(await I.pathExists(r)&&(s=await I.readFile(r,"utf8")),s.includes("CORS_ORIGIN")||(s+=`
|
|
83
83
|
CORS_ORIGIN=http://localhost:3001`),t.auth&&(s.includes("BETTER_AUTH_SECRET")||(s+=`
|
|
84
84
|
BETTER_AUTH_SECRET=${re()}`),s.includes("BETTER_AUTH_URL")||(s+=`
|
|
85
85
|
BETTER_AUTH_URL=http://localhost:3000`)),t.database!=="none"){if(t.orm==="prisma"&&!s.includes("DATABASE_URL")){let g=t.database==="sqlite"?"":`
|
|
86
86
|
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/mydb?schema=public"`;s+=g}t.database==="sqlite"&&!t.turso&&(s.includes("TURSO_CONNECTION_URL")||(s+=`
|
|
87
|
-
TURSO_CONNECTION_URL=http://127.0.0.1:8080`))}await
|
|
88
|
-
`),await
|
|
87
|
+
TURSO_CONNECTION_URL=http://127.0.0.1:8080`))}await I.writeFile(r,s.trim());let i=U.join(o,".env"),n="";await I.pathExists(i)&&(n=await I.readFile(i,"utf8")),n.includes("VITE_SERVER_URL")||(n+=`VITE_SERVER_URL=http://localhost:3000
|
|
88
|
+
`),await I.writeFile(i,n.trim())}import P from"node:path";import u from"fs-extra";async function me(e,t,a,o){t.includes("todo")?await vt(e,a,o):await xt(e,a)}async function vt(e,t,a){let o=P.join(f,"template/examples/todo");if(await u.pathExists(o)){let r=P.join(o,"apps/client/src/routes"),s=P.join(e,"apps/client/src/routes");if(await u.copy(r,s,{overwrite:!0}),t!=="none"){let i=P.join(o,`apps/server/src/routers/with-${t}-todo.ts`),n=P.join(e,"apps/server/src/routers/todo.ts");await u.pathExists(i)&&await u.copy(i,n,{overwrite:!0})}await Tt(e,a),await Et(e)}}async function Tt(e,t){let a=P.join(e,"apps/client/src/components/header.tsx");if(await u.pathExists(a)){let o=await u.readFile(a,"utf8");t?o=o.replace(/const links = \[\s*{ to: "\/", label: "Home" },\s*{ to: "\/dashboard", label: "Dashboard" },/,`const links = [
|
|
89
89
|
{ to: "/", label: "Home" },
|
|
90
90
|
{ to: "/dashboard", label: "Dashboard" },
|
|
91
91
|
{ to: "/todos", label: "Todos" },`):o=o.replace(/const links = \[\s*{ to: "\/", label: "Home" },/,`const links = [
|
|
@@ -122,8 +122,8 @@ ${p.cyan("\u2022")} Start desktop app: ${p.dim(`cd apps/client && ${e} desktop:d
|
|
|
122
122
|
${p.cyan("\u2022")} Build desktop app: ${p.dim(`cd apps/client && ${e} desktop:build`)}
|
|
123
123
|
${p.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies. See: ${p.dim("https://v2.tauri.app/start/prerequisites/")}
|
|
124
124
|
|
|
125
|
-
`}import _ from"node:path";import{$ as Dt}from"execa";import C from"fs-extra";async function he(e,t){await It(e,t),await Rt(e,t)}async function It(e,t){let a=_.join(e,"package.json");if(await C.pathExists(a)){let o=await C.readJson(a);if(o.name=t.projectName,t.packageManager!=="bun"&&(o.packageManager=t.packageManager==="npm"?"npm@10.9.2":t.packageManager==="pnpm"?"pnpm@10.6.4":"bun@1.2.5"),await C.writeJson(a,o,{spaces:2}),t.packageManager==="pnpm"){let r=_.join(f,"template/with-pnpm/pnpm-workspace.yaml"),s=_.join(e,"pnpm-workspace.yaml");await C.pathExists(r)&&await C.copy(r,s)}}}async function Rt(e,t){let a=_.join(e,"apps/server/package.json");if(await C.pathExists(a)){let o=await C.readJson(a);t.database!=="none"&&(t.database==="sqlite"&&(o.scripts["db:local"]="turso dev --db-file local.db"),t.orm==="prisma"?(o.scripts["db:push"]="prisma db push --schema ./prisma/schema",o.scripts["db:studio"]="prisma studio"):t.orm==="drizzle"&&(o.scripts["db:push"]="drizzle-kit push",o.scripts["db:studio"]="drizzle-kit studio")),await C.writeJson(a,o,{spaces:2})}}async function ge(e,t){t&&await Dt({cwd:e})`git init`}import j from"node:path";import b from"fs-extra";async function we(e){let t=j.join(f,"template/base");if(!await b.pathExists(t))throw new Error(`Template directory not found: ${t}`);await b.copy(t,e)}async function be(e,t){if(!t)return;let a=j.join(f,"template/with-auth");await b.pathExists(a)&&await b.copy(a,e,{overwrite:!0})}async function ye(e,t,a,o){if(t==="none"||a==="none")return;let r=j.join(f,zt(t,a));if(await b.pathExists(r)){await b.copy(r,e,{overwrite:!0});let s=j.join(e,"apps/server/src"),i=j.join(s,"lib"),n=j.join(s,"with-auth-lib");o?await b.pathExists(n)&&(await b.remove(i),await b.move(n,i)):await b.remove(n)}}async function Pe(e){let t=[j.join(e,"_gitignore"),j.join(e,"apps/client/_gitignore"),j.join(e,"apps/server/_gitignore")];for(let a of t)if(await b.pathExists(a)){let o=j.join(j.dirname(a),".gitignore");await b.move(a,o)}}function zt(e,t){return e==="drizzle"?t==="sqlite"?"template/with-drizzle-sqlite":"template/with-drizzle-postgres":e==="prisma"?t==="sqlite"?"template/with-prisma-sqlite":"template/with-prisma-postgres":"template/base"}async function ke(e){let t=Ft(),a=Lt.resolve(process.cwd(),e.projectName);try{return await Ut.ensureDir(a),await we(a),await Pe(a),await be(a,e.auth),await ye(a,e.orm,e.database,e.auth),await me(a,e.examples,e.orm,e.auth),await de(a,e.database,e.orm,e.turso??e.database==="sqlite"),await se(a,e.auth),await ue(a,e),await ge(a,e.git),e.addons.length>0&&await ee(a,e.addons,e.packageManager),await he(a,e),await ie(a,e),fe(e.database,e.projectName,e.packageManager,!e.noInstall,e.orm,e.addons),a}catch(o){throw t.message(je.red("Failed")),o instanceof Error&&(Nt(je.red(`Error during project creation: ${o.message}`)),process.exit(1)),o}}import{log as ve,spinner as Te}from"@clack/prompts";import{$ as H}from"execa";import M from"picocolors";async function xe({projectDir:e,packageManager:t,addons:a=[]}){let o=Te();try{switch(o.start(`Running ${t} install...`),t){case"npm":await H({cwd:e,stderr:"inherit"})`${t} install`;break;case"pnpm":case"bun":await H({cwd:e})`${t} install`;break}o.stop("Dependencies installed successfully"),(a.includes("biome")||a.includes("husky"))&&await _t(e,t)}catch(r){throw o.stop(M.red("Failed to install dependencies")),r instanceof Error&&ve.error(M.red(`Installation error: ${r.message}`)),r}}async function _t(e,t){let a=Te();try{a.start("Running Biome format check..."),await H({cwd:e})`${t} biome check --write .`,a.stop("Biome check completed successfully")}catch{a.stop(M.yellow("Biome check encountered issues")),ve.warn(M.yellow("Some files may need manual formatting"))}}import{cancel as Ca,group as Sa}from"@clack/prompts";import Oa from"picocolors";import{cancel as Mt,isCancel as Bt,multiselect as Gt}from"@clack/prompts";import qt from"picocolors";async function $e(e){if(e!==void 0)return e;let t=await Gt({message:"Which Addons would you like to add?",options:[{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"},{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)"}],required:!1});return Bt(t)&&(Mt(qt.red("Operation cancelled")),process.exit(0)),t.includes("husky")&&!t.includes("biome")&&t.push("biome"),t}import{cancel as Jt,confirm as Vt,isCancel as Ht}from"@clack/prompts";import Wt from"picocolors";async function Ee(e,t){if(!t)return!1;if(e!==void 0)return e;let a=await Vt({message:"Would you like to add authentication with Better-Auth?",initialValue:d.auth});return Ht(a)&&(Jt(Wt.red("Operation cancelled")),process.exit(0)),a}import{cancel as Qt,isCancel as Kt,select as Xt}from"@clack/prompts";import Yt from"picocolors";async function Ce(e){if(e!==void 0)return e;let t=await Xt({message:"Which database would you like to use?",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"by Turso"},{value:"postgres",label:"PostgreSQL",hint:"Traditional relational database"}],initialValue:"sqlite"});return Kt(t)&&(Qt(Yt.red("Operation cancelled")),process.exit(0)),t}import{cancel as Zt,isCancel as ea,multiselect as ta}from"@clack/prompts";import aa from"picocolors";async function Se(e,t){if(e!==void 0)return e;if(t==="none")return[];let a=await ta({message:"Which examples would you like to include?",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"}],required:!1,initialValues:d.examples});return ea(a)&&(Zt(aa.red("Operation cancelled")),process.exit(0)),a}import{cancel as oa,confirm as ra,isCancel as sa}from"@clack/prompts";import ia from"picocolors";async function Oe(e){if(e!==void 0)return e;let t=await ra({message:"Initialize a new git repository?",initialValue:d.git});return sa(t)&&(oa(ia.red("Operation cancelled")),process.exit(0)),t}import{cancel as na,confirm as ca,isCancel as pa}from"@clack/prompts";import la from"picocolors";async function Ae(e){if(e!==void 0)return e;let t=await ca({message:"Do you want to install project dependencies?",initialValue:!d.noInstall});return pa(t)&&(na(la.red("Operation cancelled")),process.exit(0)),!t}import{cancel as da,isCancel as ua,select as ma}from"@clack/prompts";import fa from"picocolors";async function De(e,t){if(!t)return"none";if(e!==void 0)return e;let a=await ma({message:"Which ORM would you like to use?",options:[{value:"drizzle",label:"Drizzle",hint:"Type-safe, lightweight ORM"},{value:"prisma",label:"Prisma",hint:"Powerful, feature-rich ORM with schema migrations"}],initialValue:"drizzle"});return ua(a)&&(da(fa.red("Operation cancelled")),process.exit(0)),a}import{cancel as ha,isCancel as ga,select as wa}from"@clack/prompts";import ba from"picocolors";var Ie=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};async function Re(e){if(e!==void 0)return e;let t=Ie(),a=await wa({message:"Which package manager do you want to use?",options:[{value:"npm",label:"npm",hint:"Node Package Manager"},{value:"bun",label:"bun",hint:"All-in-one JavaScript runtime & toolkit"},{value:"pnpm",label:"pnpm",hint:"Fast, disk space efficient package manager"}],initialValue:t});return ga(a)&&(ha(ba.red("Operation cancelled")),process.exit(0)),a}import z from"node:path";import{cancel as ya,isCancel as Pa,text as ja}from"@clack/prompts";import O from"fs-extra";import ka from"picocolors";var va=["<",">",":",'"',"|","?","*"],ze=255;function Le(e){if(e!=="."){if(!e)return"Project name cannot be empty";if(e.length>ze)return`Project name must be less than ${ze} characters`;if(va.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"||e.toLowerCase()==="favicon.ico")return"Project name is reserved"}}async function Ne(e){if(e)if(e==="."){let s=process.cwd();if(O.readdirSync(s).length===0)return e}else{let s=z.basename(e);if(!Le(s)){let n=z.resolve(process.cwd(),e);if(!O.pathExistsSync(n)||O.readdirSync(n).length===0)return e}}let t=!1,a="",o=d.projectName,r=1;for(;O.pathExistsSync(z.resolve(process.cwd(),o));)o=`${d.projectName}-${r}`,r++;for(;!t;){let s=await ja({message:"Enter your project name or path (relative to current directory)",placeholder:o,initialValue:e,defaultValue:o,validate:i=>{let n=i.trim()||o;if(n==="."){if(O.readdirSync(process.cwd()).length>0)return"Current directory is not empty. Please choose a different directory.";t=!0;return}let g=z.resolve(process.cwd(),n),v=z.basename(g),T=Le(v);if(T)return T;if(!g.startsWith(process.cwd()))return"Project path must be within current directory";if(O.pathExistsSync(g)&&O.readdirSync(g).length>0)return`Directory "${n}" already exists and is not empty. Please choose a different name or path.`;t=!0}});Pa(s)&&(ya(ka.red("Operation cancelled.")),process.exit(0)),a=s||o}return a}import{cancel as Ta,confirm as xa,isCancel as $a}from"@clack/prompts";import Ea from"picocolors";async function Fe(e){if(e!==void 0)return e;let t=await xa({message:"Set up a Turso database for this project?",initialValue:!0});return $a(t)&&(Ta(Ea.red("Operation cancelled")),process.exit(0)),t}async function Ue(e){let t=await Sa({projectName:async()=>Ne(e.projectName),database:()=>Ce(e.database),orm:({results:a})=>De(e.orm,a.database!=="none"),auth:({results:a})=>Ee(e.auth,a.database!=="none"),turso:({results:a})=>a.database==="sqlite"&&a.orm!=="prisma"?Fe(e.turso):Promise.resolve(!1),addons:()=>$e(e.addons),examples:({results:a})=>Se(e.examples,a.database),git:()=>Oe(e.git),packageManager:()=>Re(e.packageManager),noInstall:()=>Ae(e.noInstall)},{onCancel:()=>{Ca(Oa.red("Operation cancelled")),process.exit(0)}});return{projectName:t.projectName,database:t.database,orm:t.orm,auth:t.auth,addons:t.addons,examples:t.examples,git:t.git,packageManager:t.packageManager,noInstall:t.noInstall,turso:t.turso}}import E from"picocolors";function W(e){let t=[];return e.projectName&&t.push(`${E.blue("Project Name:")} ${e.projectName}`),e.database&&t.push(`${E.blue("Database:")} ${e.database}`),e.orm&&t.push(`${E.blue("ORM:")} ${e.orm}`),e.auth!==void 0&&t.push(`${E.blue("Authentication:")} ${e.auth}`),e.addons?.length&&t.push(`${E.blue("Addons:")} ${e.addons.join(", ")}`),e.git!==void 0&&t.push(`${E.blue("Git Init:")} ${e.git}`),e.packageManager&&t.push(`${E.blue("Package Manager:")} ${e.packageManager}`),e.noInstall!==void 0&&t.push(`${E.blue("Skip Install:")} ${e.noInstall}`),e.turso!==void 0&&t.push(`${E.blue("Turso Setup:")} ${e.turso}`),t.join(`
|
|
126
|
-
`)}function _e(e){let t=[];if(e.database==="none"?t.push("--no-database"):e.database==="sqlite"?t.push("--sqlite"):e.database==="postgres"&&t.push("--postgres"),e.database!=="none"&&(e.orm==="drizzle"?t.push("--drizzle"):e.orm==="prisma"&&t.push("--prisma")),e.auth?t.push("--auth"):t.push("--no-auth"),e.git?t.push("--git"):t.push("--no-git"),e.noInstall?t.push("--no-install"):t.push("--install"),e.packageManager&&t.push(`--${e.packageManager}`),e.addons.length>0)for(let s of e.addons)t.push(`--${s}`);else t.push("--no-addons");e.examples&&e.examples.length>0?t.push(`--examples ${e.examples.join(",")}`):t.push("--no-examples"),e.database==="sqlite"&&(e.turso?t.push("--turso"):t.push("--no-turso"));let a="npx create-better-t-stack",o=e.projectName?` ${e.projectName}`:"",r=t.length>0?` ${t.join(" ")}`:"";return`${a}${o}${r}`}import
|
|
125
|
+
`}import _ from"node:path";import{$ as It,execa as Dt}from"execa";import C from"fs-extra";async function he(e,t){await Rt(e,t),await zt(e,t)}async function Rt(e,t){let a=_.join(e,"package.json");if(await C.pathExists(a)){let o=await C.readJson(a);o.name=t.projectName;let{stdout:r}=await Dt(t.packageManager,["-v"],{cwd:e});if(o.packageManager=`${t.packageManager}@${r.trim()}`,await C.writeJson(a,o,{spaces:2}),t.packageManager==="pnpm"){let s=_.join(f,"template/with-pnpm/pnpm-workspace.yaml"),i=_.join(e,"pnpm-workspace.yaml");await C.pathExists(s)&&await C.copy(s,i)}}}async function zt(e,t){let a=_.join(e,"apps/server/package.json");if(await C.pathExists(a)){let o=await C.readJson(a);t.database!=="none"&&(t.database==="sqlite"&&(o.scripts["db:local"]="turso dev --db-file local.db"),t.orm==="prisma"?(o.scripts["db:push"]="prisma db push --schema ./prisma/schema",o.scripts["db:studio"]="prisma studio"):t.orm==="drizzle"&&(o.scripts["db:push"]="drizzle-kit push",o.scripts["db:studio"]="drizzle-kit studio")),await C.writeJson(a,o,{spaces:2})}}async function ge(e,t){t&&await It({cwd:e})`git init`}import j from"node:path";import b from"fs-extra";async function we(e){let t=j.join(f,"template/base");if(!await b.pathExists(t))throw new Error(`Template directory not found: ${t}`);await b.copy(t,e)}async function be(e,t){if(!t)return;let a=j.join(f,"template/with-auth");await b.pathExists(a)&&await b.copy(a,e,{overwrite:!0})}async function ye(e,t,a,o){if(t==="none"||a==="none")return;let r=j.join(f,Lt(t,a));if(await b.pathExists(r)){await b.copy(r,e,{overwrite:!0});let s=j.join(e,"apps/server/src"),i=j.join(s,"lib"),n=j.join(s,"with-auth-lib");o?await b.pathExists(n)&&(await b.remove(i),await b.move(n,i)):await b.remove(n)}}async function Pe(e){let t=[j.join(e,"_gitignore"),j.join(e,"apps/client/_gitignore"),j.join(e,"apps/server/_gitignore")];for(let a of t)if(await b.pathExists(a)){let o=j.join(j.dirname(a),".gitignore");await b.move(a,o)}}function Lt(e,t){return e==="drizzle"?t==="sqlite"?"template/with-drizzle-sqlite":"template/with-drizzle-postgres":e==="prisma"?t==="sqlite"?"template/with-prisma-sqlite":"template/with-prisma-postgres":"template/base"}async function ke(e){let t=Ut(),a=Nt.resolve(process.cwd(),e.projectName);try{return await _t.ensureDir(a),await we(a),await Pe(a),await be(a,e.auth),await ye(a,e.orm,e.database,e.auth),await me(a,e.examples,e.orm,e.auth),await de(a,e.database,e.orm,e.turso??e.database==="sqlite"),await se(a,e.auth),await ue(a,e),await ge(a,e.git),e.addons.length>0&&await ee(a,e.addons,e.packageManager),await he(a,e),await ie(a,e),fe(e.database,e.projectName,e.packageManager,!e.noInstall,e.orm,e.addons),a}catch(o){throw t.message(je.red("Failed")),o instanceof Error&&(Ft(je.red(`Error during project creation: ${o.message}`)),process.exit(1)),o}}import{log as ve,spinner as Te}from"@clack/prompts";import{$ as H}from"execa";import M from"picocolors";async function xe({projectDir:e,packageManager:t,addons:a=[]}){let o=Te();try{switch(o.start(`Running ${t} install...`),t){case"npm":await H({cwd:e,stderr:"inherit"})`${t} install`;break;case"pnpm":case"bun":await H({cwd:e})`${t} install`;break}o.stop("Dependencies installed successfully"),(a.includes("biome")||a.includes("husky"))&&await Mt(e,t)}catch(r){throw o.stop(M.red("Failed to install dependencies")),r instanceof Error&&ve.error(M.red(`Installation error: ${r.message}`)),r}}async function Mt(e,t){let a=Te();try{a.start("Running Biome format check..."),await H({cwd:e})`${t} biome check --write .`,a.stop("Biome check completed successfully")}catch{a.stop(M.yellow("Biome check encountered issues")),ve.warn(M.yellow("Some files may need manual formatting"))}}import{cancel as Sa,group as Oa}from"@clack/prompts";import Aa from"picocolors";import{cancel as Bt,isCancel as Gt,multiselect as qt}from"@clack/prompts";import Jt from"picocolors";async function $e(e){if(e!==void 0)return e;let t=await qt({message:"Which Addons would you like to add?",options:[{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"},{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)"}],required:!1});return Gt(t)&&(Bt(Jt.red("Operation cancelled")),process.exit(0)),t.includes("husky")&&!t.includes("biome")&&t.push("biome"),t}import{cancel as Vt,confirm as Ht,isCancel as Wt}from"@clack/prompts";import Qt from"picocolors";async function Ee(e,t){if(!t)return!1;if(e!==void 0)return e;let a=await Ht({message:"Would you like to add authentication with Better-Auth?",initialValue:d.auth});return Wt(a)&&(Vt(Qt.red("Operation cancelled")),process.exit(0)),a}import{cancel as Kt,isCancel as Xt,select as Yt}from"@clack/prompts";import Zt from"picocolors";async function Ce(e){if(e!==void 0)return e;let t=await Yt({message:"Which database would you like to use?",options:[{value:"none",label:"None",hint:"No database setup"},{value:"sqlite",label:"SQLite",hint:"by Turso"},{value:"postgres",label:"PostgreSQL",hint:"Traditional relational database"}],initialValue:"sqlite"});return Xt(t)&&(Kt(Zt.red("Operation cancelled")),process.exit(0)),t}import{cancel as ea,isCancel as ta,multiselect as aa}from"@clack/prompts";import oa from"picocolors";async function Se(e,t){if(e!==void 0)return e;if(t==="none")return[];let a=await aa({message:"Which examples would you like to include?",options:[{value:"todo",label:"Todo App",hint:"A simple CRUD example app"}],required:!1,initialValues:d.examples});return ta(a)&&(ea(oa.red("Operation cancelled")),process.exit(0)),a}import{cancel as ra,confirm as sa,isCancel as ia}from"@clack/prompts";import na from"picocolors";async function Oe(e){if(e!==void 0)return e;let t=await sa({message:"Initialize a new git repository?",initialValue:d.git});return ia(t)&&(ra(na.red("Operation cancelled")),process.exit(0)),t}import{cancel as ca,confirm as pa,isCancel as la}from"@clack/prompts";import da from"picocolors";async function Ae(e){if(e!==void 0)return e;let t=await pa({message:"Do you want to install project dependencies?",initialValue:!d.noInstall});return la(t)&&(ca(da.red("Operation cancelled")),process.exit(0)),!t}import{cancel as ua,isCancel as ma,select as fa}from"@clack/prompts";import ha from"picocolors";async function Ie(e,t){if(!t)return"none";if(e!==void 0)return e;let a=await fa({message:"Which ORM would you like to use?",options:[{value:"drizzle",label:"Drizzle",hint:"Type-safe, lightweight ORM"},{value:"prisma",label:"Prisma",hint:"Powerful, feature-rich ORM with schema migrations"}],initialValue:"drizzle"});return ma(a)&&(ua(ha.red("Operation cancelled")),process.exit(0)),a}import{cancel as ga,isCancel as wa,select as ba}from"@clack/prompts";import ya from"picocolors";var De=()=>{let e=process.env.npm_config_user_agent;return e?.startsWith("pnpm")?"pnpm":e?.startsWith("bun")?"bun":"npm"};async function Re(e){if(e!==void 0)return e;let t=De(),a=await ba({message:"Which package manager do you want to use?",options:[{value:"npm",label:"npm",hint:"Node Package Manager"},{value:"bun",label:"bun",hint:"All-in-one JavaScript runtime & toolkit"},{value:"pnpm",label:"pnpm",hint:"Fast, disk space efficient package manager"}],initialValue:t});return wa(a)&&(ga(ya.red("Operation cancelled")),process.exit(0)),a}import z from"node:path";import{cancel as Pa,isCancel as ja,text as ka}from"@clack/prompts";import O from"fs-extra";import va from"picocolors";var Ta=["<",">",":",'"',"|","?","*"],ze=255;function Le(e){if(e!=="."){if(!e)return"Project name cannot be empty";if(e.length>ze)return`Project name must be less than ${ze} characters`;if(Ta.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"||e.toLowerCase()==="favicon.ico")return"Project name is reserved"}}async function Ne(e){if(e)if(e==="."){let s=process.cwd();if(O.readdirSync(s).length===0)return e}else{let s=z.basename(e);if(!Le(s)){let n=z.resolve(process.cwd(),e);if(!O.pathExistsSync(n)||O.readdirSync(n).length===0)return e}}let t=!1,a="",o=d.projectName,r=1;for(;O.pathExistsSync(z.resolve(process.cwd(),o));)o=`${d.projectName}-${r}`,r++;for(;!t;){let s=await ka({message:"Enter your project name or path (relative to current directory)",placeholder:o,initialValue:e,defaultValue:o,validate:i=>{let n=i.trim()||o;if(n==="."){if(O.readdirSync(process.cwd()).length>0)return"Current directory is not empty. Please choose a different directory.";t=!0;return}let g=z.resolve(process.cwd(),n),v=z.basename(g),T=Le(v);if(T)return T;if(!g.startsWith(process.cwd()))return"Project path must be within current directory";if(O.pathExistsSync(g)&&O.readdirSync(g).length>0)return`Directory "${n}" already exists and is not empty. Please choose a different name or path.`;t=!0}});ja(s)&&(Pa(va.red("Operation cancelled.")),process.exit(0)),a=s||o}return a}import{cancel as xa,confirm as $a,isCancel as Ea}from"@clack/prompts";import Ca from"picocolors";async function Fe(e){if(e!==void 0)return e;let t=await $a({message:"Set up a Turso database for this project?",initialValue:!0});return Ea(t)&&(xa(Ca.red("Operation cancelled")),process.exit(0)),t}async function Ue(e){let t=await Oa({projectName:async()=>Ne(e.projectName),database:()=>Ce(e.database),orm:({results:a})=>Ie(e.orm,a.database!=="none"),auth:({results:a})=>Ee(e.auth,a.database!=="none"),turso:({results:a})=>a.database==="sqlite"&&a.orm!=="prisma"?Fe(e.turso):Promise.resolve(!1),addons:()=>$e(e.addons),examples:({results:a})=>Se(e.examples,a.database),git:()=>Oe(e.git),packageManager:()=>Re(e.packageManager),noInstall:()=>Ae(e.noInstall)},{onCancel:()=>{Sa(Aa.red("Operation cancelled")),process.exit(0)}});return{projectName:t.projectName,database:t.database,orm:t.orm,auth:t.auth,addons:t.addons,examples:t.examples,git:t.git,packageManager:t.packageManager,noInstall:t.noInstall,turso:t.turso}}import E from"picocolors";function W(e){let t=[];return e.projectName&&t.push(`${E.blue("Project Name:")} ${e.projectName}`),e.database&&t.push(`${E.blue("Database:")} ${e.database}`),e.orm&&t.push(`${E.blue("ORM:")} ${e.orm}`),e.auth!==void 0&&t.push(`${E.blue("Authentication:")} ${e.auth}`),e.addons?.length&&t.push(`${E.blue("Addons:")} ${e.addons.join(", ")}`),e.git!==void 0&&t.push(`${E.blue("Git Init:")} ${e.git}`),e.packageManager&&t.push(`${E.blue("Package Manager:")} ${e.packageManager}`),e.noInstall!==void 0&&t.push(`${E.blue("Skip Install:")} ${e.noInstall}`),e.turso!==void 0&&t.push(`${E.blue("Turso Setup:")} ${e.turso}`),t.join(`
|
|
126
|
+
`)}function _e(e){let t=[];if(e.database==="none"?t.push("--no-database"):e.database==="sqlite"?t.push("--sqlite"):e.database==="postgres"&&t.push("--postgres"),e.database!=="none"&&(e.orm==="drizzle"?t.push("--drizzle"):e.orm==="prisma"&&t.push("--prisma")),e.auth?t.push("--auth"):t.push("--no-auth"),e.git?t.push("--git"):t.push("--no-git"),e.noInstall?t.push("--no-install"):t.push("--install"),e.packageManager&&t.push(`--${e.packageManager}`),e.addons.length>0)for(let s of e.addons)t.push(`--${s}`);else t.push("--no-addons");e.examples&&e.examples.length>0?t.push(`--examples ${e.examples.join(",")}`):t.push("--no-examples"),e.database==="sqlite"&&(e.turso?t.push("--turso"):t.push("--no-turso"));let a="npx create-better-t-stack",o=e.projectName?` ${e.projectName}`:"",r=t.length>0?` ${t.join(" ")}`:"";return`${a}${o}${r}`}import Ia from"node:path";import Da from"fs-extra";var Me=()=>{let e=Ia.join(f,"package.json");return Da.readJSONSync(e).version??"1.0.0"};import Be from"gradient-string";var Ge=`
|
|
127
127
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
128
128
|
\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
129
129
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D
|
|
@@ -142,5 +142,5 @@ ${p.yellow("NOTE:")} Tauri requires Rust and platform-specific dependencies. See
|
|
|
142
142
|
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557
|
|
143
143
|
\u2551 Better T-Stack \u2551
|
|
144
144
|
\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
145
|
-
`)):console.log(Be(Object.values(qe)).multiline(Ge))};process.on("SIGINT",()=>{k.error($.red("Operation cancelled")),process.exit(0)});var Q=new
|
|
146
|
-
${$.white(_e(s))}`));let n=((Date.now()-e)/1e3).toFixed(2);
|
|
145
|
+
`)):console.log(Be(Object.values(qe)).multiline(Ge))};process.on("SIGINT",()=>{k.error($.red("Operation cancelled")),process.exit(0)});var Q=new Fa;async function Ua(){let e=Date.now();Q.name("create-better-t-stack").description("Create a new Better-T Stack project").version(Me()).argument("[project-directory]","Project name/directory").option("-y, --yes","Use default configuration").option("--no-database","Skip database setup").option("--sqlite","Use SQLite database").option("--postgres","Use PostgreSQL database").option("--auth","Include authentication").option("--no-auth","Exclude authentication").option("--pwa","Include Progressive Web App support").option("--tauri","Include Tauri desktop app support").option("--biome","Include Biome for linting and formatting").option("--husky","Include Husky, lint-staged for Git hooks").option("--no-addons","Skip all additional addons").option("--examples <examples>","Include specified examples").option("--no-examples","Skip all examples").option("--git","Include git setup").option("--no-git","Skip git initialization").option("--npm","Use npm package manager").option("--pnpm","Use pnpm package manager").option("--bun","Use bun package manager").option("--drizzle","Use Drizzle ORM").option("--prisma","Use Prisma ORM (coming soon)").option("--install","Install dependencies").option("--no-install","Skip installing dependencies").option("--turso","Set up Turso for SQLite database").option("--no-turso","Skip Turso setup for SQLite database").parse();let t=Na();try{Je(),za($.magenta("Creating a new Better-T-Stack project"));let a=Q.opts(),o=Q.args[0],r={...o&&{projectName:o},...a.database===!1&&{database:"none"},...a.sqlite&&{database:"sqlite"},...a.postgres&&{database:"postgres"},...a.drizzle&&{orm:"drizzle"},...a.prisma&&{orm:"prisma"},..."auth"in a&&{auth:a.auth},...a.npm&&{packageManager:"npm"},...a.pnpm&&{packageManager:" pnpm"},...a.bun&&{packageManager:"bun"},..."git"in a&&{git:a.git},..."install"in a&&{noInstall:!a.install},..."turso"in a&&{turso:a.turso},...(a.pwa||a.tauri||a.biome||a.husky||a.addons===!1)&&{addons:a.addons===!1?[]:[...a.pwa?["pwa"]:[],...a.tauri?["tauri"]:[],...a.biome?["biome"]:[],...a.husky?["husky"]:[]]},...(a.examples||a.examples===!1)&&{examples:a.examples===!1?[]:typeof a.examples=="string"?a.examples.split(",").filter(g=>g==="todo"):[]}};!a.yes&&Object.keys(r).length>0&&(k.info($.yellow("Using these pre-selected options:")),k.message(W(r)),k.message(""));let s=a.yes?{...d,projectName:o??d.projectName,database:a.database===!1?"none":a.database??d.database,orm:a.database===!1?"none":a.drizzle?"drizzle":a.prisma?"prisma":d.orm,auth:a.auth??d.auth,git:a.git??d.git,noInstall:"noInstall"in a?a.noInstall:d.noInstall,packageManager:r.packageManager??d.packageManager,addons:r.addons?.length?r.addons:d.addons,examples:r.examples?.length?r.examples:d.examples,turso:"turso"in a?a.turso:r.database==="sqlite"?d.turso:!1}:await Ue(r);a.yes&&(k.info($.yellow("Using these default options:")),k.message(W(s)),k.message(""));let i=await ke(s);s.noInstall||await xe({projectDir:i,packageManager:s.packageManager,addons:s.addons}),k.success($.blue(`You can reproduce this setup with the following command:
|
|
146
|
+
${$.white(_e(s))}`));let n=((Date.now()-e)/1e3).toFixed(2);La($.magenta(`Project created successfully in ${$.bold(n)} seconds!`))}catch(a){t.stop($.red("Failed")),a instanceof Error&&(Ra($.red(`An unexpected error occurred: ${a.message}`)),process.exit(1))}}Ua().catch(e=>{k.error("Aborting installation..."),e instanceof Error?k.error(e.message):(k.error("An unknown error has occurred. Please open an issue on GitHub with the below:"),console.log(e)),process.exit(1)});
|
package/package.json
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-better-t-stack",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.10",
|
|
4
4
|
"description": "CLI tool to scaffold Better-T Stack projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
8
8
|
"create-better-t-stack": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"template"
|
|
13
|
+
],
|
|
10
14
|
"keywords": [],
|
|
11
15
|
"repository": {
|
|
12
16
|
"type": "git",
|
|
13
17
|
"url": "git+https://github.com/better-t-stack/create-better-t-stack.git",
|
|
14
18
|
"directory": "apps/cli"
|
|
15
19
|
},
|
|
16
|
-
"files": [
|
|
17
|
-
"dist",
|
|
18
|
-
"template"
|
|
19
|
-
],
|
|
20
20
|
"homepage": "https://better-t-stack.pages.dev/",
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsup",
|