create-t3-chill-app
Version:
Build a project with Next.js, Supabase, tRPC, Prisma, and Stripe using the Create T3 Chill App CLI
82 lines (79 loc) • 28.2 kB
JavaScript
import{Command as St}from"commander";import{execSync as Y}from"child_process";var Te={name:"create-t3-chill-app",version:"1.1.1",description:"Build a project with Next.js, Supabase, tRPC, Prisma, and Stripe using the Create T3 Chill App CLI."},Ae=`
\u2588\u2580\u2580 \u2588\u2580\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2580\u2588 \u2580\u2580\u2588\u2580\u2580 \u2588\u2580\u2580 \u2591\u2591 \u2580\u2580\u2588\u2580\u2580 \u2588\u2580\u2580\u2588 \u2591\u2591 \u2588\u2580\u2580 \u2588\u2591\u2591\u2588 \u2591\u2580\u2591 \u2588\u2591\u2591 \u2588\u2591\u2591 \u2591\u2591 \u2588\u2580\u2580\u2588 \u2588\u2580\u2580\u2588 \u2588\u2580\u2580\u2588
\u2588\u2591\u2591 \u2588\u2584\u2584\u2580 \u2588\u2580\u2580 \u2588\u2584\u2584\u2588 \u2591\u2591\u2588\u2591\u2591 \u2588\u2580\u2580 \u2580\u2580 \u2591\u2591\u2588\u2591\u2591 \u2591\u2591\u2580\u2584 \u2580\u2580 \u2588\u2591\u2591 \u2588\u2580\u2580\u2588 \u2580\u2588\u2580 \u2588\u2591\u2591 \u2588\u2591\u2591 \u2580\u2580 \u2588\u2584\u2584\u2588 \u2588\u2591\u2591\u2588 \u2588\u2591\u2591\u2588
\u2580\u2580\u2580 \u2580\u2591\u2580\u2580 \u2580\u2580\u2580 \u2580\u2591\u2591\u2580 \u2591\u2591\u2580\u2591\u2591 \u2580\u2580\u2580 \u2591\u2591 \u2591\u2591\u2580\u2591\u2591 \u2588\u2584\u2584\u2588 \u2591\u2591 \u2580\u2580\u2580 \u2580\u2591\u2591\u2580 \u2580\u2580\u2580 \u2580\u2580\u2580 \u2580\u2580\u2580 \u2591\u2591 \u2580\u2591\u2591\u2580 \u2588\u2580\u2580\u2580 \u2588\u2580\u2580\u2580
`,F={nodeVersion:18,dockerPorts:{start:54321,end:54329}},w={files:[".env",".env.local",".env.production",".env.example"],requiredVars:["DATABASE_URL","DIRECT_URL"],requiredLocalVars:["NEXT_PUBLIC_SUPABASE_URL","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY","NODE_ENV"],stripeVars:["STRIPE_SECRET_KEY","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY","NEXT_PUBLIC_STRIPE_PRICE_ID","STRIPE_WEBHOOK_SECRET"]},$={directories:["template","template/app","template/components","template/lib","template/hooks","template/providers","template/prisma","template/supabase"],coreFiles:["next.config.ts","tailwind.config.ts","postcss.config.mjs","components.json","eslint.config.mjs","tsconfig.json","next-env.d.ts","middleware.ts","prisma.config.ts",".gitignore",".npmignore",".prettierrc","STRIPE_SETUP.md","README.md","app/favicon.ico","app/globals.css","app/layout.tsx","app/page.tsx","lib/utils.ts","lib/env.ts","lib/prisma.ts","lib/routes.ts","supabase/config.toml","supabase/.gitignore"],sourceDirectories:["app/trpc","app/api","app/(protected)","app/auth","lib/supabase","lib/stripe","components","providers","hooks"]};var S=Te,j=Ae,D=F.nodeVersion,It=F.dockerPorts,Tt=w.files,q=w.requiredVars,M=w.requiredLocalVars,K=w.stripeVars,At=$.directories,kt=$.coreFiles,xt=$.sourceDirectories;import u from"chalk";var ke=e=>{console.log(u.blue(e))},xe=e=>{console.log(u.green(e))},Le=e=>{console.log(u.yellow(e))},Ne=e=>{console.log(u.red(e))},$e=e=>{console.log(u.gray(e))},De=e=>{console.log(u.cyan(e))},Oe=e=>{console.log(u.magenta(e))},Ue=e=>{console.log(u.blue.bold(`
${e}
`))},Be=(e,t="gray")=>{e.forEach(r=>{console.log(u[t](` ${r}`))})},Ve=()=>{console.log(u.cyan.bold(j)),console.log(u.magenta.bold(`Welcome to Create T3 Chill App! \u{1F680}
`)),console.log(u.gray("The fastest way to ship products with Next.js, Supabase, tRPC, Prisma, and Stripe."))},Fe={info:ke,success:xe,warning:Le,error:Ne,gray:$e,cyan:De,magenta:Oe,step:Ue,list:Be,printWelcome:Ve},s=Fe;var je=async()=>new Promise(e=>{let t=()=>{try{process.stdin.setRawMode(!1),process.stdin.pause()}catch{}},r=n=>{n[0]===3&&(t(),console.log(`
`),process.exit(0)),t(),e()};try{process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.once("data",r)}catch{setTimeout(()=>e(),1e3)}}),qe=async(e=!0)=>new Promise(t=>{let r=()=>{try{process.stdin.setRawMode(!1),process.stdin.pause()}catch{}},n=o=>{o[0]===3&&(r(),console.log(`
`),process.exit(0)),r();let a=o.toString().toLowerCase();t(a==="y"||a==="yes"?!0:a==="n"||a==="no"?!1:e)};try{process.stdin.setRawMode(!0),process.stdin.resume(),process.stdin.once("data",n)}catch{t(e)}}),Me=()=>{process.stdout.write("\r\x1B[K")},Ke=e=>{process.stdout.write(`\x1B[${e}A`)},Ye=()=>{let e=()=>{try{process.stdin.setRawMode(!1),process.stdin.pause()}catch{}console.log(`
\u{1F44B} Goodbye!`),process.exit(0)};process.on("SIGINT",e),process.on("SIGTERM",e),process.on("uncaughtException",t=>{console.error(`
\u274C Unexpected error:`,t.message),e()})},Xe={waitForUserInput:je,askYesNo:qe,clearLine:Me,moveCursorUp:Ke,setupGracefulExit:Ye},f=Xe;var He=e=>{let t=[];return e?.trim()||t.push("Project name cannot be empty"),e.includes(" ")&&t.push("Project name cannot contain spaces"),/^[a-zA-Z0-9-_]+$/.test(e)||t.push("Project name can only contain letters, numbers, hyphens, and underscores"),e.length>214&&t.push("Project name must be less than 214 characters"),(e.startsWith(".")||e.startsWith("-")||e.startsWith("_"))&&t.push("Project name cannot start with a dot, hyphen, or underscore"),{isValid:t.length===0,errors:t}},We=(e,t)=>{let r=parseInt(e.slice(1).split(".")[0]??"0",10);return{isValid:r>=t,majorVersion:r}},Ge=e=>({yes:!!e.yes,skipInstall:!!e.skipInstall,skipSetup:!!e.skipSetup}),Qe={validateProjectName:He,validateNodeVersion:We,sanitizeOptions:Ge},g=Qe;var ze=()=>{let e=process.version,{isValid:t}=g.validateNodeVersion(e,D),r=[];return t||r.push(`Node.js version is too old (${e}). Please install Node.js ${D} or higher.`,"Download from: https://nodejs.org/"),{isValid:t,version:e,issues:r}},Je=()=>{let e=[];try{return Y("docker --version",{stdio:"pipe"}),{isValid:!0,issues:[]}}catch{return e.push("Docker is not installed.","Please install Docker Desktop from: https://docker.com","After installation, ensure Docker Desktop is running."),{isValid:!1,issues:e}}},Ze=()=>{let e=[];try{return Y("docker info",{stdio:"pipe"}),{isValid:!0,isRunning:!0,issues:[]}}catch{return e.push("Docker is installed but not running.","Please start Docker Desktop and try again."),{isValid:!1,isRunning:!1,issues:e}}},X=async()=>{s.step("\u{1F50D} Checking prerequisites...");let e=!1;for(;!e;){let t=!1,r=[],n=ze();n.isValid?s.success(`\u2705 Node.js version: ${n.version}`):(s.error(`\u274C Node.js version: ${n.version}`),t=!0,r.push(...n.issues));let o=Je();if(o.isValid){s.success("\u2705 Docker: Installed");let i=Ze();i.isRunning?s.success("\u2705 Docker: Running"):(s.error("\u274C Docker: Not running"),t=!0,r.push(...i.issues))}else s.error("\u274C Docker: Not installed"),t=!0,r.push(...o.issues);t?(s.error(`
\u274C Prerequisite check failed. Please fix the following issues:
`),s.list(r.map(i=>`\u2022 ${i}`),"yellow"),s.warning(`
\u23F3 Press any key when you've fixed the issues and want to retry...`),await f.waitForUserInput(),s.step(`
\u{1F50D} Rechecking prerequisites...`)):(e=!0,s.success(`
\u2705 All prerequisites met!
`))}};import{existsSync as O}from"fs";import{dirname as rt,resolve as b}from"path";import ot from"prompts";import{fileURLToPath as nt}from"url";var _=(e,t,r,n=[])=>{let o=new Error(e);return o.name=t,Object.defineProperty(o,"code",{value:r,enumerable:!0,configurable:!1,writable:!1}),Object.defineProperty(o,"suggestions",{value:n,enumerable:!0,configurable:!1,writable:!1}),o};var d=(e,t=[])=>_(e,"ProjectCreationError","PROJECT_CREATION_ERROR",t),H=(e,t=[])=>_(e,"EnvironmentError","ENVIRONMENT_ERROR",t),W=(e,t=[])=>_(e,"ContainerError","CONTAINER_ERROR",t),R=(e,t=[])=>_(e,"DatabaseError","DATABASE_ERROR",t);import{existsSync as G}from"fs";import{copyFile as Q,mkdir as z,readdir as et}from"fs/promises";import{join as P}from"path";var J=async(e,t)=>{try{G(t)||await z(t,{recursive:!0});let r=await et(e,{withFileTypes:!0});await Promise.all(r.map(async n=>{let o=P(e,n.name),i=P(t,n.name);n.isDirectory()?await J(o,i):await Q(o,i)}))}catch{throw d(`Failed to copy directory from ${e} to ${t}`,["Check file permissions","Ensure source directory exists","Verify sufficient disk space"])}},Z=async e=>{try{return G(e)}catch{return!1}},ee=async e=>{try{await z(e,{recursive:!0})}catch{throw d(`Failed to create directory: ${e}`,["Check directory permissions","Ensure parent directory exists","Verify disk space is available"])}},tt=async(e,t,r)=>{let n=e.map(async i=>{let a=P(t,i),l=P(r,i);if(await Z(a)){let c=P(l,"..");return await ee(c),await Q(a,l),1}return 0});return(await Promise.all(n)).reduce((i,a)=>i+a,0)},st={copyDirectory:J,fileExists:Z,createDirectory:ee,copyFiles:tt},m=st;var it=nt(import.meta.url),te=rt(it),at=async(e,t)=>{if(e){let o=g.validateProjectName(e);if(!o.isValid)throw d(o.errors[0]||"Invalid project name");return e}if(t?.yes)return"my-t3-chill-app";let n=await ot({type:"text",name:"projectName",message:"Enter a name for your project",initial:"my-t3-chill-app",validate:o=>{let i=g.validateProjectName(o);return i.isValid?O(o)?"Directory already exists":!0:i.errors[0]||"Invalid project name"}});if(!n.projectName)throw d("Project creation was cancelled by the user");return n.projectName},ct=()=>{let e=[b(te,"../template"),b(te,"../../template"),b(process.cwd(),"template"),b(process.cwd(),"cli/template")];for(let t of e)if(O(t))return t;throw d("Template files not found",["Run the build script: npm run build","Check if you're in the correct directory","Verify template directory exists"])},se=async(e,t)=>{s.step("\u{1F4C1} Creating project...");let r=await at(e,t),n=b(process.cwd(),r);if(O(n))throw d(`Directory "${r}" already exists`,["Choose a different project name","Remove the existing directory",`Try: rm -rf ${r} (use with caution)`]);let o=ct();await m.createDirectory(n),s.gray(" Copying template files...");try{return await m.copyDirectory(o,n),s.success("\u2705 Project created successfully!"),s.gray(` Location: ${n}
`),{name:r,path:n,templatePath:o}}catch{throw d("Failed to copy template files",["Check file permissions","Ensure sufficient disk space","Verify template directory is complete"])}};import{execSync as gt}from"child_process";import{execSync as lt}from"child_process";var pt=(e,t,r={})=>{try{return lt(e,{cwd:t,stdio:r.stdio||"inherit",timeout:r.timeout||12e4}),!0}catch{return!1}},ut={runCommand:pt},h=ut;import{readFile as U,writeFile as C}from"fs/promises";import{join as y}from"path";var I=async(e,t)=>{let r=y(e,"supabase","config.toml");try{let o=await U(r,"utf8");o=o.replace(/(\[api\][\s\S]*?)^port = \d+$/m,`$1port = ${t.api}`),o=o.replace(/(\[db\][\s\S]*?)^port = \d+$/m,`$1port = ${t.db}`),o=o.replace(/(\[db\][\s\S]*?)^shadow_port = \d+$/m,`$1shadow_port = ${t.shadowDb}`),o=o.replace(/(\[studio\][\s\S]*?)^port = \d+$/m,`$1port = ${t.studio}`),o=o.replace(/(\[inbucket\][\s\S]*?)^port = \d+$/m,`$1port = ${t.inbucket}`),o=o.replace(/(\[analytics\][\s\S]*?)^port = \d+$/m,`$1port = ${t.analytics}`),o=o.replace(/^api_url = "http:\/\/127\.0\.0\.1"$/m,`api_url = "http://127.0.0.1:${t.api}"`),await C(r,o,"utf8"),s.success(`\u2705 Updated supabase/config.toml with new ports
`)}catch(n){throw s.error(`\u274C Failed to update supabase/config.toml: ${n}`),new Error(`Failed to update Supabase configuration: ${n}`)}},T=async(e,t)=>{let r=y(e,".env"),n=y(e,".env.local");try{await re(r,t,"env"),await re(n,t,"env.local"),s.success(`\u2705 Updated environment files with new ports
`)}catch(o){throw s.error(`\u274C Failed to update environment files: ${o}`),new Error(`Failed to update environment files: ${o}`)}},re=async(e,t,r)=>{try{let n=await U(e,"utf8");if(r==="env"){let o=`postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres`,i=`postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres`;n=n.replace(/^DATABASE_URL=.*$/m,`DATABASE_URL="${o}"`).replace(/^DIRECT_URL=.*$/m,`DIRECT_URL="${i}"`)}else{let o=`http://127.0.0.1:${t.api}`;n=n.replace(/^NEXT_PUBLIC_SUPABASE_URL=.*$/m,`NEXT_PUBLIC_SUPABASE_URL="${o}"`)}await C(e,n,"utf8")}catch(n){if(n.code!=="ENOENT")throw n}},oe=async(e,t)=>{let r=y(e,".env.example"),n=y(e,".env.local.example");try{let o=`# Database Configuration (Generated with port: ${t.db})
DATABASE_URL="postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres"
DIRECT_URL="postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres"
`;await C(r,o,"utf8");let i=`# Supabase Configuration (Generated with API port: ${t.api})
NEXT_PUBLIC_SUPABASE_URL="http://127.0.0.1:${t.api}"
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY="[LOCAL_ANON_KEY]"
# Environment
NODE_ENV="development"
# Stripe Configuration (Optional)
STRIPE_SECRET_KEY="[STRIPE_SECRET_KEY]"
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="[NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY]"
NEXT_PUBLIC_STRIPE_PRICE_ID="[NEXT_PUBLIC_STRIPE_PRICE_ID]"
STRIPE_WEBHOOK_SECRET="[STRIPE_WEBHOOK_SECRET]"
`;await C(n,i,"utf8"),s.success(`
\u2705 Updated environment template files with port-specific configurations`)}catch(o){throw s.error(`\u274C Failed to update environment templates: ${o}`),new Error(`Failed to update environment templates: ${o}`)}},ne=async e=>{let t=y(e,"supabase","config.toml");try{let r=await U(t,"utf8"),n=r.match(/^shadow_port = (\d+)$/m),i=r.match(/\[api\][\s\S]*?(?=\[|$)/)?.[0]?.match(/^port = (\d+)$/m),a=i&&i[1]?parseInt(i[1]):54321,c=r.match(/\[db\][\s\S]*?(?=\[|$)/)?.[0]?.match(/^port = (\d+)$/m),p=c&&c[1]?parseInt(c[1]):54322,k=n&&n[1]?parseInt(n[1]):54320,x=r.match(/\[studio\][\s\S]*?(?=\[|$)/)?.[0]?.match(/^port = (\d+)$/m),Re=x&&x[1]?parseInt(x[1]):54323,L=r.match(/\[inbucket\][\s\S]*?(?=\[|$)/)?.[0]?.match(/^port = (\d+)$/m),Ce=L&&L[1]?parseInt(L[1]):54324,N=r.match(/\[analytics\][\s\S]*?(?=\[|$)/)?.[0]?.match(/^port = (\d+)$/m),Ie=N&&N[1]?parseInt(N[1]):54327;return{api:a,db:p,shadowDb:k,studio:Re,inbucket:Ce,analytics:Ie}}catch{return null}};import{exec as mt}from"child_process";import{promisify as dt}from"util";var B=dt(mt),E={api:54321,db:54322,studio:54323,inbucket:54324,analytics:54327,shadowDb:54320},V=async e=>{try{if(process.platform==="win32"){let{stdout:r}=await B(`netstat -an | findstr :${e}`);return r.trim()===""}else try{let{stdout:r}=await B(`lsof -i :${e}`);return r.trim()===""}catch{let{stdout:r}=await B(`netstat -an | grep :${e}`);return r.trim()===""}}catch{return!0}},ft=async(e,t=10)=>{for(let r=0;r<t;r++){let n=e+r;if(await V(n))return n}throw new Error(`Could not find an available port starting from ${e} (tried ${t} ports)`)},A=async(e=!1)=>{e?s.info(`
\u{1F504} Re-checking port availability...`):s.step("\u{1F50D} Checking port availability...");let t={api:E.api,db:E.db,studio:E.studio,inbucket:E.inbucket,analytics:E.analytics,shadowDb:E.shadowDb},r=new Set,n=0;for(let[o,i]of Object.entries(E))if(!await V(i)||r.has(i)){n++;try{let l=i+1,c;do c=await ft(l),l=c+1;while(r.has(c));t[o]=c,r.add(c),s.info(` \u{1F4CD} ${o}: ${i} \u2192 ${c} (conflict resolved)`)}catch(l){throw s.error(`\u274C Could not find available port for ${o}: ${l}`),new Error(`Port allocation failed for ${o}`)}}else r.add(i),s.info(` \u2705 ${o}: ${i} (available)`);return n>0?s.success(`
\u2705 Port allocation completed (${n} conflicts resolved)`):s.success("\u2705 All default ports are available"),t},ie=async e=>{let t=[];for(let[r,n]of Object.entries(e))await V(n)||t.push(`${r}: ${n}`);return{isValid:t.length===0,conflicts:t}};var ae=e=>{try{return gt("npx supabase status",{cwd:e,stdio:"pipe"}),!0}catch{return!1}},ce=async e=>{if(s.step("\u{1F433} Starting Supabase containers..."),ae(e)){s.success("\u2705 Supabase containers are already running");let i=await ne(e);if(i)return i}let t=await A();await I(e,t),await T(e,t);let r=3,n=null;for(let i=1;i<=r;i++)try{s.info(`\u{1F680} Starting containers (attempt ${i}/${r})...`);let a=await ie(t);if(!a.isValid&&(s.warning(`\u26A0\uFE0F Port conflicts detected: ${a.conflicts.join(", ")}`),i<r)){s.info("\u{1F504} Finding new ports and retrying...");let c=await A(!0);await I(e,c),await T(e,c),Object.assign(t,c);continue}if(h.runCommand("npx supabase start",e,{timeout:12e4}))return s.success(`
\u2705 Supabase containers started successfully`),ae(e)||s.warning("\u26A0\uFE0F Containers started but status check failed"),s.step("\u{1F517} Supabase setup completed"),s.info("\u{1F4CD} Services running on:"),s.list([`API: http://127.0.0.1:${t.api}`,`Database: postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres`,`Studio: http://127.0.0.1:${t.studio}`,`Inbucket: http://127.0.0.1:${t.inbucket}`],"cyan"),t;throw new Error("Supabase start command failed")}catch(a){if(n=a,s.warning(`
\u26A0\uFE0F Attempt ${i} failed: ${n.message}`),i<r){s.info(`\u{1F504} Retrying with new port allocation...
`);try{h.runCommand("npx supabase stop",e,{timeout:3e4})}catch{}let l=await A(!0);await I(e,l),await T(e,l),Object.assign(t,l)}}let o=["Ensure Docker Desktop is running","Check if other applications are using the required ports","Try manually running: npx supabase stop && npx supabase start","Check Docker logs for more details","Ensure no other Supabase instances are running"];throw W(`Failed to start Supabase containers after ${r} attempts. Last error: ${n?.message}`,o)};import{readdir as me}from"fs/promises";import{join as de}from"path";var le=async e=>{s.step("\u{1F4E6} Installing dependencies...");let t=h.runCommand("npm install",e,{timeout:3e5});return t?s.step("\u2705 Dependencies installed successfully"):(s.error("\u274C Failed to install dependencies"),s.error(" Try running: npm install manually"),s.error(" Common issues:"),s.error(" \u2022 Check internet connection"),s.error(" \u2022 Clear npm cache: npm cache clean --force"),s.error(" \u2022 Delete node_modules and package-lock.json")),t},pe=async e=>{let t=h.runCommand("npm run prisma:generate",e);return t||(s.error("\u274C Failed to generate Prisma client"),s.error(" Try running: npm run prisma:generate manually"),s.error(" Make sure Prisma schema is valid")),t},ue=async e=>{s.step("\u{1F5C4}\uFE0F Resetting database...");let t=h.runCommand("npm run prisma:reset -- --force",e);return t||(s.error("\u274C Failed to reset database"),s.error(" Try running: npm run prisma:reset manually"),s.error(" Make sure database connection is working")),t};var ht=async e=>{try{let t=de(e,"prisma","migrations");if(!await m.fileExists(t))return!1;let r=await me(t);for(let n of r){let o=de(t,n);if((await me(o)).includes("migration.sql"))return!0}return!1}catch{return!1}},fe=async e=>{s.step("\u{1F5C4}\uFE0F Setting up database...");try{if(await pe(e),!await ht(e))throw R("No valid migration files found",["Template wasn't built correctly","Run: npm run build to rebuild the CLI template","Check if prisma/migrations directory exists"]);if(await ue(e))s.success("\u2705 Database setup completed");else throw R("Failed to reset database",["Ensure database is not locked by another process","Check if Supabase containers are running properly","Manual commands to try:"," npm run prisma:generate"," npm run prisma:reset -- --force"])}catch(t){throw t&&typeof t=="object"&&"name"in t&&t.name==="DatabaseError"?t:R("Database setup failed",["Database connection issues","Supabase containers not running","Environment variables incorrect"])}};import{readFile as ge}from"fs/promises";import{join as v}from"path";var he=async e=>{try{let t=v(e,".env"),r=v(e,".env.local"),n=await ge(t,"utf8"),o=await ge(r,"utf8"),i=[],a=[],l=[];for(let p of q)n.includes(`${p}=`)||i.push(`${p} (in .env)`);for(let p of M)o.includes(`${p}=`)||i.push(`${p} (in .env.local)`);for(let p of K)o.includes(`${p}=`)||a.push(`${p} (in .env.local)`);let c=["[LOCAL_ANON_KEY]","[NEXT_PUBLIC_SUPABASE_URL]","[NODE_ENV]","[STRIPE_SECRET_KEY]","[NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY]","[NEXT_PUBLIC_STRIPE_PRICE_ID]","[STRIPE_WEBHOOK_SECRET]"];for(let p of c)if(o.includes(p)){let k=p.replace(/[\[\]]/g,"");l.push(k)}return{requiredMissing:i,optionalMissing:a,hasPlaceholders:l.length>0,placeholderVars:l}}catch{throw H("Failed to read environment files",["Ensure .env and .env.local files exist","Check file permissions"])}};var Et=async e=>{let t=[".env",".env.local"];for(let r of t){let n=v(e,`${r}.example`);await m.fileExists(n)?(await m.copyFiles([`${r}.example`],e,e),s.success(`\u2705 Copied ${r}.example to ${r}`)):s.warning(`\u26A0\uFE0F Missing ${r}.example file`)}},Ee=async e=>{s.step("\u{1F4CB} Checking environment files...");let t=v(e,".env"),r=v(e,".env.local");!await m.fileExists(t)||!await m.fileExists(r)?(s.warning("\u{1F527} Environment files missing, copying from examples..."),await Et(e),s.success("\u2705 Environment files created from examples")):s.success("\u2705 Environment files found")},ye=async e=>{let t=await he(e),r=[];r.push(...t.requiredMissing);let n=t.placeholderVars.filter(o=>o==="LOCAL_ANON_KEY"||o==="NEXT_PUBLIC_SUPABASE_URL"||o==="NODE_ENV");if(n.length>0&&r.push(...n.map(o=>`${o} (has placeholder value)`)),r.length>0)return s.error("\u274C Required environment variables need attention:"),s.list(r.map(o=>`\u2022 ${o}`),"yellow"),s.info(`
\u{1F64C} These variables are required for the app to function:`),s.list(["DATABASE_URL - Database connection string (in .env)","DIRECT_URL - Direct database connection (in .env)","NEXT_PUBLIC_SUPABASE_URL - Supabase project URL (in .env.local)","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_OR_ANON_KEY - Supabase anon key (in .env.local)","NODE_ENV - Environment mode (in .env.local)"],"cyan"),n.length>0&&(s.info(`
\u{1F4A1} For placeholder values:`),s.list(["LOCAL_ANON_KEY - Get from Supabase container status above","NEXT_PUBLIC_SUPABASE_URL - Usually http://127.0.0.1:54321 for local","NODE_ENV - Set to 'development' for local development"],"cyan")),s.warning(`
\u23F3 Please update your environment files and press any key to retry...`),await f.waitForUserInput(),ye(e);s.success("\u2705 Required environment variables validated")},Pe=async e=>{let t=await he(e),r=[];r.push(...t.optionalMissing);let n=t.placeholderVars.filter(o=>o==="STRIPE_SECRET_KEY"||o==="NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY"||o==="NEXT_PUBLIC_STRIPE_PRICE_ID"||o==="STRIPE_WEBHOOK_SECRET");if(n.length>0&&r.push(...n.map(o=>`${o} (has placeholder value)`)),r.length>0){if(s.warning(`
\u26A0\uFE0F Optional Stripe environment variables need attention:`),s.list(r.map(i=>`\u2022 ${i}`),"yellow"),s.info(`
\u{1F4A1} Stripe integration is optional and includes:`),s.list(["Payment processing and subscriptions","Protected content based on subscription status","Webhook handling for real-time updates"],"cyan"),s.info(`
To set up Stripe (optional):`),s.list(["1. Create a Stripe account at https://stripe.com","2. Get your secret and publishable keys from the dashboard","3. Create a product and get the price ID","4. Set up webhooks and get the webhook secret","5. Add all keys to your .env.local file"],"cyan"),s.info(`
Required Stripe variables for full integration:`),s.list(["STRIPE_SECRET_KEY - Your secret key (in .env.local)","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY - Your publishable key (in .env.local)","NEXT_PUBLIC_STRIPE_PRICE_ID - Product price ID (in .env.local)","STRIPE_WEBHOOK_SECRET - Webhook endpoint secret (in .env.local)"],"gray"),s.warning(`
\u2753 Do you want to continue without Stripe? (Y/n)`),!await f.askYesNo(!0))return s.warning(`
\u23F3 Please add your Stripe environment variables and press any key to retry...`),await f.waitForUserInput(),Pe(e);s.info("\u2705 Continuing without Stripe integration")}else s.success("\u2705 Stripe environment variables found")},be=async e=>{s.step("\u{1F527} Validating environment variables..."),await ye(e),await Pe(e),s.success("\u2705 Environment setup completed")};var ve=()=>{s.step("\u{1F4B3} Stripe Setup Instructions"),s.info("To complete Stripe integration:"),s.list(["1. Create a Stripe account at https://stripe.com","2. Get your API keys from the Stripe Dashboard","3. Add them to your .env file:"," \u2022 STRIPE_SECRET_KEY=sk_test_..."," \u2022 NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...","4. Configure your webhook endpoint (for production)"],"cyan"),s.info(`
\u{1F4A1} For development:`),s.list(["\u2022 Use test keys (they start with sk_test_ and pk_test_)","\u2022 Test payments with Stripe's test card numbers","\u2022 Visit: https://stripe.com/docs/testing"],"cyan")};var yt=(e,t)=>{s.success(`
\u{1F389} Project setup completed successfully!
`),s.info("Your T3 Chill App is ready! Here's what's been set up:"),s.list(["\u2705 Dependencies installed","\u2705 Supabase containers running","\u2705 Database initialized with Prisma","\u2705 Environment variables configured"],"cyan"),t&&(s.info(`
\u{1F310} Services are running on:`),s.list([`API Server: http://127.0.0.1:${t.api}`,`Database: postgresql://postgres:postgres@127.0.0.1:${t.db}/postgres`,`Studio Dashboard: http://127.0.0.1:${t.studio}`,`Email Testing: http://127.0.0.1:${t.inbucket}`],"cyan")),s.step("\u{1F680} Next steps:"),s.list([`cd ${e.name}`,"npm run dev","Open http://localhost:3000"],"cyan"),s.success(`
\u{1F499} Thank you for using Create T3 Chill App!
`),s.info("If you like this project, please give it a \u2B50\uFE0F on GitHub:"),s.cyan(` https://github.com/yasindunethmina/create-t3-chill-app
`),s.info("Questions? Need help? I'm here for you!"),s.gray(`Reach out on social media - I'm also open for new opportunities:
`),s.list(["\u{1D54F} (Twitter): https://x.com/yasinduneth","LinkedIn: https://www.linkedin.com/in/yasinduneth"],"cyan"),s.success(`
Happy building! \u{1F680}
`)},Pt=()=>{s.printWelcome(),s.step("\u{1F64F} Credits & Resources"),s.info("Built with love using:"),s.list(["Next.js - https://nextjs.org","Tailwind CSS - https://tailwindcss.com","Supabase - https://supabase.com","tRPC - https://trpc.io","Prisma - https://prisma.io","Stripe - https://stripe.com"],"cyan")},bt=e=>{e instanceof Error?(s.error(`
\u274C Setup failed: ${e.message}`),"suggestions"in e&&Array.isArray(e.suggestions)&&(s.info(`
\u{1F4A1} Troubleshooting suggestions:`),s.list(e.suggestions.map(t=>`\u2022 ${t}`)))):s.error(`
\u274C Setup failed: ${String(e)}`),s.info(`
\u{1F504} You can try running the setup again with:`),s.list(["npm run dev (to start development)","Or re-run this CLI to try setup again"])},we=async(e,t)=>{try{if(Pt(),!t.skipInstall&&!await le(e.path))throw new Error("Failed to install dependencies");let r=await ce(e.path);await oe(e.path,r),await Ee(e.path),await be(e.path),await fe(e.path),ve(),yt(e,r)}catch(r){throw bt(r),r}};var vt=e=>{s.success(`
\u2705 Project created successfully!
`),s.info("Next steps:"),s.list([`cd ${e}`,"npm install","npm run dev"],"cyan"),s.gray(`
Setup was skipped. Please run the setup manually when ready.`),s.success(`
\u{1F499} Thank you for using Create T3 Chill App!
`),s.info("If you like this project, please give it a \u2B50\uFE0F on GitHub:"),s.cyan(` https://github.com/yasindunethmina/create-t3-chill-app
`),s.info("Questions? Need help? I'm here for you!"),s.gray(`Reach out on social media - I'm also open for new opportunities:
`),s.list(["\u{1D54F} (Twitter): https://x.com/yasinduneth","LinkedIn: https://www.linkedin.com/in/yasinduneth"],"cyan"),s.success(`
Happy building! \u{1F680}
`)},wt=e=>{s.error(`
\u274C Critical error occurred:`),e instanceof Error?(s.error(` ${e.message}`),e.message.includes("ENOENT")||e.message.includes("not found")?(s.info(`
\u{1F4A1} This might be due to:`),s.list(["Missing template files (run build script)","Invalid project path","File system permissions"])):e.message.includes("EACCES")||e.message.includes("permission")?(s.info(`
\u{1F4A1} Permission denied. Try:`),s.list(["Running with appropriate permissions","Checking directory permissions"])):(e.message.includes("network")||e.message.includes("timeout"))&&(s.info(`
\u{1F4A1} Network issue. Please:`),s.list(["Check your internet connection","Try again in a few moments"]))):s.error(` ${String(e)}`),s.info(`
\u{1F504} You can try running the command again.`),process.exit(1)},Se=async(e,t={})=>{try{await X();let r=await se(e,t);t.skipSetup?vt(r.path):await we(r,t)}catch(r){wt(r)}};f.setupGracefulExit();var _e=new St;_e.name(S.name).description(S.description).version(S.version).argument("[project-name]","Name of the project directory").option("-y, --yes","Skip prompts and use defaults").option("--skip-install","Skip installing dependencies").option("--skip-setup","Skip database and container setup").action(async(e,t)=>{let r=g.sanitizeOptions(t);await Se(e,r)});_e.parse();
//# sourceMappingURL=index.js.map