next-ts-cli 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/index.js +94 -0
- package/package.json +97 -0
- package/template/base/.cusror/mpc.json +8 -0
- package/template/base/.husky/commit-msg +1 -0
- package/template/base/.husky/pre-commit +1 -0
- package/template/base/LICENSE +21 -0
- package/template/base/README.md +0 -0
- package/template/base/app/apple-icon.png +0 -0
- package/template/base/app/favicon.ico +0 -0
- package/template/base/app/globals.css +9 -0
- package/template/base/app/layout.tsx +61 -0
- package/template/base/app/loading.tsx +7 -0
- package/template/base/app/manifest.ts +33 -0
- package/template/base/app/opengraph-image.png +0 -0
- package/template/base/app/page.tsx +7 -0
- package/template/base/app/robots.ts +28 -0
- package/template/base/app/sitemap.ts +17 -0
- package/template/base/app/twitter-image.png +0 -0
- package/template/base/biome.jsonc +272 -0
- package/template/base/commitlint.config.ts +25 -0
- package/template/base/hooks/use-hydration.tsx +16 -0
- package/template/base/jest.config.js +18 -0
- package/template/base/jest.setup.js +2 -0
- package/template/base/lib/fonts.ts +14 -0
- package/template/base/lib/indexing.ts +14 -0
- package/template/base/lib/microdata.ts +51 -0
- package/template/base/lib/utils.ts +6 -0
- package/template/base/next.config.ts +7 -0
- package/template/base/package-lock.json +9296 -0
- package/template/base/package.json +59 -0
- package/template/base/postcss.config.js +5 -0
- package/template/base/providers/MicrodataScript.tsx +18 -0
- package/template/base/public/.gitkeep +3 -0
- package/template/base/test/index.test.tsx +9 -0
- package/template/base/tsconfig.json +38 -0
- package/template/extras/better-auth/api/auth/[...all]/route.ts +7 -0
- package/template/extras/better-auth/base-auth.ts +12 -0
- package/template/extras/better-auth/with-drizzle-auth.ts +31 -0
- package/template/extras/clerk/layout.tsx +89 -0
- package/template/extras/clerk/proxy.ts +21 -0
- package/template/extras/docker/.dockerignore +60 -0
- package/template/extras/docker/Dockerfile +51 -0
- package/template/extras/docker/docker-compose.prod.yml +34 -0
- package/template/extras/drizzle/db/index.ts +9 -0
- package/template/extras/drizzle/db/schema.ts +7 -0
- package/template/extras/drizzle/drizzle.config.ts +15 -0
- package/template/extras/neon/index.ts +10 -0
- package/template/extras/shadcnui/components.json +21 -0
- package/template/extras/shadcnui/globals.css +71 -0
- package/template/extras/stripe/checkout_session/route.ts +60 -0
- package/template/extras/stripe/stripe.ts +17 -0
- package/template/extras/stripe/webhook/stripe/route.ts +89 -0
- package/template/extras/supabase/client.ts +8 -0
- package/template/extras/supabase/getAuth.ts +50 -0
- package/template/extras/supabase/proxy.ts +70 -0
- package/template/extras/supabase/server.ts +34 -0
- package/template/extras/supabase/storage.ts +90 -0
- package/template/extras/vercel-ai/route.ts +12 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Shoubhit Dash
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
__ __ __ __
|
|
5
|
+
.-----.-----.--.--| |_ ______| |_.-----.______.----| |__|
|
|
6
|
+
| | -__|_ _| _|______| _|__ --|______| __| | |
|
|
7
|
+
|__|__|_____|__.__|____| |____|_____| |____|__|__|
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
**Minimal CLI to scaffold a production-ready Next.js applications**
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/next-ts-cli)
|
|
13
|
+
[](https://opensource.org/licenses/MIT)
|
|
14
|
+
[](https://nodejs.org/)
|
|
15
|
+
|
|
16
|
+
[Getting Started](#getting-started) •
|
|
17
|
+
[Integrations](#integrations) •
|
|
18
|
+
[Core Stack](#core-stack) •
|
|
19
|
+
[CLI Options](#cli-options) •
|
|
20
|
+
[Contributing](#contributing)
|
|
21
|
+
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Getting Started
|
|
26
|
+
|
|
27
|
+
### Quick start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# npm
|
|
31
|
+
npx next-ts-cli@latest
|
|
32
|
+
|
|
33
|
+
# pnpm
|
|
34
|
+
pnpm create next-ts-cli
|
|
35
|
+
|
|
36
|
+
# yarn
|
|
37
|
+
yarn create next-ts-cli
|
|
38
|
+
|
|
39
|
+
# bun
|
|
40
|
+
bunx next-ts-cli
|
|
41
|
+
```
|
|
42
|
+
The CLI will guide you through the setup process:
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
◆ Project name
|
|
46
|
+
│ my-awesome-app
|
|
47
|
+
│
|
|
48
|
+
◆ What authentication provider would you like to use?
|
|
49
|
+
│ ○ None ○ BetterAuth ○ Clerk ● Supabase
|
|
50
|
+
|
|
51
|
+
etc...
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Non-interactive usage
|
|
55
|
+
You can also scaffold the project without the interactive configuration. **--cli** command is required to do that.
|
|
56
|
+
```bash
|
|
57
|
+
# npm
|
|
58
|
+
npx next-ts-cli my-app --cli --supabase --drizzle --shadcnui --stripe
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
## Integrations
|
|
64
|
+
|
|
65
|
+
🔐 **Authentication** — Choose between Clerk, Supabase or BetterAuth for authenticating your app
|
|
66
|
+
|
|
67
|
+
🗄️ **Database** — Select between Neon database or Supabase to store users and stuff.
|
|
68
|
+
|
|
69
|
+
⚙️ **ORM** — Drizzle ORM integration for type-safe database queries
|
|
70
|
+
|
|
71
|
+
💳 **Stripe** — Stripe Payments integration with checkout sessions and webhooks
|
|
72
|
+
|
|
73
|
+
🐳 **Docker** — Production Dockerfile and docker-compose configuration
|
|
74
|
+
|
|
75
|
+
🎨 **Shadcn/ui** - With neutral configuration to start building compound components
|
|
76
|
+
|
|
77
|
+
**AI Integration** - Vercel AI Gateway for building an AI app
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
_*These integrations are all optional, so not required to scaffold the core project_
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Core Stack
|
|
84
|
+
The core project structure contains the following features:
|
|
85
|
+
|
|
86
|
+
🌐 **Next.js** - With App router and always up to date.
|
|
87
|
+
|
|
88
|
+
💨 **Tailwind CSS v4** - Style fast and efficiently with the latest version of Tailwind CSS.
|
|
89
|
+
|
|
90
|
+
🐶 **Husky** - Automatically lint your commit messages, code, and run tests upon committing or pushing.
|
|
91
|
+
|
|
92
|
+
🔹 **Biome** - Fast linting and formatter tool (~35x faster than Prettier)
|
|
93
|
+
|
|
94
|
+
➿ **Conventional Commits** - Define commit messages convention in your project
|
|
95
|
+
|
|
96
|
+
💡 **SEO** - Metadata, Microdata, Sitemap, robots.txt and Manifest. Having online presence has never been easier
|
|
97
|
+
|
|
98
|
+
📊 **Google Analytics** - Natively integrated with Next.js
|
|
99
|
+
|
|
100
|
+
🔨 **Jest** - Last but not least, make Test Driven Development your standard
|
|
101
|
+
|
|
102
|
+
## CLI Options
|
|
103
|
+
|
|
104
|
+
| Flag | Description |
|
|
105
|
+
|------|-------------|
|
|
106
|
+
| `--cli` | Run in CLI mode (non-interactive) |
|
|
107
|
+
| `-d, --default` | Scaffold the project without any additional integrations |
|
|
108
|
+
| `-a, --alias <alias>` | Set custom import alias (default: `@/`) |
|
|
109
|
+
| `-v, --version` | Display version number |
|
|
110
|
+
| `--docker` | Include Docker configuration (CLI mode) |
|
|
111
|
+
| `--drizzle` | Include Drizzle ORM (CLI mode) |
|
|
112
|
+
| `--supabase` | Include Supabase auth (CLI mode) |
|
|
113
|
+
| `--betterauth` | Include BetterAuth (CLI mode) |
|
|
114
|
+
| `--clerk` | Include Clerk.js (CLI mode) |
|
|
115
|
+
| `--neon` | Include Neon database (CLI mode) |
|
|
116
|
+
| `--stripe` | Include Stripe integration (CLI mode) |
|
|
117
|
+
| `--shadcnui` | Include Shadcn UI (CLI mode) |
|
|
118
|
+
| `--vercelAi` | Include Vercel AI Gateway (CLI mode) |
|
|
119
|
+
|
|
120
|
+
**Note:** Package flags (`--docker`, `--drizzle`, etc.) must be used with `--cli` flag to skip interactive prompts.
|
|
121
|
+
|
|
122
|
+
### Examples
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Create with defaults
|
|
126
|
+
npx next-ts-cli my-app -d
|
|
127
|
+
|
|
128
|
+
# Create with specific packages (CLI mode)
|
|
129
|
+
npx next-ts-cli my-app --cli --drizzle --betterauth --stripe
|
|
130
|
+
|
|
131
|
+
# Custom import alias
|
|
132
|
+
npx next-ts-cli my-app -a "~/"
|
|
133
|
+
|
|
134
|
+
# Full stack example
|
|
135
|
+
npx next-ts-cli my-app --cli --drizzle --betterauth --stripe --docker --shadcnui
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Core tech stack
|
|
141
|
+
|
|
142
|
+
### Base Template Includes
|
|
143
|
+
|
|
144
|
+
| Technology | Version | Description |
|
|
145
|
+
|------------|---------|-------------|
|
|
146
|
+
| [Next.js](https://nextjs.org/) | 16.x | React framework with App Router |
|
|
147
|
+
| [React](https://react.dev/) | 19.x | UI library |
|
|
148
|
+
| [TypeScript](https://www.typescriptlang.org/) | 5.x | Type safety |
|
|
149
|
+
| [Tailwind CSS](https://tailwindcss.com/) | 4.x | Utility-first CSS |
|
|
150
|
+
| [Biome](https://biomejs.dev/) | 2.x | Linter and formatter |
|
|
151
|
+
| [Jest](https://jestjs.io/) | 30.x | Testing framework |
|
|
152
|
+
| [Framer Motion](https://www.framer.com/motion/) | 12.x | Animation library |
|
|
153
|
+
| [Husky](https://typicode.github.io/husky/) | 9.x | Git hooks |
|
|
154
|
+
| [Commitlint](https://commitlint.js.org/) | 20.x | Commit message linting |
|
|
155
|
+
|
|
156
|
+
## Contributing
|
|
157
|
+
|
|
158
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
|
|
159
|
+
|
|
160
|
+
### Quick Setup
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
# Fork and clone
|
|
164
|
+
gh repo fork rossifire/next-ts-cli
|
|
165
|
+
gh repo clone <your-github-name>/next-ts-cli
|
|
166
|
+
|
|
167
|
+
# Install dependencies
|
|
168
|
+
pnpm install
|
|
169
|
+
|
|
170
|
+
# Start development
|
|
171
|
+
pnpm dev
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Credits
|
|
183
|
+
|
|
184
|
+
This project is inspired by [create-t3-app](https://github.com/t3-oss/create-t3-app).
|
|
185
|
+
|
|
186
|
+
<div align="center">
|
|
187
|
+
|
|
188
|
+
**Made with ❤️ by [Daniele Rossino](https://github.com/RossiFire)**
|
|
189
|
+
|
|
190
|
+
[⭐ Star on GitHub](https://github.com/RossiFire/next-ts-cli) • [🐛 Report Bug](https://github.com/RossiFire/next-ts-cli/issues) • [✨ Request Feature](https://github.com/RossiFire/next-ts-cli/issues)
|
|
191
|
+
|
|
192
|
+
</div>
|
|
193
|
+
|
|
194
|
+
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import Be from"path";import{execa as _t}from"execa";import Oe from"fs-extra";import*as m from"@clack/prompts";import{Command as Je}from"commander";import K from"path";import{fileURLToPath as De}from"url";var Me=De(import.meta.url),Ge=K.dirname(Me),p=K.join(Ge,"../"),W=`
|
|
3
|
+
__ __ __ __
|
|
4
|
+
.-----.-----.--.--| |_ ______| |_.-----.______.----| |__|
|
|
5
|
+
| | -__|_ _| _|______| _|__ --|______| __| | |
|
|
6
|
+
|__|__|_____|__.__|____| |____|_____| |____|__|__|
|
|
7
|
+
`,j="next-ts-cli",O="next-ts-cli";import Le from"path";import Ve from"fs-extra";var y=()=>{let e=Le.join(p,"package.json");return Ve.readJSONSync(e).version??"1.0.0"};var v=class extends Error{constructor(n){super(n)}};import z from"chalk";var r={error(...e){console.log(z.red(...e))},warn(...e){console.log(z.yellow(...e))},info(...e){console.log(z.cyan(...e))},success(...e){console.log(z.green(...e))}};var U=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var $e=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,H=e=>{if(!e)return;let n=U(e),o=n.split("/"),t=o.findIndex(s=>s.startsWith("@")),i=o[o.length-1];if(o.findIndex(s=>s.startsWith("@"))!==-1&&(i=o.slice(t).join("/")),!(n==="."||$e.test(i??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var Y=e=>{if(e.startsWith(".")||e.startsWith("/"))return"Import alias can't start with '.' or '/'"};var A={appName:j,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}},X=async()=>{let e=A,n=new Je().name(O).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",A.flags.alias).option("--drizzle [boolean]","Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").option("--docker [boolean]","Experimental: Boolean value if we should install Docker. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").option("--stripe [boolean]","Experimental: Boolean value if we should install Stripe. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").option("--shadcnui [boolean]","Experimental: Boolean value if we should install Shadcn UI. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").option("--betterauth [boolean]","Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--cli`. Not valid with --clerk",t=>!!t&&t!=="false").option("--clerk [boolean]","Experimental: Boolean value if we should install Clerk.js . Must be used in conjunction with `--cli`. Not valid with --betterAuth",t=>!!t&&t!=="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",t=>!!t&&t!=="false").option("--neon [boolean]","Experimental: Boolean value if we should install Neon. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").option("--vercelAi [boolean]","Experimental: Boolean value if we should install Vercel AI Gateway. Must be used in conjunction with `--cli`.",t=>!!t&&t!=="false").version(y(),"-v, --version","Display the version number").parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&r.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
|
|
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=n.args[0];if(o&&(e.appName=o),e.flags=n.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&&(r.warn("Incompatible combination Supabase and BetterAuth. Exiting."),process.exit(0)),e.flags.clerk&&e.flags.betterauth&&(r.warn("Incompatible combination Clerk and BetterAuth. Exiting."),process.exit(0)),e.flags.supabase&&e.flags.clerk&&(r.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 r.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 v("Non-interactive environment");let t=await m.group({...!o&&{name:()=>m.text({message:"Project name",defaultValue:o,placeholder:A.appName,validate:H})},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:a})=>{if(a.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:A.flags.alias,placeholder:A.flags.alias,validate:Y}),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)}}),i=[],s=t.additionalConfig;return t.orm==="drizzle"&&i.push("drizzle"),t.authentication==="better-auth"&&i.push("betterAuth"),t.authentication==="clerk"&&i.push("clerk"),(s.includes("supabase")||t.authentication==="supabase")&&i.push("supabase"),t.database==="neon"&&i.push("neon"),s.includes("docker")&&i.push("docker"),s.includes("stripe")&&i.push("stripe"),s.includes("shadcnui")&&i.push("shadcnui"),s.includes("vercelAi")&&i.push("vercelAi"),{appName:t.name??e.appName,packages:i,flags:{...e.flags,alias:t.importAlias??e.flags.alias}}}catch(t){if(t instanceof v)r.warn(`
|
|
11
|
+
${O} needs an interactive terminal to provide options`),await m.confirm({message:"Continue scaffolding a default next-ts-cli?",initialValue:!0})||(r.info("Exiting..."),process.exit(0)),r.info(`Bootstrapping a default next-ts-cli in ./${e.appName}`);else throw t}return e};import Ze from"path";import Z from"chalk";import Fe from"ora";var q=e=>{let{packages:n}=e;r.info("Adding boilerplate...");for(let[o,t]of Object.entries(n))if(t.inUse){let i=Fe(`Boilerplating ${o}...`).start();t.installer(e),i.succeed(Z.green(`Successfully setup boilerplate for ${Z.green.bold(o)}`))}r.info("")};import Ke from"path";import*as k from"@clack/prompts";import b from"chalk";import R from"fs-extra";import We from"ora";var Q=async({projectName:e,projectDir:n,pkgManager:o})=>{let t=Ke.join(p,"template/base");r.info(`
|
|
12
|
+
Using: ${b.cyan.bold(o)}
|
|
13
|
+
`);let i=We(`Scaffolding in: ${n}...
|
|
14
|
+
`).start();if(R.existsSync(n))if(R.readdirSync(n).length===0)e!=="."&&i.info(`${b.cyan.bold(e)} exists but is empty, continuing...
|
|
15
|
+
`);else{i.stopAndPersist();let a=await k.select({message:`${b.redBright.bold("Warning:")} ${b.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(a)||a==="abort")&&(i.fail("Aborting installation..."),process.exit(1));let c=await k.confirm({message:`Are you sure you want to ${a==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(k.isCancel(c)||!c)&&(i.fail("Aborting installation..."),process.exit(1)),a==="clear"&&(i.info(`Emptying ${b.cyan.bold(e)} and creating next-ts-cli app..
|
|
16
|
+
`),R.emptyDirSync(n))}i.start(),R.copySync(t,n);let s=e==="."?"App":b.cyan.bold(e);i.succeed(`${s} ${b.green("scaffolded successfully!")}
|
|
17
|
+
`)};import He from"path";import Ye from"fs-extra";var ee=({projectDir:e,projectName:n,packages:o})=>{let t=Xe(n,o);Ye.writeFileSync(He.join(e,"README.md"),t)},Xe=(e,n)=>{let o=[];o.push(`# ${e}
|
|
18
|
+
|
|
19
|
+
This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/next-ts-cli).
|
|
20
|
+
|
|
21
|
+
## Base Tech Stack
|
|
22
|
+
|
|
23
|
+
- **Next.js 16** \u2014 React framework with App Router
|
|
24
|
+
- **Tailwind CSS 4** \u2014 Utility-first CSS
|
|
25
|
+
- **Husky** \u2014 Git hooks
|
|
26
|
+
- **Commitlint** \u2014 Commit message linting
|
|
27
|
+
- **Lint-staged** \u2014 Run linters on staged files
|
|
28
|
+
- **Biome** \u2014 Linter and formatter
|
|
29
|
+
- **Jest** \u2014 Testing framework`);let t=[];n.clerk.inUse&&t.push("- **Clerk** \u2014 Fast authentication"),n.betterAuth.inUse&&t.push("- **BetterAuth** \u2014 Flexible authentication"),n.supabase.inUse&&t.push("- **Supabase** \u2014 Auth & database with real-time capabilities"),n.drizzle.inUse&&t.push("- **Drizzle ORM** \u2014 Type-safe database queries"),n.shadcnui.inUse&&t.push("- **Shadcn UI** \u2014 UI library"),n.stripe.inUse&&t.push("- **Stripe** \u2014 Payment checkout and webhooks boilerplate to easily create SaaS features"),n.docker.inUse&&t.push("- **Docker** \u2014 Containerization"),n.vercelAi.inUse&&t.push("- **Vercel AI Gateway** \u2014 AI capabilities witsh chat API route boilerplate"),t.length>0&&o.push(`
|
|
30
|
+
### Selected Integrations
|
|
31
|
+
|
|
32
|
+
${t.join(`
|
|
33
|
+
`)}`),o.push(`
|
|
34
|
+
## Getting Started
|
|
35
|
+
|
|
36
|
+
\`\`\`bash
|
|
37
|
+
npm install
|
|
38
|
+
npm run dev
|
|
39
|
+
\`\`\`
|
|
40
|
+
|
|
41
|
+
Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
42
|
+
|
|
43
|
+
## Scripts
|
|
44
|
+
|
|
45
|
+
| Command | Description |
|
|
46
|
+
|---------|-------------|
|
|
47
|
+
| \`npm run dev\` | Start development server |
|
|
48
|
+
| \`npm run build\` | Build for production |
|
|
49
|
+
| \`npm run start\` | Start production server |
|
|
50
|
+
| \`npm run lint\` | Lint code |
|
|
51
|
+
| \`npm run test\` | Run tests |`),n.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 |"),n.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 i=[];return n.drizzle.inUse&&i.push("DATABASE_URL="),n.betterAuth.inUse&&i.push("BETTER_AUTH_SECRET=","BETTER_AUTH_URL=http://localhost:3000"),n.supabase.inUse&&i.push("NEXT_PUBLIC_SUPABASE_URL=","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="),n.stripe.inUse&&i.push("STRIPE_SECRET_KEY=","STRIPE_WEBHOOK_SECRET=","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="),n.clerk.inUse&&i.push("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=","CLERK_SECRET_KEY="),n.vercelAi.inUse&&i.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 te=async({projectName:e,scopedAppName:n,packages:o})=>{let t=u(),i=Ze.resolve(process.cwd(),e);return await Q({projectName:e,projectDir:i,pkgManager:t,scopedAppName:n}),q({projectName:e,scopedAppName:n,projectDir:i,pkgManager:t,packages:o}),ee({projectDir:i,projectName:e,packages:o}),i};import{execSync as M}from"child_process";import N from"path";import*as D from"@clack/prompts";import E from"chalk";import{execa as T}from"execa";import ne from"fs-extra";import qe from"ora";var Qe=e=>{try{return M("git --version",{cwd:e}),!0}catch{return!1}},G=e=>ne.existsSync(N.join(e,".git")),L=async e=>{try{return await T("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},et=()=>{let n=M("git --version").toString().trim().split(" ")[2],o=n?.split(".")[0],t=n?.split(".")[1];return{major:Number(o),minor:Number(t)}},tt=()=>M("git config --global init.defaultBranch || echo main").toString().trim(),oe=async e=>{if(r.info("Initializing Git..."),!Qe(e)){r.warn("Git is not installed. Skipping Git initialization.");return}let n=qe(`Creating a new git repo...
|
|
53
|
+
`).start(),o=G(e),t=await L(e),i=N.parse(e).name;if(t&&o){if(n.stop(),!await D.confirm({message:`${E.redBright.bold("Warning:")} Git is already initialized in "${i}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){n.info("Skipping Git initialization.");return}ne.removeSync(N.join(e,".git"))}else if(t&&!o&&(n.stop(),!await D.confirm({message:`${E.redBright.bold("Warning:")} "${i}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){n.info("Skipping Git initialization.");return}try{let s=tt(),{major:a,minor:c}=et();a<2||a===2&&c<28?(await T("git",["init"],{cwd:e}),await T("git",["symbolic-ref","HEAD",`refs/heads/${s}`],{cwd:e})):await T("git",["init",`--initial-branch=${s}`],{cwd:e}),await T("git",["add","."],{cwd:e}),n.succeed(`${E.green("Successfully initialized and staged")} ${E.green.bold("git")}
|
|
54
|
+
`)}catch{n.fail(`${E.bold.red("Failed:")} could not initialize git. Update git to the latest version!
|
|
55
|
+
`)}};var ie=async({projectName:e=j,packages:n,projectDir:o})=>{let t=u();r.info("Next steps:"),e!=="."&&r.info(` cd ${e}`),t==="yarn"?r.info(` ${t}`):r.info(` ${t} install`),n?.drizzle.inUse&&(["npm","bun"].includes(t)?r.info(` ${t} run db:push`):r.info(` ${t} db:push`)),["npm","bun"].includes(t)?r.info(` ${t} run dev`):r.info(` ${t} dev`),!await L(o)&&!G(o)&&r.info(" git init"),r.info(' git commit -m "initial commit"')};import C from"fs";import nt from"path";function se(e,n,o){let t=C.readdirSync(e);try{t.forEach(i=>{let s=nt.join(e,i);if(C.statSync(s).isDirectory())se(s,n,o);else{let c=C.readFileSync(s,"utf8").replace(new RegExp(n,"g"),o);C.writeFileSync(s,c,"utf8")}})}catch(i){r.error(`Error setting import alias: ${i}`)}}var re=(e,n)=>{try{let o=n.replace(/\*/g,"").replace(/[^/]$/,"$&/");se(e,"@/",o)}catch(o){r.error(`Error setting import alias: ${o}`)}};import ot from"crypto";import it from"path";import st from"fs-extra";var ae=({projectDir:e,packages:n,scopedAppName:o})=>{let t=n?.betterAuth.inUse,i=n?.supabase.inUse,s=n?.drizzle.inUse,a=n?.stripe.inUse,c=n?.clerk.inUse,l=n?.neon.inUse,f=n?.vercelAi.inUse,h=rt(!!t,!!i,!!s,!!a,!!c,!!l,!!f,o),Ne=it.join(e,".env");st.writeFileSync(Ne,h,"utf-8")},rt=(e,n,o,t,i,s,a,c)=>{let l=`
|
|
56
|
+
# Base variables
|
|
57
|
+
NEXT_PUBLIC_BASE_URL="http://localhost:3000"
|
|
58
|
+
GOOGLE_ANALYTICS_TAG="G-xxxxxxx" # Google Analytics G-Tag ID
|
|
59
|
+
`;if(e){let f=Buffer.from(ot.getRandomValues(new Uint8Array(32))).toString("base64");l+=`
|
|
60
|
+
# Better Auth
|
|
61
|
+
# Secret used by Better Auth
|
|
62
|
+
BETTER_AUTH_SECRET="${f}" # Generated by next-ts-cli.
|
|
63
|
+
|
|
64
|
+
# Better Auth GitHub OAuth
|
|
65
|
+
BETTER_AUTH_GITHUB_CLIENT_ID=""
|
|
66
|
+
BETTER_AUTH_GITHUB_CLIENT_SECRET=""
|
|
67
|
+
`}return n&&(l+=`
|
|
68
|
+
# Supabase
|
|
69
|
+
# https://supabase.com/docs/guides/functions/secrets
|
|
70
|
+
|
|
71
|
+
NEXT_PUBLIC_SUPABASE_URL="https://<app-id>.supabase.co",
|
|
72
|
+
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=""
|
|
73
|
+
`),o&&(l+="# Drizzle"),s&&(l+=" + Neon"),l+=`
|
|
74
|
+
`,(o||s)&&(l+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${c}"
|
|
75
|
+
`),t&&(l+=`
|
|
76
|
+
# https://dashboard.stripe.com/apikeys
|
|
77
|
+
|
|
78
|
+
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_51RZ9UnQC7cOBbbnZtnhM1edX2tFsJbI5yXyTXAYPZ9XsgItjaIwOqSBi2r4tVHqKGUsjMci9ZGm7QNcEEqnJ35rs00ZXh0Nf6t
|
|
79
|
+
STRIPE_SECRET_KEY=sk_test_51RZ9UnQC7cOBbbnZLJ5jbTUVZnOESNFCJHs6QOHDyRNwkLvJIdVnNN0n6UDsPlxn2ExWvImO02lrlXZM18EvQhfZ003BpVYc9x
|
|
80
|
+
STRIPE_WEBHOOK_SECRET="whsec_60679b8e1b2ccc566077ad18684e6531bc02fdddbe608327f2c2420aae719648"
|
|
81
|
+
`),i&&(l+=`
|
|
82
|
+
# Clerk
|
|
83
|
+
# https://clerk.com/docs/nextjs/getting-started/quickstart
|
|
84
|
+
|
|
85
|
+
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
|
|
86
|
+
CLERK_SECRET_KEY="sk_test_..."
|
|
87
|
+
|
|
88
|
+
`),a&&(l+=`
|
|
89
|
+
# Vercel AI Gateway
|
|
90
|
+
# https://ai-sdk.dev/docs/getting-started/nextjs-app-router
|
|
91
|
+
|
|
92
|
+
AI_GATEWAY_API_KEY="sk_..."
|
|
93
|
+
`),l};import _ from"path";import de from"fs-extra";import ce from"path";import pe from"fs-extra";import at from"sort-package-json";var le={"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:n,devMode:o,projectDir:t}=e,i=pe.readJSONSync(ce.join(t,"package.json"));n.forEach(a=>{let c=le[a];o&&i.devDependencies?i.devDependencies[a]=c:i.dependencies&&(i.dependencies[a]=c)});let s=at(i);pe.writeJSONSync(ce.join(t,"package.json"),s,{spaces:2})};var me=({projectDir:e,packages:n})=>{let o=n?.drizzle.inUse;d({projectDir:e,dependencies:["better-auth"],devMode:!1});let i=_.join(p,"template/extras"),s=_.join(i,"better-auth"),a="api/auth/[...all]/route.ts",c=_.join(s,a),l=_.join(e,"app",a);de.copySync(c,l);let f=_.join(s,o?"with-drizzle-auth.ts":"base-auth.ts"),h=_.join(e,"lib","auth.ts");de.copySync(f,h)};import w from"path";import ue from"fs-extra";import lt from"path";import fe from"fs-extra";import ct from"sort-package-json";var x=e=>{let{scripts:n,projectDir:o}=e,t=lt.join(o,"package.json"),i=fe.readJSONSync(t);i.scripts={...i.scripts,...n};let s=ct(i);fe.writeJSONSync(t,s,{spaces:2})};var ge=({projectDir:e})=>{d({projectDir:e,dependencies:["drizzle-kit"],devMode:!0}),d({projectDir:e,dependencies:["drizzle-orm","postgres"],devMode:!1});let n=w.join(p,"template/extras"),o=w.join(n,"drizzle"),t=w.join(o,"drizzle.config.ts"),i=w.join(e,"drizzle.config.ts");ue.copySync(t,i);let s=w.join(o,"db"),a=w.join(e,"lib/db");ue.copySync(s,a),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 V from"fs-extra";var he=({projectDir:e})=>{let n=g.join(p,"template/extras"),o=g.join(n,"docker");V.copySync(g.join(o,"Dockerfile"),g.join(e,"Dockerfile")),V.copySync(g.join(o,"docker-compose.prod.yml"),g.join(e,"docker-compose.prod.yml")),V.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 $ from"path";import pt from"fs-extra";var be=({projectDir:e})=>{d({projectDir:e,dependencies:["@supabase/ssr","@supabase/supabase-js"],devMode:!1});let n=$.join(p,"template/extras"),o=$.join(n,"supabase"),t=$.join(e,"lib","supabase");pt.copySync(o,t)};import S from"path";import ke from"fs-extra";var ye=({projectDir:e})=>{d({projectDir:e,dependencies:["stripe","@stripe/stripe-js","@stripe/react-stripe-js"],devMode:!1});let n=S.join(p,"template/extras"),o=S.join(n,"stripe"),t=S.join(o,"checkout_session"),i=S.join(e,"app/api/checkout_session");ke.copySync(t,i);let s=S.join(o,"webhook/stripe/route.ts"),a=S.join(e,"app/api/webhook/stripe/route.ts");ke.copySync(s,a),x({projectDir:e,scripts:{"webhook:local":"stripe listen --forward-to localhost:3000/api/webhook/stripe"}})};import I from"path";import J from"fs-extra";var _e=({projectDir:e})=>{let n=I.join(p,"template/extras"),o=I.join(n,"shadcnui"),t=I.join(o,"components.json"),i=I.join(e,"components.json");J.copySync(t,i);let s=I.join(o,"globals.css"),a=I.join(e,"app/globals.css");J.writeFileSync(a,J.readFileSync(s,"utf8"))};import P from"path";import xe from"fs-extra";var we=({projectDir:e})=>{d({projectDir:e,dependencies:["@clerk/nextjs"],devMode:!1});let n=P.join(p,"template/extras"),o=P.join(n,"clerk"),t=P.join(o,"proxy.ts"),i=P.join(e,"proxy.ts");xe.copySync(t,i);let s=P.join(o,"layout.tsx"),a=P.join(e,"app/layout.tsx");xe.copySync(s,a)};import Se from"path";import dt from"fs-extra";var Ie=({projectDir:e,packages:n})=>{if(d({projectDir:e,dependencies:["@neondatabase/serverless"],devMode:!1}),n?.drizzle.inUse){let o=Se.join(p,"template/extras/neon/index.ts"),t=Se.join(e,"lib/db/index.ts");dt.copySync(o,t)}};import B from"path";import mt from"fs-extra";var Pe=({projectDir:e})=>{d({projectDir:e,dependencies:["ai","@ai-sdk/react"],devMode:!1});let n=B.join(p,"template/extras"),o=B.join(n,"vercel-ai"),t=B.join(o,"route.ts"),i=B.join(e,"app/api/chat/route.ts");mt.copySync(t,i)};var ve=e=>({docker:{inUse:e.includes("docker"),installer:he},betterAuth:{inUse:e.includes("betterAuth"),installer:me},drizzle:{inUse:e.includes("drizzle"),installer:ge},envVariables:{inUse:!0,installer:ae},supabase:{inUse:e.includes("supabase"),installer:be},stripe:{inUse:e.includes("stripe"),installer:ye},shadcnui:{inUse:e.includes("shadcnui"),installer:_e},clerk:{inUse:e.includes("clerk"),installer:we},neon:{inUse:e.includes("neon"),installer:Ie},vercelAi:{inUse:e.includes("vercelAi"),installer:Pe}});import Ae from"path";var Ee=e=>{let o=U(e).split("/"),t=o[o.length-1];if(t==="."){let a=Ae.resolve(process.cwd());t=Ae.basename(a)}let i=o.findIndex(a=>a.startsWith("@"));o.findIndex(a=>a.startsWith("@"))!==-1&&(t=o.slice(i).join("/"));let s=o.filter(a=>!a.startsWith("@")).join("/");return[t,s]};import ft from"gradient-string";var ut=["#003f5b","#5f5195","#cc4c91","#ff6f4e","#ff7030","#f10062","#b1008f","#2c19a8"],Te=()=>{let e=ft(ut),n=u();(n==="yarn"||n==="pnpm")&&console.log(""),console.log(e.multiline(W))};import gt from"chalk";import{execa as je}from"execa";import ze from"ora";var F=async(e,n,o)=>{let{onDataHandle:t,args:i=["install"],stdout:s="pipe"}=o,a=ze(`Running ${n} install...`).start(),c=je(n,i,{cwd:e,stdout:s});return await new Promise((l,f)=>{t&&c.stdout?.on("data",t(a)),c.on("error",h=>f(h)),c.on("close",()=>l())}),a},ht=async(e,n)=>{switch(e){case"npm":return await je(e,["install"],{cwd:n,stderr:"inherit"}),null;case"pnpm":return F(n,e,{onDataHandle:o=>t=>{let i=t.toString();i.includes("Progress")&&(o.text=i.includes("|")?i.split(" | ")[1]??"":i)}});case"yarn":return F(n,e,{onDataHandle:o=>t=>{o.text=t.toString()}});case"bun":return F(n,e,{stdout:"ignore"})}},Ue=async({projectDir:e})=>{r.info("Installing dependencies...");let n=u();(await ht(n,e)??ze()).succeed(gt.green(`Successfully installed dependencies!
|
|
94
|
+
`))};import{execSync as bt}from"child_process";import kt from"https";var Re=e=>{let n=y();n.includes("beta")?(r.warn(" You are using a beta version of next-ts-cli."),r.warn(" Please report any bugs you encounter.")):n.includes("next")?(r.warn(" You are running next-ts-cli with the @next tag which is no longer maintained."),r.warn(" Please run the CLI with @latest instead.")):n!==e&&(r.warn(" You are using an outdated version of next-ts-cli."),r.warn(" Your version:",n+".","Latest version in the npm registry:",e),r.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")};function yt(){return new Promise((e,n)=>{kt.get("https://registry.npmjs.org/-/package/next-ts-cli/dist-tags",o=>{if(o.statusCode===200){let t="";o.on("data",i=>{t+=i}),o.on("end",()=>{e(JSON.parse(t).latest)})}else n()}).on("error",()=>{n()})})}var Ce=()=>yt().catch(()=>{try{return bt("npm view next-ts-cli version").toString().trim()}catch{return null}});var xt=async()=>{let e=await Ce(),n=u();Te(),e&&Re(e);let{appName:o,packages:t,flags:{alias:i}}=await X(),s=ve(t),[a,c]=Ee(o),l=await te({projectName:c,scopedAppName:a,packages:s,alias:i}),f=Oe.readJSONSync(Be.join(l,"package.json"));if(f.name=a,f.nextTsMetadata={initVersion:y()},n!=="bun"){let{stdout:h}=await _t(n,["-v"],{cwd:l});f.packageManager=`${n}@${h.trim()}`}Oe.writeJSONSync(Be.join(l,"package.json"),f,{spaces:2}),i!=="@/"&&re(l,i),await Ue({projectDir:l}),await oe(l),await ie({projectName:c,packages:s,projectDir:l}),process.exit(0)};xt().catch(e=>{r.error("Aborting installation..."),e instanceof Error?r.error(e):(r.error("An unknown error has occurred. Please open an issue on github with the below:"),console.log(e)),process.exit(1)});
|
package/package.json
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "next-ts-cli",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Create production-ready web application with the Next.js",
|
|
5
|
+
"author": "Daniele Rossino <daniele.rossino10@gmail.com>",
|
|
6
|
+
"maintainers": [],
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/RossiFire/next-ts-cli.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"next-ts-cli",
|
|
14
|
+
"next.js",
|
|
15
|
+
"saas",
|
|
16
|
+
"template",
|
|
17
|
+
"supabase",
|
|
18
|
+
"tailwind",
|
|
19
|
+
"typescript"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": "./dist/index.js",
|
|
23
|
+
"bin": {
|
|
24
|
+
"next-ts-cli": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"template",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"package.json"
|
|
32
|
+
],
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=22.0.0"
|
|
35
|
+
},
|
|
36
|
+
"packageManager": "npm@11.7.0",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"typecheck": "tsc",
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"dev": "tsup --watch",
|
|
41
|
+
"clean": "rm -rf dist node_modules",
|
|
42
|
+
"start": "node dist/index.js",
|
|
43
|
+
"lint": "biome check",
|
|
44
|
+
"format": "biome format --write",
|
|
45
|
+
"lint:fix": "biome check --write",
|
|
46
|
+
"release": "changeset version",
|
|
47
|
+
"pub:release": "npm build && npm publish"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@ai-sdk/react": "^3.0.6",
|
|
51
|
+
"@clack/core": "^0.5.0",
|
|
52
|
+
"@clack/prompts": "^0.11.0",
|
|
53
|
+
"@clerk/nextjs": "^6.36.5",
|
|
54
|
+
"@neondatabase/serverless": "^1.0.2",
|
|
55
|
+
"@stripe/react-stripe-js": "^5.4.1",
|
|
56
|
+
"@stripe/stripe-js": "^8.6.0",
|
|
57
|
+
"@supabase/ssr": "^0.8.0",
|
|
58
|
+
"@supabase/supabase-js": "^2.89.0",
|
|
59
|
+
"ai": "^6.0.6",
|
|
60
|
+
"chalk": "5.6.2",
|
|
61
|
+
"commander": "^14.0.2",
|
|
62
|
+
"execa": "^9.6.1",
|
|
63
|
+
"fs-extra": "^11.2.0",
|
|
64
|
+
"gradient-string": "^3.0.0",
|
|
65
|
+
"ora": "9.0.0",
|
|
66
|
+
"sort-package-json": "^3.6.0",
|
|
67
|
+
"stripe": "^20.1.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@auth/drizzle-adapter": "^1.1.0",
|
|
71
|
+
"@biomejs/biome": "2.3.10",
|
|
72
|
+
"@changesets/changelog-github": "^0.4.8",
|
|
73
|
+
"@changesets/cli": "^2.27.3",
|
|
74
|
+
"@libsql/client": "^0.14.0",
|
|
75
|
+
"@planetscale/database": "^1.19.0",
|
|
76
|
+
"@tailwindcss/postcss": "^4.0.15",
|
|
77
|
+
"@total-typescript/ts-reset": "^0.3.7",
|
|
78
|
+
"@types/fs-extra": "^11.0.4",
|
|
79
|
+
"@types/gradient-string": "^1.1.6",
|
|
80
|
+
"@types/node": "^24.10.1",
|
|
81
|
+
"better-auth": "^1.4.10",
|
|
82
|
+
"drizzle-kit": "^0.31.8",
|
|
83
|
+
"drizzle-orm": "^0.41.0",
|
|
84
|
+
"mysql2": "^3.16.0",
|
|
85
|
+
"next": "^15.5.9",
|
|
86
|
+
"next-auth": "^4.24.7",
|
|
87
|
+
"postgres": "^3.4.7",
|
|
88
|
+
"react": "^19.2.3",
|
|
89
|
+
"react-dom": "^19.2.3",
|
|
90
|
+
"superjson": "^2.2.6",
|
|
91
|
+
"tailwindcss": "^4.0.15",
|
|
92
|
+
"tsup": "^8.5.1",
|
|
93
|
+
"type-fest": "^5.3.1",
|
|
94
|
+
"typescript": "^5.8.2",
|
|
95
|
+
"zod": "^3.24.2"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx --no -- commitlint --edit $1
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
npx lint-staged && npm run format
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 RossiFire
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Metadata } from "next";
|
|
2
|
+
import { GoogleAnalytics } from "@next/third-parties/google";
|
|
3
|
+
import { barrio, dmSans } from "@/lib/fonts";
|
|
4
|
+
import { allowIndexing } from "@/lib/indexing";
|
|
5
|
+
import { microdata } from "@/lib/microdata";
|
|
6
|
+
import { cn } from "@/lib/utils";
|
|
7
|
+
import { MicrodataScript } from "@/providers/MicrodataScript";
|
|
8
|
+
import "./globals.css";
|
|
9
|
+
|
|
10
|
+
const homePageMicrodata = microdata("WebSite", {
|
|
11
|
+
name: "next-ts",
|
|
12
|
+
url: process.env.NEXT_PUBLIC_BASE_URL,
|
|
13
|
+
description:
|
|
14
|
+
"next-ts is a Production-Ready and Scalable Next.js Template Starter. Stop wasting time setting up your _next_ big project, with next-ts it's all ready to go!",
|
|
15
|
+
author: "next-ts",
|
|
16
|
+
publisher: "next-ts",
|
|
17
|
+
inLanguage: "en_US",
|
|
18
|
+
isAccessibleForFree: true,
|
|
19
|
+
image: `${process.env.NEXT_PUBLIC_BASE_URL}/public/myimage.png`,
|
|
20
|
+
mainEntityOfPage: {
|
|
21
|
+
"@type": "WebSite",
|
|
22
|
+
"@id": process.env.NEXT_PUBLIC_BASE_URL,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
export const metaData: Metadata = {
|
|
27
|
+
metadataBase: new URL(process.env.NEXT_PUBLIC_BASE_URL || "http://localhost:3000"),
|
|
28
|
+
title: {
|
|
29
|
+
default: "next-ts",
|
|
30
|
+
template: "next-ts - %s",
|
|
31
|
+
},
|
|
32
|
+
description:
|
|
33
|
+
"next-ts is a Production-Ready and Scalable Next.js Template Starter. Stop wasting time setting up your _next_ big project, with next-ts it's all ready to go!",
|
|
34
|
+
keywords: ["next-ts"],
|
|
35
|
+
authors: [{ name: "next-ts" }],
|
|
36
|
+
creator: "next-ts",
|
|
37
|
+
publisher: "next-ts",
|
|
38
|
+
formatDetection: {
|
|
39
|
+
email: false,
|
|
40
|
+
address: false,
|
|
41
|
+
telephone: false,
|
|
42
|
+
},
|
|
43
|
+
alternates: {
|
|
44
|
+
canonical: process.env.NEXT_PUBLIC_BASE_URL,
|
|
45
|
+
},
|
|
46
|
+
...allowIndexing(),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
50
|
+
return (
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<body className={cn("bg-background", barrio.variable, dmSans.variable)}>
|
|
53
|
+
{children}
|
|
54
|
+
<MicrodataScript id="home-microdata" microdata={homePageMicrodata} />
|
|
55
|
+
{process.env.GOOGLE_ANALYTICS_TAG && (
|
|
56
|
+
<GoogleAnalytics gaId={process.env.GOOGLE_ANALYTICS_TAG} />
|
|
57
|
+
)}
|
|
58
|
+
</body>
|
|
59
|
+
</html>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { MetadataRoute } from "next";
|
|
2
|
+
|
|
3
|
+
export default function Manifest(): MetadataRoute.Manifest {
|
|
4
|
+
return {
|
|
5
|
+
name: "My App",
|
|
6
|
+
short_name: "My App",
|
|
7
|
+
description: "My App Description",
|
|
8
|
+
start_url: "/",
|
|
9
|
+
display: "browser",
|
|
10
|
+
background_color: "#ffffff",
|
|
11
|
+
theme_color: "#ffffff",
|
|
12
|
+
orientation: "portrait",
|
|
13
|
+
icons: [
|
|
14
|
+
{
|
|
15
|
+
src: "/icons/icon-192x192.png",
|
|
16
|
+
sizes: "192x192",
|
|
17
|
+
type: "image/png",
|
|
18
|
+
purpose: "maskable",
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
src: "/icons/icon-512x512.png",
|
|
22
|
+
sizes: "512x512",
|
|
23
|
+
type: "image/png",
|
|
24
|
+
purpose: "maskable",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
categories: ["productivity", "utilities"],
|
|
28
|
+
lang: "en",
|
|
29
|
+
dir: "auto",
|
|
30
|
+
scope: "/",
|
|
31
|
+
prefer_related_applications: false,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MetadataRoute } from "next";
|
|
2
|
+
|
|
3
|
+
export default function robots(): MetadataRoute.Robots {
|
|
4
|
+
return {
|
|
5
|
+
// Base rules
|
|
6
|
+
rules: [
|
|
7
|
+
{
|
|
8
|
+
userAgent: "*",
|
|
9
|
+
allow: "/",
|
|
10
|
+
disallow: ["/api/*", "/static/*", "/*.json$", "/*.xml$", "/admin/*", "/private/*"],
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
// Those rules are for blocking GPT and Common Crawl's web crawlers from indexing the site
|
|
14
|
+
// If you want to allow them, you can remove the rules below
|
|
15
|
+
{
|
|
16
|
+
userAgent: "GPTBot",
|
|
17
|
+
disallow: "/",
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
userAgent: "CCBot",
|
|
22
|
+
disallow: "/",
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
sitemap: `${process.env.NEXT_PUBLIC_BASE_URL}/sitemap.xml`,
|
|
26
|
+
host: process.env.NEXT_PUBLIC_BASE_URL,
|
|
27
|
+
};
|
|
28
|
+
}
|