next-ts-cli 1.0.13 → 1.0.15

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
@@ -37,7 +37,7 @@ pnpm create next-ts-cli
37
37
  yarn create next-ts-cli
38
38
 
39
39
  # bun
40
- bunx next-ts-cli
40
+ bun next-ts-cli
41
41
  ```
42
42
  The CLI will guide you through the setup process:
43
43
 
@@ -127,36 +127,18 @@ The core project structure contains the following features:
127
127
 
128
128
  ```bash
129
129
  # Create with defaults
130
- npx next-ts-cli my-app -d
130
+ npx next-ts-cli my-app --cli -d
131
131
 
132
132
  # Create with specific packages (CLI mode)
133
133
  npx next-ts-cli my-app --cli --drizzle --betterauth --stripe
134
134
 
135
135
  # Custom import alias
136
- npx next-ts-cli my-app -a "~/"
136
+ npx next-ts-cli my-app --cli -a "~/"
137
137
 
138
138
  # Full stack example
139
139
  npx next-ts-cli my-app --cli --drizzle --betterauth --stripe --docker --shadcnui
140
140
  ```
141
141
 
142
- ---
143
-
144
- ## Core tech stack
145
-
146
- ### Base Template Includes
147
-
148
- | Technology | Version | Description |
149
- |------------|---------|-------------|
150
- | [Next.js](https://nextjs.org/) | 16.x | React framework with App Router |
151
- | [React](https://react.dev/) | 19.x | UI library |
152
- | [TypeScript](https://www.typescriptlang.org/) | 5.x | Type safety |
153
- | [Tailwind CSS](https://tailwindcss.com/) | 4.x | Utility-first CSS |
154
- | [Biome](https://biomejs.dev/) | 2.x | Linter and formatter |
155
- | [Jest](https://jestjs.io/) | 30.x | Testing framework |
156
- | [Framer Motion](https://www.framer.com/motion/) | 12.x | Animation library |
157
- | [Husky](https://typicode.github.io/husky/) | 9.x | Git hooks |
158
- | [Commitlint](https://commitlint.js.org/) | 20.x | Commit message linting |
159
-
160
142
  ## Contributing
161
143
 
162
144
  Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
- import Oe from"path";import{execa as Pt}from"execa";import Be from"fs-extra";import*as m from"@clack/prompts";import{Command as We}from"commander";import H from"path";import{fileURLToPath as Ge}from"url";var Le=Ge(import.meta.url),Je=H.dirname(Le),p=H.join(Je,"../"),Y=`
2
+ import De from"path";import{execa as At}from"execa";import Ge from"fs-extra";import*as f from"@clack/prompts";import{Command as He}from"commander";import q from"path";import{fileURLToPath as Je}from"url";var Ve=Je(import.meta.url),$e=q.dirname(Ve),p=q.join($e,"../"),Q=`
3
3
  __ __ __ __
4
4
  .-----.-----.--.--| |_ ______| |_.-----.______.----| |__|
5
5
  | | -__|_ _| _|______| _|__ --|______| __| | |
6
6
  |__|__|_____|__.__|____| |____|_____| |____|__|__|
7
- `,R="next-ts-cli",G="next-ts-cli";import Ve from"path";import $e from"fs-extra";var _=()=>{let e=Ve.join(p,"package.json");return $e.readJSONSync(e).version??"1.0.0"};var E=class extends Error{constructor(t){super(t)}};import U from"chalk";var a={error(...e){console.log(U.red(...e))},warn(...e){console.log(U.yellow(...e))},info(...e){console.log(U.cyan(...e))},success(...e){console.log(U.green(...e))}};var M=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var Ke=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,X=e=>{if(!e)return;let t=M(e),o=t.split("/"),n=o.findIndex(r=>r.startsWith("@")),s=o[o.length-1];if(o.findIndex(r=>r.startsWith("@"))!==-1&&(s=o.slice(n).join("/")),!(t==="."||Ke.test(s??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var q=e=>{if(e.startsWith(".")||e.startsWith("/"))return"Import alias can't start with '.' or '/'"};var C={appName:R,packages:[],flags:{default:!1,cli:!1,drizzle:!1,supabase:!1,betterauth:!1,alias:"@/",docker:!1,stripe:!1,shadcnui:!1,clerk:!1,neon:!1,vercelAi:!1}},Q=async()=>{let e=C,t=new We().name(G).description("A CLI for creating production-ready web applications with the Next.js").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--cli","Run in CLI mode (non-interactive)",!1).option("-d, --default","Scaffold the project without any additional integrations",!1).option("-a, --alias [alias]","Explicitly tell the CLI to use a custom import alias",C.flags.alias).option("--drizzle [boolean]","Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--docker [boolean]","Experimental: Boolean value if we should install Docker. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--stripe [boolean]","Experimental: Boolean value if we should install Stripe. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--shadcnui [boolean]","Experimental: Boolean value if we should install Shadcn UI. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--betterauth [boolean]","Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--cli`. Not valid with --clerk",n=>!!n&&n!=="false").option("--clerk [boolean]","Experimental: Boolean value if we should install Clerk.js . Must be used in conjunction with `--cli`. Not valid with --betterAuth",n=>!!n&&n!=="false").option("--supabase [boolean]","Experimental: Boolean value if we should install Supabase. Must be used in conjunction with `--cli`. Not valid with --betterAuth or --clerk",n=>!!n&&n!=="false").option("--neon [boolean]","Experimental: Boolean value if we should install Neon. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--vercelAi [boolean]","Experimental: Boolean value if we should install Vercel AI Gateway. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").version(_(),"-v, --version","Display the version number").parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&a.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
7
+ `,U="next-ts-cli",V="next-ts-cli";import Ke from"path";import We from"fs-extra";var x=()=>{let e=Ke.join(p,"package.json");return We.readJSONSync(e).version??"1.0.0"};var C=class extends Error{constructor(t){super(t)}};import M from"chalk";var a={error(...e){console.log(M.red(...e))},warn(...e){console.log(M.yellow(...e))},info(...e){console.log(M.cyan(...e))},success(...e){console.log(M.green(...e))}};var N=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var Fe=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,Z=e=>{if(!e)return;let t=N(e),s=t.split("/"),n=s.findIndex(i=>i.startsWith("@")),o=s[s.length-1];if(s.findIndex(i=>i.startsWith("@"))!==-1&&(o=s.slice(n).join("/")),!(t==="."||Fe.test(o??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var ee=e=>{if(e.startsWith(".")||e.startsWith("/"))return"Import alias can't start with '.' or '/'"};var j={appName:U,packages:[],flags:{default:!1,cli:!1,drizzle:!1,supabase:!1,betterauth:!1,alias:"@/",docker:!1,stripe:!1,shadcnui:!1,clerk:!1,neon:!1,vercelAi:!1}},te=async()=>{let e=j,t=new He().name(V).description("A CLI for creating production-ready web applications with the Next.js").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--cli","Run in CLI mode (non-interactive)",!1).option("-d, --default","Scaffold the project without any additional integrations",!1).option("-a, --alias [alias]","Explicitly tell the CLI to use a custom import alias",j.flags.alias).option("--drizzle [boolean]","Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--docker [boolean]","Experimental: Boolean value if we should install Docker. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--stripe [boolean]","Experimental: Boolean value if we should install Stripe. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--shadcnui [boolean]","Experimental: Boolean value if we should install Shadcn UI. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--betterauth [boolean]","Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--cli`. Not valid with --clerk",n=>!!n&&n!=="false").option("--clerk [boolean]","Experimental: Boolean value if we should install Clerk.js . Must be used in conjunction with `--cli`. Not valid with --betterAuth",n=>!!n&&n!=="false").option("--supabase [boolean]","Experimental: Boolean value if we should install Supabase. Must be used in conjunction with `--cli`. Not valid with --betterAuth or --clerk",n=>!!n&&n!=="false").option("--neon [boolean]","Experimental: Boolean value if we should install Neon. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--vercelAi [boolean]","Experimental: Boolean value if we should install Vercel AI Gateway. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").version(x(),"-v, --version","Display the version number").parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&a.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
8
8
  and likely to result in a crash. Please run next-ts-cli with another
9
- package manager such as pnpm, npm, or Yarn Classic.`);let o=t.args[0];if(o&&(e.appName=o),e.flags=t.opts(),e.flags.cli)return e.packages=[],e.flags.docker&&e.packages.push("docker"),e.flags.drizzle&&e.packages.push("drizzle"),e.flags.supabase&&e.packages.push("supabase"),e.flags.betterauth&&e.packages.push("betterAuth"),e.flags.clerk&&e.packages.push("clerk"),e.flags.vercelAi&&e.packages.push("vercelAi"),e.flags.supabase&&e.flags.betterauth&&(a.warn("Incompatible combination Supabase and BetterAuth. Exiting."),process.exit(0)),e.flags.clerk&&e.flags.betterauth&&(a.warn("Incompatible combination Clerk and BetterAuth. Exiting."),process.exit(0)),e.flags.supabase&&e.flags.clerk&&(a.warn("Incompatible combination Supabase and Clerk. Exiting."),process.exit(0)),e.flags.stripe&&e.packages.push("stripe"),e.flags.shadcnui&&e.packages.push("shadcnui"),e;if(e.flags.default)return e;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw a.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
10
- using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`),new E("Non-interactive environment");let n=await m.group({...!o&&{name:()=>m.text({message:"Project name",defaultValue:o,placeholder:C.appName,validate:X})},authentication:()=>m.select({message:"What authentication provider would you like to use?",options:[{value:"none",label:"None"},{value:"better-auth",label:"BetterAuth"},{value:"clerk",label:"Clerk"},{value:"supabase",label:"Supabase"}],initialValue:"none"}),database:({results:i})=>{if(i.authentication!=="supabase")return m.select({message:"What database would you like to use?",options:[{value:"none",label:"None"},{value:"neon",label:"Neon"}],initialValue:"none"})},orm:()=>m.select({message:"What ORM Database would you like to use?",options:[{value:"none",label:"None"},{value:"drizzle",label:"Drizzle"}],initialValue:"none"}),importAlias:()=>m.text({message:"Custom import aliases",defaultValue:C.flags.alias,placeholder:C.flags.alias,validate:q}),additionalConfig:()=>m.multiselect({message:"Select additional tools to install (press enter to skip)",options:[{value:"docker",label:"Docker"},{value:"stripe",label:"Stripe"},{value:"shadcnui",label:"Shadcn UI"},{value:"vercelAi",label:"Vercel AI Gateway"}],required:!1})},{onCancel(){process.exit(1)}}),s=[],r=n.additionalConfig;return n.orm==="drizzle"&&s.push("drizzle"),n.authentication==="better-auth"&&s.push("betterAuth"),n.authentication==="clerk"&&s.push("clerk"),(r.includes("supabase")||n.authentication==="supabase")&&s.push("supabase"),n.database==="neon"&&s.push("neon"),r.includes("docker")&&s.push("docker"),r.includes("stripe")&&s.push("stripe"),r.includes("shadcnui")&&s.push("shadcnui"),r.includes("vercelAi")&&s.push("vercelAi"),{appName:n.name??e.appName,packages:s,flags:{...e.flags,alias:n.importAlias??e.flags.alias}}}catch(n){if(n instanceof E)a.warn(`
11
- ${G} needs an interactive terminal to provide options`),await m.confirm({message:"Continue scaffolding a default next-ts-cli?",initialValue:!0})||(a.info("Exiting..."),process.exit(0)),a.info(`Bootstrapping a default next-ts-cli in ./${e.appName}`);else throw n}return e};import Qe from"path";import Z from"chalk";import Fe from"ora";var ee=e=>{let{packages:t}=e;a.info("Adding boilerplate...");for(let[o,n]of Object.entries(t))if(n.inUse){let s=Fe(`Boilerplating ${o}...`).start();n.installer(e),s.succeed(Z.green(`Successfully setup boilerplate for ${Z.green.bold(o)}`))}a.info("")};import N from"path";import*as k from"@clack/prompts";import y from"chalk";import j from"fs-extra";import He from"ora";var te=async({projectName:e,projectDir:t,pkgManager:o})=>{let n=N.join(p,"template/base"),s=N.join(p,"template");a.info(`
12
- Using: ${y.cyan.bold(o)}
13
- `);let r=He(`Scaffolding in: ${t}...
14
- `).start();if(j.existsSync(t))if(j.readdirSync(t).length===0)e!=="."&&r.info(`${y.cyan.bold(e)} exists but is empty, continuing...
15
- `);else{r.stopAndPersist();let c=await k.select({message:`${y.redBright.bold("Warning:")} ${y.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,options:[{label:"Abort installation (recommended)",value:"abort"},{label:"Clear the directory and continue installation",value:"clear"},{label:"Continue installation and overwrite conflicting files",value:"overwrite"}],initialValue:"abort"});(k.isCancel(c)||c==="abort")&&(r.fail("Aborting installation..."),process.exit(1));let l=await k.confirm({message:`Are you sure you want to ${c==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(k.isCancel(l)||!l)&&(r.fail("Aborting installation..."),process.exit(1)),c==="clear"&&(r.info(`Emptying ${y.cyan.bold(e)} and creating next-ts-cli app..
16
- `),j.emptyDirSync(t))}r.start(),j.copySync(n,t),j.copySync(N.join(s,"_gitignore"),N.join(t,".gitignore"));let i=e==="."?"App":y.cyan.bold(e);r.succeed(`${i} ${y.green("scaffolded successfully!")}
17
- `)};import Ye from"path";import Xe from"fs-extra";var ne=({projectDir:e,projectName:t,packages:o})=>{let n=qe(t,o);Xe.writeFileSync(Ye.join(e,"README.md"),n)},qe=(e,t)=>{let o=[];o.push(`# ${e}
9
+ package manager such as pnpm, npm, or Yarn Classic.`);let s=t.args[0];if(s&&(e.appName=s),e.flags=t.opts(),e.flags.cli)return e.packages=[],e.flags.docker&&e.packages.push("docker"),e.flags.drizzle&&e.packages.push("drizzle"),e.flags.supabase&&e.packages.push("supabase"),e.flags.betterauth&&e.packages.push("betterAuth"),e.flags.clerk&&e.packages.push("clerk"),e.flags.vercelAi&&e.packages.push("vercelAi"),e.flags.supabase&&e.flags.betterauth&&(a.warn("Incompatible combination Supabase and BetterAuth. Exiting."),process.exit(0)),e.flags.clerk&&e.flags.betterauth&&(a.warn("Incompatible combination Clerk and BetterAuth. Exiting."),process.exit(0)),e.flags.supabase&&e.flags.clerk&&(a.warn("Incompatible combination Supabase and Clerk. Exiting."),process.exit(0)),e.flags.stripe&&e.packages.push("stripe"),e.flags.shadcnui&&e.packages.push("shadcnui"),e;if(e.flags.default)return e;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw a.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
10
+ using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`),new C("Non-interactive environment");let n=await f.group({...!s&&{name:()=>f.text({message:"Project name",defaultValue:s,placeholder:j.appName,validate:Z})},authentication:()=>f.select({message:"What authentication provider would you like to use?",options:[{value:"none",label:"None"},{value:"better-auth",label:"BetterAuth"},{value:"clerk",label:"Clerk"},{value:"supabase",label:"Supabase"}],initialValue:"none"}),database:({results:r})=>{if(r.authentication!=="supabase")return f.select({message:"What database would you like to use?",options:[{value:"none",label:"None"},{value:"neon",label:"Neon"}],initialValue:"none"})},orm:()=>f.select({message:"What ORM Database would you like to use?",options:[{value:"none",label:"None"},{value:"drizzle",label:"Drizzle"}],initialValue:"none"}),importAlias:()=>f.text({message:"Custom import aliases",defaultValue:j.flags.alias,placeholder:j.flags.alias,validate:ee}),additionalConfig:()=>f.multiselect({message:"Select additional tools to install (press enter to skip)",options:[{value:"docker",label:"Docker"},{value:"stripe",label:"Stripe"},{value:"shadcnui",label:"Shadcn UI"},{value:"vercelAi",label:"Vercel AI Gateway"}],required:!1})},{onCancel(){process.exit(1)}}),o=[],i=n.additionalConfig;return n.orm==="drizzle"&&o.push("drizzle"),n.authentication==="better-auth"&&o.push("betterAuth"),n.authentication==="clerk"&&o.push("clerk"),(i.includes("supabase")||n.authentication==="supabase")&&o.push("supabase"),n.database==="neon"&&o.push("neon"),i.includes("docker")&&o.push("docker"),i.includes("stripe")&&o.push("stripe"),i.includes("shadcnui")&&o.push("shadcnui"),i.includes("vercelAi")&&o.push("vercelAi"),{appName:n.name??e.appName,packages:o,flags:{...e.flags,alias:n.importAlias??e.flags.alias}}}catch(n){if(n instanceof C)a.warn(`
11
+ ${V} needs an interactive terminal to provide options`),await f.confirm({message:"Continue scaffolding a default next-ts-cli?",initialValue:!0})||(a.info("Exiting..."),process.exit(0)),a.info(`Bootstrapping a default next-ts-cli in ./${e.appName}`);else throw n}return e};import et from"path";import ne from"chalk";import Ye from"ora";var se=e=>{let{packages:t}=e;a.info("Adding boilerplate...");for(let[s,n]of Object.entries(t))if(n.inUse){let o=Ye(`Boilerplating ${s}...`).start();n.installer(e),o.succeed(ne.green(`Successfully setup boilerplate for ${ne.green.bold(s)}`))}a.info("")};import O from"path";import*as _ from"@clack/prompts";import S from"chalk";import T from"fs-extra";import Xe from"ora";var oe=async({projectName:e,projectDir:t,pkgManager:s})=>{let n=O.join(p,"template/base"),o=O.join(p,"template");a.info(`
12
+ Using: ${S.cyan.bold(s)}
13
+ `);let i=Xe(`Scaffolding in: ${t}...
14
+ `).start();if(T.existsSync(t))if(T.readdirSync(t).length===0)e!=="."&&i.info(`${S.cyan.bold(e)} exists but is empty, continuing...
15
+ `);else{i.stopAndPersist();let c=await _.select({message:`${S.redBright.bold("Warning:")} ${S.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,options:[{label:"Abort installation (recommended)",value:"abort"},{label:"Clear the directory and continue installation",value:"clear"},{label:"Continue installation and overwrite conflicting files",value:"overwrite"}],initialValue:"abort"});(_.isCancel(c)||c==="abort")&&(i.fail("Aborting installation..."),process.exit(1));let l=await _.confirm({message:`Are you sure you want to ${c==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(_.isCancel(l)||!l)&&(i.fail("Aborting installation..."),process.exit(1)),c==="clear"&&(i.info(`Emptying ${S.cyan.bold(e)} and creating next-ts-cli app..
16
+ `),T.emptyDirSync(t))}i.start(),T.copySync(n,t),T.copySync(O.join(o,"_gitignore"),O.join(t,".gitignore"));let r=e==="."?"App":S.cyan.bold(e);i.succeed(`${r} ${S.green("scaffolded successfully!")}
17
+ `)};import qe from"path";import Qe from"fs-extra";var ie=({projectDir:e,projectName:t,packages:s})=>{let n=Ze(t,s);Qe.writeFileSync(qe.join(e,"README.md"),n)},Ze=(e,t)=>{let s=[];s.push(`# ${e}
18
18
 
19
19
  This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/next-ts-cli).
20
20
 
@@ -26,11 +26,11 @@ This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/ne
26
26
  - **Commitlint** \u2014 Commit message linting
27
27
  - **Lint-staged** \u2014 Run linters on staged files
28
28
  - **Biome** \u2014 Linter and formatter
29
- - **Jest** \u2014 Testing framework`);let n=[];t.clerk.inUse&&n.push("- **Clerk** \u2014 Fast authentication"),t.betterAuth.inUse&&n.push("- **BetterAuth** \u2014 Flexible authentication"),t.supabase.inUse&&n.push("- **Supabase** \u2014 Auth & database with real-time capabilities"),t.drizzle.inUse&&n.push("- **Drizzle ORM** \u2014 Type-safe database queries"),t.shadcnui.inUse&&n.push("- **Shadcn UI** \u2014 UI library"),t.stripe.inUse&&n.push("- **Stripe** \u2014 Payment checkout and webhooks boilerplate to easily create SaaS features"),t.docker.inUse&&n.push("- **Docker** \u2014 Containerization"),t.vercelAi.inUse&&n.push("- **Vercel AI Gateway** \u2014 AI capabilities witsh chat API route boilerplate"),n.length>0&&o.push(`
29
+ - **Jest** \u2014 Testing framework`);let n=[];t.clerk.inUse&&n.push("- **Clerk** \u2014 Fast authentication"),t.betterAuth.inUse&&n.push("- **BetterAuth** \u2014 Flexible authentication"),t.supabase.inUse&&n.push("- **Supabase** \u2014 Auth & database with real-time capabilities"),t.drizzle.inUse&&n.push("- **Drizzle ORM** \u2014 Type-safe database queries"),t.shadcnui.inUse&&n.push("- **Shadcn UI** \u2014 UI library"),t.stripe.inUse&&n.push("- **Stripe** \u2014 Payment checkout and webhooks boilerplate to easily create SaaS features"),t.docker.inUse&&n.push("- **Docker** \u2014 Containerization"),t.vercelAi.inUse&&n.push("- **Vercel AI Gateway** \u2014 AI capabilities witsh chat API route boilerplate"),n.length>0&&s.push(`
30
30
  ### Selected Integrations
31
31
 
32
32
  ${n.join(`
33
- `)}`),o.push(`
33
+ `)}`),s.push(`
34
34
  ## Getting Started
35
35
 
36
36
  \`\`\`bash
@@ -48,17 +48,17 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
48
48
  | \`npm run build\` | Build for production |
49
49
  | \`npm run start\` | Start production server |
50
50
  | \`npm run lint\` | Lint code |
51
- | \`npm run test\` | Run tests |`),t.docker.inUse&&o.push("| `npm run docker:build` | Build Docker image |\n| `npm run docker:up` | Start containers |\n| `npm run docker:down` | Stop containers |"),t.drizzle.inUse&&o.push("| `npm run db:generate` | Generate migrations |\n| `npm run db:migrate` | Run migrations |\n| `npm run db:studio` | Open Drizzle Studio |");let s=[];return t.drizzle.inUse&&s.push("DATABASE_URL="),t.betterAuth.inUse&&s.push("BETTER_AUTH_SECRET=","BETTER_AUTH_URL=http://localhost:3000"),t.supabase.inUse&&s.push("NEXT_PUBLIC_SUPABASE_URL=","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="),t.stripe.inUse&&s.push("STRIPE_SECRET_KEY=","STRIPE_WEBHOOK_SECRET=","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="),t.clerk.inUse&&s.push("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=","CLERK_SECRET_KEY="),t.vercelAi.inUse&&s.push("AI_GATEWAY_API_KEY="),o.join(`
52
- `)};var u=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var oe=async({projectName:e,scopedAppName:t,packages:o})=>{let n=u(),s=Qe.resolve(process.cwd(),e);return await te({projectName:e,projectDir:s,pkgManager:n,scopedAppName:t}),ee({projectName:e,scopedAppName:t,projectDir:s,pkgManager:n,packages:o}),ne({projectDir:s,projectName:e,packages:o}),s};import{execSync as V}from"child_process";import L from"path";import*as J from"@clack/prompts";import T from"chalk";import{execa as z}from"execa";import se from"fs-extra";import Ze from"ora";var et=e=>{try{return V("git --version",{cwd:e}),!0}catch{return!1}},tt=e=>se.existsSync(L.join(e,".git")),nt=async e=>{try{return await z("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},ot=()=>{let t=V("git --version").toString().trim().split(" ")[2],o=t?.split(".")[0],n=t?.split(".")[1];return{major:Number(o),minor:Number(n)}},st=()=>V("git config --global init.defaultBranch || echo main").toString().trim(),re=async e=>{if(a.info("Initializing Git..."),!et(e)){a.warn("Git is not installed. Skipping Git initialization.");return}let t=Ze(`Creating a new git repo...
53
- `).start(),o=tt(e),n=await nt(e),s=L.parse(e).name;if(n&&o){if(t.stop(),!await J.confirm({message:`${T.redBright.bold("Warning:")} Git is already initialized in "${s}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){t.info("Skipping Git initialization.");return}se.removeSync(L.join(e,".git"))}else if(n&&!o&&(t.stop(),!await J.confirm({message:`${T.redBright.bold("Warning:")} "${s}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){t.info("Skipping Git initialization.");return}try{let r=st(),{major:i,minor:c}=ot();i<2||i===2&&c<28?(await z("git",["init"],{cwd:e}),await z("git",["symbolic-ref","HEAD",`refs/heads/${r}`],{cwd:e})):await z("git",["init",`--initial-branch=${r}`],{cwd:e}),await z("git",["add","."],{cwd:e}),t.succeed(`${T.green("Successfully initialized and staged")} ${T.green.bold("git")}
54
- `)}catch{t.fail(`${T.bold.red("Failed:")} could not initialize git. Update git to the latest version!
55
- `)}};var ie=async({projectName:e=R})=>{let t=u();a.info("Next steps:"),e!=="."&&a.info(` cd ${e}`),["npm","bun"].includes(t)?a.info(` ${t} run dev`):a.info(` ${t} dev`)};import O from"fs";import rt from"path";function ae(e,t,o){let n=O.readdirSync(e);try{n.forEach(s=>{let r=rt.join(e,s);if(O.statSync(r).isDirectory())ae(r,t,o);else{let c=O.readFileSync(r,"utf8").replace(new RegExp(t,"g"),o);O.writeFileSync(r,c,"utf8")}})}catch(s){a.error(`Error setting import alias: ${s}`)}}var le=(e,t)=>{try{let o=t.replace(/\*/g,"").replace(/[^/]$/,"$&/");ae(e,"@/",o)}catch(o){a.error(`Error setting import alias: ${o}`)}};import it from"crypto";import at from"path";import lt from"fs-extra";var ce=({projectDir:e,packages:t,scopedAppName:o})=>{let n=t?.betterAuth.inUse,s=t?.supabase.inUse,r=t?.drizzle.inUse,i=t?.stripe.inUse,c=t?.clerk.inUse,l=t?.neon.inUse,f=t?.vercelAi.inUse,b=ct(!!n,!!s,!!r,!!i,!!c,!!l,!!f,o),De=at.join(e,".env");lt.writeFileSync(De,b,"utf-8")},ct=(e,t,o,n,s,r,i,c)=>{let l=`
51
+ | \`npm run test\` | Run tests |`),t.docker.inUse&&s.push("| `npm run docker:build` | Build Docker image |\n| `npm run docker:up` | Start containers |\n| `npm run docker:down` | Stop containers |"),t.drizzle.inUse&&s.push("| `npm run db:generate` | Generate migrations |\n| `npm run db:migrate` | Run migrations |\n| `npm run db:studio` | Open Drizzle Studio |");let o=[];return t.drizzle.inUse&&o.push("DATABASE_URL="),t.betterAuth.inUse&&o.push("BETTER_AUTH_SECRET=","BETTER_AUTH_URL=http://localhost:3000"),t.supabase.inUse&&o.push("NEXT_PUBLIC_SUPABASE_URL=","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="),t.stripe.inUse&&o.push("STRIPE_SECRET_KEY=","STRIPE_WEBHOOK_SECRET=","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="),t.clerk.inUse&&o.push("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=","CLERK_SECRET_KEY="),t.vercelAi.inUse&&o.push("AI_GATEWAY_API_KEY="),s.join(`
52
+ `)};var u=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var re=async({projectName:e,scopedAppName:t,packages:s})=>{let n=u(),o=et.resolve(process.cwd(),e);return await oe({projectName:e,projectDir:o,pkgManager:n,scopedAppName:t}),se({projectName:e,scopedAppName:t,projectDir:o,pkgManager:n,packages:s}),ie({projectDir:o,projectName:e,packages:s}),o};import{execSync as W}from"child_process";import $ from"path";import*as K from"@clack/prompts";import z from"chalk";import{execa as R}from"execa";import ae from"fs-extra";import tt from"ora";var nt=e=>{try{return W("git --version",{cwd:e}),!0}catch{return!1}},st=e=>ae.existsSync($.join(e,".git")),ot=async e=>{try{return await R("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},it=()=>{let t=W("git --version").toString().trim().split(" ")[2],s=t?.split(".")[0],n=t?.split(".")[1];return{major:Number(s),minor:Number(n)}},rt=()=>W("git config --global init.defaultBranch || echo main").toString().trim(),le=async e=>{if(a.info("Initializing Git..."),!nt(e)){a.warn("Git is not installed. Skipping Git initialization.");return}let t=tt(`Creating a new git repo...
53
+ `).start(),s=st(e),n=await ot(e),o=$.parse(e).name;if(n&&s){if(t.stop(),!await K.confirm({message:`${z.redBright.bold("Warning:")} Git is already initialized in "${o}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){t.info("Skipping Git initialization.");return}ae.removeSync($.join(e,".git"))}else if(n&&!s&&(t.stop(),!await K.confirm({message:`${z.redBright.bold("Warning:")} "${o}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){t.info("Skipping Git initialization.");return}try{let i=rt(),{major:r,minor:c}=it();r<2||r===2&&c<28?(await R("git",["init"],{cwd:e}),await R("git",["symbolic-ref","HEAD",`refs/heads/${i}`],{cwd:e})):await R("git",["init",`--initial-branch=${i}`],{cwd:e}),await R("git",["add","."],{cwd:e}),t.succeed(`${z.green("Successfully initialized and staged")} ${z.green.bold("git")}
54
+ `)}catch{t.fail(`${z.bold.red("Failed:")} could not initialize git. Update git to the latest version!
55
+ `)}};var ce=async({projectName:e=U})=>{let t=u();a.info("Next steps:"),e!=="."&&a.info(` cd ${e}`),["npm","bun"].includes(t)?a.info(` ${t} run dev`):a.info(` ${t} dev`)};import B from"fs";import at from"path";function pe(e,t,s){let n=B.readdirSync(e);try{n.forEach(o=>{let i=at.join(e,o);if(B.statSync(i).isDirectory())pe(i,t,s);else{let c=B.readFileSync(i,"utf8").replace(new RegExp(t,"g"),s);B.writeFileSync(i,c,"utf8")}})}catch(o){a.error(`Error setting import alias: ${o}`)}}var de=(e,t)=>{try{let s=t.replace(/\*/g,"").replace(/[^/]$/,"$&/");pe(e,"@/",s)}catch(s){a.error(`Error setting import alias: ${s}`)}};import lt from"crypto";import ct from"path";import pt from"fs-extra";var me=({projectDir:e,packages:t,scopedAppName:s})=>{let n=t?.betterAuth.inUse,o=t?.supabase.inUse,i=t?.drizzle.inUse,r=t?.stripe.inUse,c=t?.clerk.inUse,l=t?.neon.inUse,m=t?.vercelAi.inUse,h=dt(!!n,!!o,!!i,!!r,!!c,!!l,!!m,s),J=ct.join(e,".env");pt.writeFileSync(J,h,"utf-8")},dt=(e,t,s,n,o,i,r,c)=>{let l=`
56
56
  # Base variables
57
57
  NEXT_PUBLIC_BASE_URL="http://localhost:3000"
58
58
  GOOGLE_ANALYTICS_TAG="G-xxxxxxx" # Google Analytics G-Tag ID
59
- `;if(e){let f=Buffer.from(it.getRandomValues(new Uint8Array(32))).toString("base64");l+=`
59
+ `;if(e){let m=Buffer.from(lt.getRandomValues(new Uint8Array(32))).toString("base64");l+=`
60
60
  # Better Auth
61
- BETTER_AUTH_SECRET="${f}" # Generated by next-ts-cli.
61
+ BETTER_AUTH_SECRET="${m}" # Generated by next-ts-cli.
62
62
 
63
63
  # Better Auth GitHub OAuth
64
64
  BETTER_AUTH_GITHUB_CLIENT_ID=""
@@ -69,23 +69,23 @@ BETTER_AUTH_GITHUB_CLIENT_SECRET=""
69
69
  # https://supabase.com/docs/guides/functions/secrets
70
70
  NEXT_PUBLIC_SUPABASE_URL="https://<app-id>.supabase.co",
71
71
  NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=""
72
- `),o&&(l+="# Drizzle"),r&&(l+=" + Neon"),l+=`
73
- `,(o||r)&&(l+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${c}"
72
+ `),s&&(l+="# Drizzle"),i&&(l+=" + Neon"),l+=`
73
+ `,(s||i)&&(l+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${c}"
74
74
  `),n&&(l+=`
75
75
  # https://dashboard.stripe.com/apikeys
76
76
 
77
77
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
78
78
  STRIPE_SECRET_KEY=""
79
79
  STRIPE_WEBHOOK_SECRET=""
80
- `),s&&(l+=`
80
+ `),o&&(l+=`
81
81
  # Clerk
82
82
  # https://clerk.com/docs/nextjs/getting-started/quickstart
83
83
  NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
84
84
  CLERK_SECRET_KEY="sk_test_..."
85
85
 
86
- `),i&&(l+=`
86
+ `),r&&(l+=`
87
87
  # Vercel AI Gateway
88
88
  # https://ai-sdk.dev/docs/getting-started/nextjs-app-router
89
89
  AI_GATEWAY_API_KEY="sk_..."
90
- `),l};import S from"path";import fe from"fs-extra";import de from"path";import me from"fs-extra";import pt from"sort-package-json";var pe={"better-auth":"^1.4.10","drizzle-kit":"^0.31.5","drizzle-orm":"^0.41.0",postgres:"^3.4.7",tailwindcss:"^4.0.15",postcss:"^8.5.3","@tailwindcss/postcss":"^4.0.15","@biomejs/biome":"^2.3.10","@supabase/ssr":"^0.8.0","@supabase/supabase-js":"^2.89.0","@stripe/react-stripe-js":"^5.4.1","@stripe/stripe-js":"^8.6.0",stripe:"^20.1.0","@clerk/nextjs":"^6.36.5","@neondatabase/serverless":"^1.0.2",ai:"^6.0.6","@ai-sdk/react":"^3.0.6"};var d=e=>{let{dependencies:t,devMode:o,projectDir:n}=e,s=me.readJSONSync(de.join(n,"package.json"));t.forEach(i=>{let c=pe[i];o&&s.devDependencies?s.devDependencies[i]=c:s.dependencies&&(s.dependencies[i]=c)});let r=pt(s);me.writeJSONSync(de.join(n,"package.json"),r,{spaces:2})};var ue=({projectDir:e,packages:t})=>{let o=t?.drizzle.inUse;d({projectDir:e,dependencies:["better-auth"],devMode:!1});let s=S.join(p,"template/extras"),r=S.join(s,"better-auth"),i="api/auth/[...all]/route.ts",c=S.join(r,i),l=S.join(e,"app",i);fe.copySync(c,l);let f=S.join(r,o?"with-drizzle-auth.ts":"base-auth.ts"),b=S.join(e,"lib","auth.ts");fe.copySync(f,b)};import v from"path";import he from"fs-extra";import dt from"path";import ge from"fs-extra";import mt from"sort-package-json";var x=e=>{let{scripts:t,projectDir:o}=e,n=dt.join(o,"package.json"),s=ge.readJSONSync(n);s.scripts={...s.scripts,...t};let r=mt(s);ge.writeJSONSync(n,r,{spaces:2})};var be=({projectDir:e})=>{d({projectDir:e,dependencies:["drizzle-kit"],devMode:!0}),d({projectDir:e,dependencies:["drizzle-orm","postgres"],devMode:!1});let t=v.join(p,"template/extras"),o=v.join(t,"drizzle"),n=v.join(o,"drizzle.config.ts"),s=v.join(e,"drizzle.config.ts");he.copySync(n,s);let r=v.join(o,"db"),i=v.join(e,"lib/db");he.copySync(r,i),x({projectDir:e,scripts:{"db:push":"drizzle-kit push","db:studio":"drizzle-kit studio","db:generate":"drizzle-kit generate","db:migrate":"drizzle-kit migrate"}})};import g from"path";import $ from"fs-extra";var ye=({projectDir:e})=>{let t=g.join(p,"template/extras"),o=g.join(t,"docker");$.copySync(g.join(o,"Dockerfile"),g.join(e,"Dockerfile")),$.copySync(g.join(o,"docker-compose.prod.yml"),g.join(e,"docker-compose.prod.yml")),$.copySync(g.join(o,".dockerignore"),g.join(e,".dockerignore")),x({projectDir:e,scripts:{"docker:build":"docker compose -f docker-compose.prod.yml build","docker:up":"docker compose -f docker-compose.prod.yml up -d","docker:down":"docker compose -f docker-compose.prod.yml down","docker:logs":"docker compose -f docker-compose.prod.yml logs -f","docker:exec":"docker compose -f docker-compose.prod.yml exec app sh","docker:exec:bash":"docker compose -f docker-compose.prod.yml exec app bash","docker:exec:sh":"docker compose -f docker-compose.prod.yml exec app sh","docker:exec:npm":"docker compose -f docker-compose.prod.yml exec app npm"}})};import K from"path";import gt from"fs-extra";import B from"path";import h from"fs-extra";var w=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e;ft({serverName:t,serverConfig:o,projectDir:n}),ut({serverName:t,serverConfig:o,projectDir:n})},ft=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e,s=B.join(n,".cusror/mpc.json");h.ensureDirSync(B.dirname(s));let r={args:o.args,command:o.command,env:o.env,url:o.url},i={};h.existsSync(s)&&(i=h.readJSONSync(s)),i.mcpServers||(i.mcpServers={}),i.mcpServers[t]={...r},h.writeJSONSync(s,i,{spaces:" "})},ut=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e,s=B.join(n,".vscode/mcp.json");h.ensureDirSync(B.dirname(s));let r={};h.existsSync(s)&&(r=h.readJSONSync(s)),r.servers||(r.servers={}),r.servers[t]={...o},h.writeJSONSync(s,r,{spaces:" "})};var ke=({projectDir:e})=>{d({projectDir:e,dependencies:["@supabase/ssr","@supabase/supabase-js"],devMode:!1});let t=K.join(p,"template/extras"),o=K.join(t,"supabase"),n=K.join(e,"lib","supabase");gt.copySync(o,n),w({serverName:"supabase",serverConfig:{type:"http",url:"https://mcp.supabase.com/mcp?project_ref=<YOUR_SUPABASE_PROJECT_ID>"},projectDir:e})};import P from"path";import _e from"fs-extra";var Se=({projectDir:e})=>{d({projectDir:e,dependencies:["stripe","@stripe/stripe-js","@stripe/react-stripe-js"],devMode:!1});let t=P.join(p,"template/extras"),o=P.join(t,"stripe"),n=P.join(o,"checkout_session"),s=P.join(e,"app/api/checkout_session");_e.copySync(n,s);let r=P.join(o,"webhook/stripe/route.ts"),i=P.join(e,"app/api/webhook/stripe/route.ts");_e.copySync(r,i),x({projectDir:e,scripts:{"webhook:local":"stripe listen --forward-to localhost:3000/api/webhook/stripe"}}),w({serverName:"stripe",serverConfig:{type:"http",url:"https://mcp.stripe.com"},projectDir:e})};import I from"path";import W from"fs-extra";var xe=({projectDir:e})=>{let t=I.join(p,"template/extras"),o=I.join(t,"shadcnui"),n=I.join(o,"components.json"),s=I.join(e,"components.json");W.copySync(n,s);let r=I.join(o,"globals.css"),i=I.join(e,"app/globals.css");W.writeFileSync(i,W.readFileSync(r,"utf8")),w({serverName:"shadcn",serverConfig:{command:"npx",args:["shadcn@latest","mcp"]},projectDir:e})};import A from"path";import ve from"fs-extra";var we=({projectDir:e})=>{d({projectDir:e,dependencies:["@clerk/nextjs"],devMode:!1});let t=A.join(p,"template/extras"),o=A.join(t,"clerk"),n=A.join(o,"proxy.ts"),s=A.join(e,"proxy.ts");ve.copySync(n,s);let r=A.join(o,"layout.tsx"),i=A.join(e,"app/layout.tsx");ve.copySync(r,i)};import Pe from"path";import ht from"fs-extra";var Ie=({projectDir:e,packages:t})=>{if(d({projectDir:e,dependencies:["@neondatabase/serverless"],devMode:!1}),t?.drizzle.inUse){let o=Pe.join(p,"template/extras/neon/index.ts"),n=Pe.join(e,"lib/db/index.ts");ht.copySync(o,n)}};import D from"path";import bt from"fs-extra";var Ae=({projectDir:e})=>{d({projectDir:e,dependencies:["ai","@ai-sdk/react"],devMode:!1});let t=D.join(p,"template/extras"),o=D.join(t,"vercel-ai"),n=D.join(o,"route.ts"),s=D.join(e,"app/api/chat/route.ts");bt.copySync(n,s)};var Ee=e=>({docker:{inUse:e.includes("docker"),installer:ye},betterAuth:{inUse:e.includes("betterAuth"),installer:ue},drizzle:{inUse:e.includes("drizzle"),installer:be},envVariables:{inUse:!0,installer:ce},supabase:{inUse:e.includes("supabase"),installer:ke},stripe:{inUse:e.includes("stripe"),installer:Se},shadcnui:{inUse:e.includes("shadcnui"),installer:xe},clerk:{inUse:e.includes("clerk"),installer:we},neon:{inUse:e.includes("neon"),installer:Ie},vercelAi:{inUse:e.includes("vercelAi"),installer:Ae}});import Ce from"path";var je=e=>{let o=M(e).split("/"),n=o[o.length-1];if(n==="."){let i=Ce.resolve(process.cwd());n=Ce.basename(i)}let s=o.findIndex(i=>i.startsWith("@"));o.findIndex(i=>i.startsWith("@"))!==-1&&(n=o.slice(s).join("/"));let r=o.filter(i=>!i.startsWith("@")).join("/");return[n,r]};import yt from"gradient-string";var kt=["#003f5b","#5f5195","#cc4c91","#ff6f4e","#ff7030","#f10062","#b1008f","#2c19a8"],Te=()=>{let e=yt(kt),t=u();(t==="yarn"||t==="pnpm")&&console.log(""),console.log(e.multiline(Y))};import _t from"chalk";import{execa as ze}from"execa";import Re from"ora";var F=async(e,t,o)=>{let{onDataHandle:n,args:s=["install"],stdout:r="pipe"}=o,i=Re(`Running ${t} install...`).start(),c=ze(t,s,{cwd:e,stdout:r});return await new Promise((l,f)=>{n&&c.stdout?.on("data",n(i)),c.on("error",b=>f(b)),c.on("close",()=>l())}),i},St=async(e,t)=>{switch(e){case"npm":return await ze(e,["install"],{cwd:t,stderr:"inherit"}),null;case"pnpm":return F(t,e,{onDataHandle:o=>n=>{let s=n.toString();s.includes("Progress")&&(o.text=s.includes("|")?s.split(" | ")[1]??"":s)}});case"yarn":return F(t,e,{onDataHandle:o=>n=>{o.text=n.toString()}});case"bun":return F(t,e,{stdout:"ignore"})}},Ue=async({projectDir:e})=>{a.info("Installing dependencies...");let t=u();(await St(t,e)??Re()).succeed(_t.green(`Successfully installed dependencies!
91
- `))};import{execSync as xt}from"child_process";import vt from"https";var Me=e=>{let t=_();t.includes("beta")?(a.warn(" You are using a beta version of next-ts-cli."),a.warn(" Please report any bugs you encounter.")):t.includes("next")?(a.warn(" You are running next-ts-cli with the @next tag which is no longer maintained."),a.warn(" Please run the CLI with @latest instead.")):t!==e&&(a.warn(" You are using an outdated version of next-ts-cli."),a.warn(" Your version:",t+".","Latest version in the npm registry:",e),a.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")};function wt(){return new Promise((e,t)=>{vt.get("https://registry.npmjs.org/-/package/next-ts-cli/dist-tags",o=>{if(o.statusCode===200){let n="";o.on("data",s=>{n+=s}),o.on("end",()=>{e(JSON.parse(n).latest)})}else t()}).on("error",()=>{t()})})}var Ne=()=>wt().catch(()=>{try{return xt("npm view next-ts-cli version").toString().trim()}catch{return null}});var It=async()=>{let e=await Ne(),t=u();Te(),e&&Me(e);let{appName:o,packages:n,flags:{alias:s}}=await Q(),r=Ee(n),[i,c]=je(o),l=await oe({projectName:c,scopedAppName:i,packages:r,alias:s}),f=Be.readJSONSync(Oe.join(l,"package.json"));if(f.name=i,f.nextTsMetadata={initVersion:_()},t!=="bun"){let{stdout:b}=await Pt(t,["-v"],{cwd:l});f.packageManager=`${t}@${b.trim()}`}Be.writeJSONSync(Oe.join(l,"package.json"),f,{spaces:2}),s!=="@/"&&le(l,s),await Ue({projectDir:l}),await re(l),await ie({projectName:c}),process.exit(0)};It().catch(e=>{a.error("Aborting installation..."),e instanceof Error?a.error(e):(a.error("An unknown error has occurred. Please open an issue on github with the below:"),console.log(e)),process.exit(1)});
90
+ `),l};import b from"path";import F from"fs-extra";import ue from"path";import ge from"fs-extra";import mt from"sort-package-json";var fe={"better-auth":"^1.4.10","drizzle-kit":"^0.31.5","drizzle-orm":"^0.41.0",postgres:"^3.4.7",tailwindcss:"^4.0.15",postcss:"^8.5.3","@tailwindcss/postcss":"^4.0.15","@biomejs/biome":"^2.3.10","@supabase/ssr":"^0.8.0","@supabase/supabase-js":"^2.89.0","@stripe/react-stripe-js":"^5.4.1","@stripe/stripe-js":"^8.6.0",stripe:"^20.1.0","@clerk/nextjs":"^6.36.5","@neondatabase/serverless":"^1.0.2",ai:"^6.0.6","@ai-sdk/react":"^3.0.6"};var d=e=>{let{dependencies:t,devMode:s,projectDir:n}=e,o=ge.readJSONSync(ue.join(n,"package.json"));t.forEach(r=>{let c=fe[r];s&&o.devDependencies?o.devDependencies[r]=c:o.dependencies&&(o.dependencies[r]=c)});let i=mt(o);ge.writeJSONSync(ue.join(n,"package.json"),i,{spaces:2})};var he=({projectDir:e,packages:t})=>{let s=t?.drizzle.inUse;d({projectDir:e,dependencies:["better-auth"],devMode:!1});let o=b.join(p,"template/extras"),i=b.join(o,"better-auth"),r="api/auth/[...all]/route.ts",c=b.join(i,r),l=b.join(e,"app",r);F.copySync(c,l);let m=b.join(i,"auth-client.ts"),h=b.join(e,"lib/auth-client.ts");F.copySync(m,h);let J=b.join(i,s?"with-drizzle-auth.ts":"base-auth.ts"),Le=b.join(e,"lib","auth.ts");F.copySync(J,Le)};import w from"path";import ye from"fs-extra";import ft from"path";import be from"fs-extra";import ut from"sort-package-json";var v=e=>{let{scripts:t,projectDir:s}=e,n=ft.join(s,"package.json"),o=be.readJSONSync(n);o.scripts={...o.scripts,...t};let i=ut(o);be.writeJSONSync(n,i,{spaces:2})};var ke=({projectDir:e})=>{d({projectDir:e,dependencies:["drizzle-kit"],devMode:!0}),d({projectDir:e,dependencies:["drizzle-orm","postgres"],devMode:!1});let t=w.join(p,"template/extras"),s=w.join(t,"drizzle"),n=w.join(s,"drizzle.config.ts"),o=w.join(e,"drizzle.config.ts");ye.copySync(n,o);let i=w.join(s,"db"),r=w.join(e,"lib/db");ye.copySync(i,r),v({projectDir:e,scripts:{"db:push":"drizzle-kit push","db:studio":"drizzle-kit studio","db:generate":"drizzle-kit generate","db:migrate":"drizzle-kit migrate"}})};import y from"path";import H from"fs-extra";var Se=({projectDir:e})=>{let t=y.join(p,"template/extras"),s=y.join(t,"docker");H.copySync(y.join(s,"Dockerfile"),y.join(e,"Dockerfile")),H.copySync(y.join(s,"docker-compose.prod.yml"),y.join(e,"docker-compose.prod.yml")),H.copySync(y.join(s,".dockerignore"),y.join(e,".dockerignore")),v({projectDir:e,scripts:{"docker:build":"docker compose -f docker-compose.prod.yml build","docker:up":"docker compose -f docker-compose.prod.yml up -d","docker:down":"docker compose -f docker-compose.prod.yml down","docker:logs":"docker compose -f docker-compose.prod.yml logs -f","docker:exec":"docker compose -f docker-compose.prod.yml exec app sh","docker:exec:bash":"docker compose -f docker-compose.prod.yml exec app bash","docker:exec:sh":"docker compose -f docker-compose.prod.yml exec app sh","docker:exec:npm":"docker compose -f docker-compose.prod.yml exec app npm"}})};import A from"path";import _e from"fs-extra";import D from"path";import k from"fs-extra";var P=e=>{let{serverName:t,serverConfig:s,projectDir:n}=e;gt({serverName:t,serverConfig:s,projectDir:n}),ht({serverName:t,serverConfig:s,projectDir:n})},gt=e=>{let{serverName:t,serverConfig:s,projectDir:n}=e,o=D.join(n,".cusror/mpc.json");k.ensureDirSync(D.dirname(o));let i={args:s.args,command:s.command,env:s.env,url:s.url},r={};k.existsSync(o)&&(r=k.readJSONSync(o)),r.mcpServers||(r.mcpServers={}),r.mcpServers[t]={...i},k.writeJSONSync(o,r,{spaces:" "})},ht=e=>{let{serverName:t,serverConfig:s,projectDir:n}=e,o=D.join(n,".vscode/mcp.json");k.ensureDirSync(D.dirname(o));let i={};k.existsSync(o)&&(i=k.readJSONSync(o)),i.servers||(i.servers={}),i.servers[t]={...s},k.writeJSONSync(o,i,{spaces:" "})};var xe=({projectDir:e})=>{d({projectDir:e,dependencies:["@supabase/ssr","@supabase/supabase-js"],devMode:!1});let t=A.join(p,"template/extras"),s=A.join(t,"supabase"),n=A.join(s,"supabase"),o=A.join(e,"lib/supabase"),i=A.join(s,"proxy.ts"),r=A.join(e,"proxy.ts");_e.copySync(i,r),_e.copySync(n,o),P({serverName:"supabase",serverConfig:{type:"http",url:"https://mcp.supabase.com/mcp?project_ref=<YOUR_SUPABASE_PROJECT_ID>"},projectDir:e})};import g from"path";import G from"fs-extra";var ve=({projectDir:e})=>{d({projectDir:e,dependencies:["stripe","@stripe/stripe-js","@stripe/react-stripe-js"],devMode:!1});let t=g.join(p,"template/extras"),s=g.join(t,"stripe"),n=g.join(s,"checkout_session"),o=g.join(e,"app/api/checkout_session");G.copySync(n,o);let i=g.join(s,"webhook/stripe/route.ts"),r=g.join(e,"app/api/webhook/stripe/route.ts");G.copySync(i,r);let c=g.join(s,"stripe.ts"),l=g.join(e,"lib/stripe.ts");G.copySync(c,l);let m=g.join(s,"prices.ts"),h=g.join(e,"actions/prices.ts");G.copySync(m,h),v({projectDir:e,scripts:{"webhook:local":"stripe listen --forward-to localhost:3000/api/webhook/stripe"}}),P({serverName:"stripe",serverConfig:{type:"http",url:"https://mcp.stripe.com"},projectDir:e})};import I from"path";import Y from"fs-extra";var we=({projectDir:e})=>{let t=I.join(p,"template/extras"),s=I.join(t,"shadcnui"),n=I.join(s,"components.json"),o=I.join(e,"components.json");Y.copySync(n,o);let i=I.join(s,"globals.css"),r=I.join(e,"app/globals.css");Y.writeFileSync(r,Y.readFileSync(i,"utf8")),P({serverName:"shadcn",serverConfig:{command:"npx",args:["shadcn@latest","mcp"]},projectDir:e})};import E from"path";import Pe from"fs-extra";var Ae=({projectDir:e})=>{d({projectDir:e,dependencies:["@clerk/nextjs"],devMode:!1});let t=E.join(p,"template/extras"),s=E.join(t,"clerk"),n=E.join(s,"proxy.ts"),o=E.join(e,"proxy.ts");Pe.copySync(n,o);let i=E.join(s,"layout.tsx"),r=E.join(e,"app/layout.tsx");Pe.copySync(i,r)};import Ie from"path";import bt from"fs-extra";var Ee=({projectDir:e,packages:t})=>{if(d({projectDir:e,dependencies:["@neondatabase/serverless"],devMode:!1}),t?.drizzle.inUse){let s=Ie.join(p,"template/extras/neon/index.ts"),n=Ie.join(e,"lib/db/index.ts");bt.copySync(s,n)}};import L from"path";import yt from"fs-extra";var Ce=({projectDir:e})=>{d({projectDir:e,dependencies:["ai","@ai-sdk/react"],devMode:!1});let t=L.join(p,"template/extras"),s=L.join(t,"vercel-ai"),n=L.join(s,"route.ts"),o=L.join(e,"app/api/chat/route.ts");yt.copySync(n,o)};var je=e=>({docker:{inUse:e.includes("docker"),installer:Se},betterAuth:{inUse:e.includes("betterAuth"),installer:he},drizzle:{inUse:e.includes("drizzle"),installer:ke},envVariables:{inUse:!0,installer:me},supabase:{inUse:e.includes("supabase"),installer:xe},stripe:{inUse:e.includes("stripe"),installer:ve},shadcnui:{inUse:e.includes("shadcnui"),installer:we},clerk:{inUse:e.includes("clerk"),installer:Ae},neon:{inUse:e.includes("neon"),installer:Ee},vercelAi:{inUse:e.includes("vercelAi"),installer:Ce}});import Te from"path";var ze=e=>{let s=N(e).split("/"),n=s[s.length-1];if(n==="."){let r=Te.resolve(process.cwd());n=Te.basename(r)}let o=s.findIndex(r=>r.startsWith("@"));s.findIndex(r=>r.startsWith("@"))!==-1&&(n=s.slice(o).join("/"));let i=s.filter(r=>!r.startsWith("@")).join("/");return[n,i]};import kt from"gradient-string";var St=["#003f5b","#5f5195","#cc4c91","#ff6f4e","#ff7030","#f10062","#b1008f","#2c19a8"],Re=()=>{let e=kt(St),t=u();(t==="yarn"||t==="pnpm")&&console.log(""),console.log(e.multiline(Q))};import _t from"chalk";import{execa as Ue}from"execa";import Me from"ora";var X=async(e,t,s)=>{let{onDataHandle:n,args:o=["install"],stdout:i="pipe"}=s,r=Me(`Running ${t} install...`).start(),c=Ue(t,o,{cwd:e,stdout:i});return await new Promise((l,m)=>{n&&c.stdout?.on("data",n(r)),c.on("error",h=>m(h)),c.on("close",()=>l())}),r},xt=async(e,t)=>{switch(e){case"npm":return await Ue(e,["install"],{cwd:t,stderr:"inherit"}),null;case"pnpm":return X(t,e,{onDataHandle:s=>n=>{let o=n.toString();o.includes("Progress")&&(s.text=o.includes("|")?o.split(" | ")[1]??"":o)}});case"yarn":return X(t,e,{onDataHandle:s=>n=>{s.text=n.toString()}});case"bun":return X(t,e,{stdout:"ignore"})}},Ne=async({projectDir:e})=>{a.info("Installing dependencies...");let t=u();(await xt(t,e)??Me()).succeed(_t.green(`Successfully installed dependencies!
91
+ `))};import{execSync as vt}from"child_process";import wt from"https";var Oe=e=>{let t=x();t.includes("beta")?(a.warn(" You are using a beta version of next-ts-cli."),a.warn(" Please report any bugs you encounter.")):t.includes("next")?(a.warn(" You are running next-ts-cli with the @next tag which is no longer maintained."),a.warn(" Please run the CLI with @latest instead.")):t!==e&&(a.warn(" You are using an outdated version of next-ts-cli."),a.warn(" Your version:",t+".","Latest version in the npm registry:",e),a.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")};function Pt(){return new Promise((e,t)=>{wt.get("https://registry.npmjs.org/-/package/next-ts-cli/dist-tags",s=>{if(s.statusCode===200){let n="";s.on("data",o=>{n+=o}),s.on("end",()=>{e(JSON.parse(n).latest)})}else t()}).on("error",()=>{t()})})}var Be=()=>Pt().catch(()=>{try{return vt("npm view next-ts-cli version").toString().trim()}catch{return null}});var It=async()=>{let e=await Be(),t=u();Re(),e&&Oe(e);let{appName:s,packages:n,flags:{alias:o}}=await te(),i=je(n),[r,c]=ze(s),l=await re({projectName:c,scopedAppName:r,packages:i,alias:o}),m=Ge.readJSONSync(De.join(l,"package.json"));if(m.name=r,m.nextTsMetadata={initVersion:x()},t!=="bun"){let{stdout:h}=await At(t,["-v"],{cwd:l});m.packageManager=`${t}@${h.trim()}`}Ge.writeJSONSync(De.join(l,"package.json"),m,{spaces:2}),o!=="@/"&&de(l,o),await Ne({projectDir:l}),await le(l),await ce({projectName:c}),process.exit(0)};It().catch(e=>{a.error("Aborting installation..."),e instanceof Error?a.error(e):(a.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,6 +1,6 @@
1
1
  {
2
2
  "name": "next-ts-cli",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "Create production-ready web application with the Next.js",
5
5
  "author": "Daniele Rossino <daniele.rossino10@gmail.com>",
6
6
  "maintainers": [],
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { createAuthClient } from "better-auth/react";
3
+
4
+ if (!process.env.NEXT_PUBLIC_BASE_URL) {
5
+ throw new Error("NEXT_PUBLIC_BASE_URL is not set");
6
+ }
7
+
8
+ export const authClient = createAuthClient({
9
+ baseURL: process.env.NEXT_PUBLIC_BASE_URL,
10
+ // other configurations here
11
+ })
@@ -2,8 +2,6 @@ import "dotenv/config";
2
2
  import { betterAuth } from "better-auth";
3
3
  import { drizzleAdapter } from "better-auth/adapters/drizzle";
4
4
  import { nextCookies } from "better-auth/next-js";
5
-
6
- /* Here imports have errors because they are related to the final directory structure */
7
5
  import { db } from "@/lib/db";
8
6
 
9
7
  if (!process.env.BETTER_AUTH_GITHUB_CLIENT_ID || !process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET) {
@@ -0,0 +1,79 @@
1
+ "use server";
2
+
3
+ import type { Stripe } from "stripe";
4
+ import { stripe } from "@/lib/stripe";
5
+
6
+ /* List all prices */
7
+ async function getPrices(
8
+ options: Stripe.PriceListParams = {},
9
+ ): Promise<Stripe.Price[]> {
10
+ try {
11
+ const params: Stripe.PriceListParams = {
12
+ limit: options.limit ?? 100,
13
+ };
14
+
15
+ if (options.active !== undefined) {
16
+ params.active = options.active;
17
+ }
18
+
19
+ if (options.product) {
20
+ params.product = options.product;
21
+ }
22
+
23
+ if (options.starting_after) {
24
+ params.starting_after = options.starting_after;
25
+ }
26
+
27
+ if (options.ending_before) {
28
+ params.ending_before = options.ending_before;
29
+ }
30
+
31
+ const prices = await stripe().prices.list(params);
32
+
33
+ return prices.data;
34
+ } catch (error) {
35
+ console.error("Error fetching Stripe prices:", error);
36
+ throw new Error("Failed to fetch prices");
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Get a single price by ID
42
+ */
43
+ async function getPrice(priceId: string): Promise<Stripe.Price> {
44
+ try {
45
+ const price = await stripe().prices.retrieve(priceId);
46
+ return price;
47
+ } catch (error) {
48
+ console.error("Error fetching Stripe price:", error);
49
+ throw new Error(`Failed to fetch price: ${priceId}`);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Get prices for a specific product
55
+ */
56
+ async function getPricesByProduct(
57
+ productId: string,
58
+ active?: boolean,
59
+ ): Promise<Stripe.Price[]> {
60
+ return getPrices({
61
+ product: productId,
62
+ active,
63
+ });
64
+ }
65
+
66
+ /* List all products */
67
+ async function getProducts(
68
+ options: Stripe.ProductListParams = {},
69
+ ): Promise<Stripe.Product[]> {
70
+ try {
71
+ const products = await stripe().products.list(options);
72
+ return products.data;
73
+ } catch (error) {
74
+ console.error("Error fetching Stripe products:", error);
75
+ throw new Error("Failed to fetch products");
76
+ }
77
+ }
78
+
79
+ export { getPrices, getPrice, getPricesByProduct, getProducts };
@@ -1,70 +1,17 @@
1
- import { createServerClient } from "@supabase/ssr";
2
- import { NextResponse, type NextRequest } from "next/server";
3
-
4
- export async function updateSession(request: NextRequest) {
5
- let supabaseResponse = NextResponse.next({
6
- request,
7
- });
8
-
9
-
10
- // With Fluid compute, don't put this client in a global environment
11
- // variable. Always create a new one on each request.
12
- const supabase = createServerClient(
13
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
14
- process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
15
- {
16
- cookies: {
17
- getAll() {
18
- return request.cookies.getAll();
19
- },
20
- setAll(cookiesToSet) {
21
- cookiesToSet.forEach(({ name, value }) =>
22
- request.cookies.set(name, value),
23
- );
24
- supabaseResponse = NextResponse.next({
25
- request,
26
- });
27
- cookiesToSet.forEach(({ name, value, options }) =>
28
- supabaseResponse.cookies.set(name, value, options),
29
- );
30
- },
31
- },
32
- },
33
- );
34
-
35
- // Do not run code between createServerClient and
36
- // supabase.auth.getClaims(). A simple mistake could make it very hard to debug
37
- // issues with users being randomly logged out.
38
-
39
- // IMPORTANT: If you remove getClaims() and you use server-side rendering
40
- // with the Supabase client, your users may be randomly logged out.
41
- const { data } = await supabase.auth.getClaims();
42
- const user = data?.claims;
43
-
44
- if (
45
- request.nextUrl.pathname !== "/" &&
46
- !user &&
47
- !request.nextUrl.pathname.startsWith("/login") &&
48
- !request.nextUrl.pathname.startsWith("/auth")
49
- ) {
50
- // no user, potentially respond by redirecting the user to the login page
51
- const url = request.nextUrl.clone();
52
- url.pathname = "/auth/login";
53
- return NextResponse.redirect(url);
54
- }
55
-
56
- // IMPORTANT: You *must* return the supabaseResponse object as it is.
57
- // If you're creating a new response object with NextResponse.next() make sure to:
58
- // 1. Pass the request in it, like so:
59
- // const myNewResponse = NextResponse.next({ request })
60
- // 2. Copy over the cookies, like so:
61
- // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
62
- // 3. Change the myNewResponse object to fit your needs, but avoid changing
63
- // the cookies!
64
- // 4. Finally:
65
- // return myNewResponse
66
- // If this is not done, you may be causing the browser and server to go out
67
- // of sync and terminate the user's session prematurely!
68
-
69
- return supabaseResponse;
1
+ import { type NextRequest } from "next/server"
2
+ import { updateSession } from "@/lib/supabase/proxy"
3
+ export async function proxy(request: NextRequest) {
4
+ return await updateSession(request)
70
5
  }
6
+ export const config = {
7
+ matcher: [
8
+ /*
9
+ * Match all request paths except for the ones starting with:
10
+ * - _next/static (static files)
11
+ * - _next/image (image optimization files)
12
+ * - favicon.ico (favicon file)
13
+ * Feel free to modify this pattern to include more paths.
14
+ */
15
+ "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
16
+ ],
17
+ }
@@ -0,0 +1,65 @@
1
+ import { createServerClient } from '@supabase/ssr'
2
+ import { NextResponse, type NextRequest } from 'next/server'
3
+
4
+ export async function updateSession(request: NextRequest) {
5
+ let supabaseResponse = NextResponse.next({
6
+ request,
7
+ })
8
+
9
+ // With Fluid compute, don't put this client in a global environment
10
+ // variable. Always create a new one on each request.
11
+ const supabase = createServerClient(
12
+ process.env.NEXT_PUBLIC_SUPABASE_URL!,
13
+ process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY!,
14
+ {
15
+ cookies: {
16
+ getAll() {
17
+ return request.cookies.getAll()
18
+ },
19
+ setAll(cookiesToSet) {
20
+ cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value))
21
+ supabaseResponse = NextResponse.next({
22
+ request,
23
+ })
24
+ cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options))
25
+ },
26
+ },
27
+ }
28
+ )
29
+
30
+ // Do not run code between createServerClient and
31
+ // supabase.auth.getClaims(). A simple mistake could make it very hard to debug
32
+ // issues with users being randomly logged out.
33
+
34
+ // IMPORTANT: If you remove getClaims() and you use server-side rendering
35
+ // with the Supabase client, your users may be randomly logged out.
36
+ const { data } = await supabase.auth.getClaims()
37
+
38
+ const user = data?.claims
39
+
40
+ if (
41
+ !user &&
42
+ !request.nextUrl.pathname.startsWith('/login') &&
43
+ !request.nextUrl.pathname.startsWith('/auth')
44
+ ) {
45
+ // no user, potentially respond by redirecting the user to the login page
46
+ const url = request.nextUrl.clone()
47
+ url.pathname = '/login'
48
+ return NextResponse.redirect(url)
49
+ }
50
+
51
+ // IMPORTANT: You *must* return the supabaseResponse object as it is. If you're
52
+ // creating a new response object with NextResponse.next() make sure to:
53
+ // 1. Pass the request in it, like so:
54
+ // const myNewResponse = NextResponse.next({ request })
55
+ // 2. Copy over the cookies, like so:
56
+ // myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll())
57
+ // 3. Change the myNewResponse object to fit your needs, but avoid changing
58
+ // the cookies!
59
+ // 4. Finally:
60
+ // return myNewResponse
61
+ // If this is not done, you may be causing the browser and server to go out
62
+ // of sync and terminate the user's session prematurely!
63
+
64
+ return supabaseResponse
65
+ }