next-ts-cli 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,20 +1,435 @@
1
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=`
2
+
3
+ // src/index.ts
4
+ import path21 from "path";
5
+ import { execa as execa3 } from "execa";
6
+ import fs19 from "fs-extra";
7
+
8
+ // src/cli/index.ts
9
+ import * as p from "@clack/prompts";
10
+ import { Command } from "commander";
11
+
12
+ // src/consts.ts
13
+ import path from "path";
14
+ import { fileURLToPath } from "url";
15
+ var __filename = fileURLToPath(import.meta.url);
16
+ var distPath = path.dirname(__filename);
17
+ var PKG_ROOT = path.join(distPath, "../");
18
+ var TITLE_TEXT = `
3
19
  __ __ __ __
4
20
  .-----.-----.--.--| |_ ______| |_.-----.______.----| |__|
5
21
  | | -__|_ _| _|______| _|__ --|______| __| | |
6
22
  |__|__|_____|__.__|____| |____|_____| |____|__|__|
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,
23
+ `;
24
+ var DEFAULT_APP_NAME = "next-ts-cli";
25
+ var CREATE_NEXT_TS_CLI = "next-ts-cli";
26
+
27
+ // src/utils/getVersion.ts
28
+ import path2 from "path";
29
+ import fs from "fs-extra";
30
+ var getVersion = () => {
31
+ const packageJsonPath = path2.join(PKG_ROOT, "package.json");
32
+ const packageJsonContent = fs.readJSONSync(packageJsonPath);
33
+ return packageJsonContent.version ?? "1.0.0";
34
+ };
35
+
36
+ // src/utils/isTTYError.ts
37
+ var IsTTYError = class extends Error {
38
+ constructor(msg) {
39
+ super(msg);
40
+ }
41
+ };
42
+
43
+ // src/utils/logger.ts
44
+ import chalk from "chalk";
45
+ var logger = {
46
+ error(...args) {
47
+ console.log(chalk.red(...args));
48
+ },
49
+ warn(...args) {
50
+ console.log(chalk.yellow(...args));
51
+ },
52
+ info(...args) {
53
+ console.log(chalk.cyan(...args));
54
+ },
55
+ success(...args) {
56
+ console.log(chalk.green(...args));
57
+ }
58
+ };
59
+
60
+ // src/utils/removeTrailingSlash.ts
61
+ var removeTrailingSlash = (input) => {
62
+ if (input.length > 1 && input.endsWith("/")) {
63
+ input = input.slice(0, -1);
64
+ }
65
+ return input;
66
+ };
67
+
68
+ // src/utils/validateAppName.ts
69
+ var validationRegExp = /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
70
+ var validateAppName = (rawInput) => {
71
+ if (!rawInput) {
72
+ return;
73
+ }
74
+ const input = removeTrailingSlash(rawInput);
75
+ const paths = input.split("/");
76
+ const indexOfDelimiter = paths.findIndex((p4) => p4.startsWith("@"));
77
+ let appName = paths[paths.length - 1];
78
+ if (paths.findIndex((p4) => p4.startsWith("@")) !== -1) {
79
+ appName = paths.slice(indexOfDelimiter).join("/");
80
+ }
81
+ if (input === "." || validationRegExp.test(appName ?? "")) {
82
+ return;
83
+ } else {
84
+ return "App name must consist of only lowercase alphanumeric characters, '-', and '_'";
85
+ }
86
+ };
87
+
88
+ // src/utils/validateImportAlias.ts
89
+ var validateImportAlias = (input) => {
90
+ if (input.startsWith(".") || input.startsWith("/")) {
91
+ return "Import alias can't start with '.' or '/'";
92
+ }
93
+ return;
94
+ };
95
+
96
+ // src/cli/index.ts
97
+ var defaultOptions = {
98
+ appName: DEFAULT_APP_NAME,
99
+ packages: [],
100
+ flags: {
101
+ default: false,
102
+ cli: false,
103
+ drizzle: false,
104
+ supabase: false,
105
+ betterauth: false,
106
+ alias: "@/",
107
+ docker: false,
108
+ stripe: false,
109
+ shadcnui: false,
110
+ clerk: false,
111
+ neon: false,
112
+ vercelAi: false
113
+ }
114
+ };
115
+ var runCli = async () => {
116
+ const cliResults = defaultOptions;
117
+ const program = new Command().name(CREATE_NEXT_TS_CLI).description(
118
+ "A CLI for creating production-ready web applications with the Next.js"
119
+ ).argument(
120
+ "[dir]",
121
+ "The name of the application, as well as the name of the directory to create"
122
+ ).option("--cli", "Run in CLI mode (non-interactive)", false).option(
123
+ "-d, --default",
124
+ "Scaffold the project without any additional integrations",
125
+ false
126
+ ).option(
127
+ "-a, --alias [alias]",
128
+ "Explicitly tell the CLI to use a custom import alias",
129
+ defaultOptions.flags.alias
130
+ ).option(
131
+ "--drizzle [boolean]",
132
+ "Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--cli`.",
133
+ (value) => !!value && value !== "false"
134
+ ).option(
135
+ "--docker [boolean]",
136
+ "Experimental: Boolean value if we should install Docker. Must be used in conjunction with `--cli`.",
137
+ (value) => !!value && value !== "false"
138
+ ).option(
139
+ "--stripe [boolean]",
140
+ "Experimental: Boolean value if we should install Stripe. Must be used in conjunction with `--cli`.",
141
+ (value) => !!value && value !== "false"
142
+ ).option(
143
+ "--shadcnui [boolean]",
144
+ "Experimental: Boolean value if we should install Shadcn UI. Must be used in conjunction with `--cli`.",
145
+ (value) => !!value && value !== "false"
146
+ ).option(
147
+ "--betterauth [boolean]",
148
+ "Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--cli`. Not valid with --clerk",
149
+ (value) => !!value && value !== "false"
150
+ ).option(
151
+ "--clerk [boolean]",
152
+ "Experimental: Boolean value if we should install Clerk.js . Must be used in conjunction with `--cli`. Not valid with --betterAuth",
153
+ (value) => !!value && value !== "false"
154
+ ).option(
155
+ "--supabase [boolean]",
156
+ "Experimental: Boolean value if we should install Supabase. Must be used in conjunction with `--cli`. Not valid with --betterAuth or --clerk",
157
+ (value) => !!value && value !== "false"
158
+ ).option(
159
+ "--neon [boolean]",
160
+ "Experimental: Boolean value if we should install Neon. Must be used in conjunction with `--cli`.",
161
+ (value) => !!value && value !== "false"
162
+ ).option(
163
+ "--vercelAi [boolean]",
164
+ "Experimental: Boolean value if we should install Vercel AI Gateway. Must be used in conjunction with `--cli`.",
165
+ (value) => !!value && value !== "false"
166
+ ).version(getVersion(), "-v, --version", "Display the version number").parse(process.argv);
167
+ if (process.env.npm_config_user_agent?.startsWith("yarn/3")) {
168
+ logger.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
8
169
  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}
170
+ package manager such as pnpm, npm, or Yarn Classic.`);
171
+ }
172
+ const cliProvidedName = program.args[0];
173
+ if (cliProvidedName) {
174
+ cliResults.appName = cliProvidedName;
175
+ }
176
+ cliResults.flags = program.opts();
177
+ if (cliResults.flags.cli) {
178
+ cliResults.packages = [];
179
+ if (cliResults.flags.docker) cliResults.packages.push("docker");
180
+ if (cliResults.flags.drizzle) cliResults.packages.push("drizzle");
181
+ if (cliResults.flags.supabase) cliResults.packages.push("supabase");
182
+ if (cliResults.flags.betterauth) cliResults.packages.push("betterAuth");
183
+ if (cliResults.flags.clerk) cliResults.packages.push("clerk");
184
+ if (cliResults.flags.vercelAi) cliResults.packages.push("vercelAi");
185
+ if (cliResults.flags.supabase && cliResults.flags.betterauth) {
186
+ logger.warn("Incompatible combination Supabase and BetterAuth. Exiting.");
187
+ process.exit(0);
188
+ }
189
+ if (cliResults.flags.clerk && cliResults.flags.betterauth) {
190
+ logger.warn("Incompatible combination Clerk and BetterAuth. Exiting.");
191
+ process.exit(0);
192
+ }
193
+ if (cliResults.flags.supabase && cliResults.flags.clerk) {
194
+ logger.warn("Incompatible combination Supabase and Clerk. Exiting.");
195
+ process.exit(0);
196
+ }
197
+ if (cliResults.flags.stripe) cliResults.packages.push("stripe");
198
+ if (cliResults.flags.shadcnui) cliResults.packages.push("shadcnui");
199
+ return cliResults;
200
+ }
201
+ if (cliResults.flags.default) {
202
+ return cliResults;
203
+ }
204
+ try {
205
+ if (process.env.TERM_PROGRAM?.toLowerCase().includes("mintty")) {
206
+ logger.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
207
+ using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`);
208
+ throw new IsTTYError("Non-interactive environment");
209
+ }
210
+ const project = await p.group(
211
+ {
212
+ ...!cliProvidedName && {
213
+ name: () => p.text({
214
+ message: "Project name",
215
+ defaultValue: cliProvidedName,
216
+ placeholder: defaultOptions.appName,
217
+ validate: validateAppName
218
+ })
219
+ },
220
+ authentication: () => {
221
+ return p.select({
222
+ message: "What authentication provider would you like to use?",
223
+ options: [
224
+ { value: "none", label: "None" },
225
+ { value: "better-auth", label: "BetterAuth" },
226
+ { value: "clerk", label: "Clerk" },
227
+ { value: "supabase", label: "Supabase" }
228
+ ],
229
+ initialValue: "none"
230
+ });
231
+ },
232
+ database: ({ results }) => {
233
+ if (results.authentication === "supabase") {
234
+ return;
235
+ }
236
+ return p.select({
237
+ message: "What database would you like to use?",
238
+ options: [
239
+ { value: "none", label: "None" },
240
+ { value: "neon", label: "Neon" }
241
+ ],
242
+ initialValue: "none"
243
+ });
244
+ },
245
+ orm: () => {
246
+ return p.select({
247
+ message: "What ORM Database would you like to use?",
248
+ options: [
249
+ { value: "none", label: "None" },
250
+ { value: "drizzle", label: "Drizzle" }
251
+ ],
252
+ initialValue: "none"
253
+ });
254
+ },
255
+ importAlias: () => {
256
+ return p.text({
257
+ message: "Custom import aliases",
258
+ defaultValue: defaultOptions.flags.alias,
259
+ placeholder: defaultOptions.flags.alias,
260
+ validate: validateImportAlias
261
+ });
262
+ },
263
+ additionalConfig: () => {
264
+ return p.multiselect({
265
+ message: "Select additional tools to install (press enter to skip)",
266
+ options: [
267
+ { value: "docker", label: "Docker" },
268
+ { value: "stripe", label: "Stripe" },
269
+ { value: "shadcnui", label: "Shadcn UI" },
270
+ { value: "vercelAi", label: "Vercel AI Gateway" }
271
+ ],
272
+ required: false
273
+ });
274
+ }
275
+ },
276
+ {
277
+ onCancel() {
278
+ process.exit(1);
279
+ }
280
+ }
281
+ );
282
+ const packages = [];
283
+ const additionalConfig = project.additionalConfig;
284
+ if (project.orm === "drizzle") packages.push("drizzle");
285
+ if (project.authentication === "better-auth") packages.push("betterAuth");
286
+ if (project.authentication === "clerk") packages.push("clerk");
287
+ if (additionalConfig.includes("supabase") || project.authentication === "supabase")
288
+ packages.push("supabase");
289
+ if (project.database === "neon") packages.push("neon");
290
+ if (additionalConfig.includes("docker")) packages.push("docker");
291
+ if (additionalConfig.includes("stripe")) packages.push("stripe");
292
+ if (additionalConfig.includes("shadcnui")) packages.push("shadcnui");
293
+ if (additionalConfig.includes("vercelAi")) packages.push("vercelAi");
294
+ return {
295
+ appName: project.name ?? cliResults.appName,
296
+ packages,
297
+ flags: {
298
+ ...cliResults.flags,
299
+ alias: project.importAlias ?? cliResults.flags.alias
300
+ }
301
+ };
302
+ } catch (err) {
303
+ if (err instanceof IsTTYError) {
304
+ logger.warn(`
305
+ ${CREATE_NEXT_TS_CLI} needs an interactive terminal to provide options`);
306
+ const shouldContinue = await p.confirm({
307
+ message: `Continue scaffolding a default next-ts-cli?`,
308
+ initialValue: true
309
+ });
310
+ if (!shouldContinue) {
311
+ logger.info("Exiting...");
312
+ process.exit(0);
313
+ }
314
+ logger.info(
315
+ `Bootstrapping a default next-ts-cli in ./${cliResults.appName}`
316
+ );
317
+ } else {
318
+ throw err;
319
+ }
320
+ }
321
+ return cliResults;
322
+ };
323
+
324
+ // src/helpers/createProject.ts
325
+ import path5 from "path";
326
+
327
+ // src/helpers/installPackages.ts
328
+ import chalk2 from "chalk";
329
+ import ora from "ora";
330
+ var installPackages = (options) => {
331
+ const { packages } = options;
332
+ logger.info("Adding boilerplate...");
333
+ for (const [name, pkgOpts] of Object.entries(packages)) {
334
+ if (pkgOpts.inUse) {
335
+ const spinner = ora(`Boilerplating ${name}...`).start();
336
+ pkgOpts.installer(options);
337
+ spinner.succeed(
338
+ chalk2.green(
339
+ `Successfully setup boilerplate for ${chalk2.green.bold(name)}`
340
+ )
341
+ );
342
+ }
343
+ }
344
+ logger.info("");
345
+ };
346
+
347
+ // src/helpers/scaffoldProject.ts
348
+ import path3 from "path";
349
+ import * as p2 from "@clack/prompts";
350
+ import chalk3 from "chalk";
351
+ import fs2 from "fs-extra";
352
+ import ora2 from "ora";
353
+ var scaffoldProject = async ({
354
+ projectName,
355
+ projectDir,
356
+ pkgManager
357
+ }) => {
358
+ const srcDir = path3.join(PKG_ROOT, "template/base");
359
+ logger.info(`
360
+ Using: ${chalk3.cyan.bold(pkgManager)}
361
+ `);
362
+ const spinner = ora2(`Scaffolding in: ${projectDir}...
363
+ `).start();
364
+ if (fs2.existsSync(projectDir)) {
365
+ if (fs2.readdirSync(projectDir).length === 0) {
366
+ if (projectName !== ".")
367
+ spinner.info(
368
+ `${chalk3.cyan.bold(projectName)} exists but is empty, continuing...
369
+ `
370
+ );
371
+ } else {
372
+ spinner.stopAndPersist();
373
+ const overwriteDir = await p2.select({
374
+ message: `${chalk3.redBright.bold("Warning:")} ${chalk3.cyan.bold(
375
+ projectName
376
+ )} already exists and isn't empty. How would you like to proceed?`,
377
+ options: [
378
+ {
379
+ label: "Abort installation (recommended)",
380
+ value: "abort"
381
+ },
382
+ {
383
+ label: "Clear the directory and continue installation",
384
+ value: "clear"
385
+ },
386
+ {
387
+ label: "Continue installation and overwrite conflicting files",
388
+ value: "overwrite"
389
+ }
390
+ ],
391
+ initialValue: "abort"
392
+ });
393
+ if (p2.isCancel(overwriteDir) || overwriteDir === "abort") {
394
+ spinner.fail("Aborting installation...");
395
+ process.exit(1);
396
+ }
397
+ const confirmOverwriteDir = await p2.confirm({
398
+ message: `Are you sure you want to ${overwriteDir === "clear" ? "clear the directory" : "overwrite conflicting files"}?`,
399
+ initialValue: false
400
+ });
401
+ if (p2.isCancel(confirmOverwriteDir) || !confirmOverwriteDir) {
402
+ spinner.fail("Aborting installation...");
403
+ process.exit(1);
404
+ }
405
+ if (overwriteDir === "clear") {
406
+ spinner.info(
407
+ `Emptying ${chalk3.cyan.bold(projectName)} and creating next-ts-cli app..
408
+ `
409
+ );
410
+ fs2.emptyDirSync(projectDir);
411
+ }
412
+ }
413
+ }
414
+ spinner.start();
415
+ fs2.copySync(srcDir, projectDir);
416
+ const scaffoldedName = projectName === "." ? "App" : chalk3.cyan.bold(projectName);
417
+ spinner.succeed(
418
+ `${scaffoldedName} ${chalk3.green("scaffolded successfully!")}
419
+ `
420
+ );
421
+ };
422
+
423
+ // src/helpers/generateReadme.ts
424
+ import path4 from "path";
425
+ import fs3 from "fs-extra";
426
+ var generateReadme = ({ projectDir, projectName, packages }) => {
427
+ const content = generateReadmeContent(projectName, packages);
428
+ fs3.writeFileSync(path4.join(projectDir, "README.md"), content);
429
+ };
430
+ var generateReadmeContent = (projectName, packages) => {
431
+ const sections = [];
432
+ sections.push(`# ${projectName}
18
433
 
19
434
  This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/next-ts-cli).
20
435
 
@@ -26,11 +441,23 @@ This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/ne
26
441
  - **Commitlint** \u2014 Commit message linting
27
442
  - **Lint-staged** \u2014 Run linters on staged files
28
443
  - **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(`
444
+ - **Jest** \u2014 Testing framework`);
445
+ const features = [];
446
+ if (packages.clerk.inUse) features.push("- **Clerk** \u2014 Fast authentication");
447
+ if (packages.betterAuth.inUse) features.push("- **BetterAuth** \u2014 Flexible authentication");
448
+ if (packages.supabase.inUse) features.push("- **Supabase** \u2014 Auth & database with real-time capabilities");
449
+ if (packages.drizzle.inUse) features.push("- **Drizzle ORM** \u2014 Type-safe database queries");
450
+ if (packages.shadcnui.inUse) features.push("- **Shadcn UI** \u2014 UI library");
451
+ if (packages.stripe.inUse) features.push("- **Stripe** \u2014 Payment checkout and webhooks boilerplate to easily create SaaS features");
452
+ if (packages.docker.inUse) features.push("- **Docker** \u2014 Containerization");
453
+ if (packages.vercelAi.inUse) features.push("- **Vercel AI Gateway** \u2014 AI capabilities witsh chat API route boilerplate");
454
+ if (features.length > 0) {
455
+ sections.push(`
30
456
  ### Selected Integrations
31
457
 
32
- ${t.join(`
33
- `)}`),o.push(`
458
+ ${features.join("\n")}`);
459
+ }
460
+ sections.push(`
34
461
  ## Getting Started
35
462
 
36
463
  \`\`\`bash
@@ -48,47 +475,944 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
48
475
  | \`npm run build\` | Build for production |
49
476
  | \`npm run start\` | Start production server |
50
477
  | \`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=`
478
+ | \`npm run test\` | Run tests |`);
479
+ if (packages.docker.inUse) {
480
+ sections.push(`| \`npm run docker:build\` | Build Docker image |
481
+ | \`npm run docker:up\` | Start containers |
482
+ | \`npm run docker:down\` | Stop containers |`);
483
+ }
484
+ if (packages.drizzle.inUse) {
485
+ sections.push(`| \`npm run db:generate\` | Generate migrations |
486
+ | \`npm run db:migrate\` | Run migrations |
487
+ | \`npm run db:studio\` | Open Drizzle Studio |`);
488
+ }
489
+ const envVars = [];
490
+ if (packages.drizzle.inUse) {
491
+ envVars.push("DATABASE_URL=");
492
+ }
493
+ if (packages.betterAuth.inUse) {
494
+ envVars.push("BETTER_AUTH_SECRET=", "BETTER_AUTH_URL=http://localhost:3000");
495
+ }
496
+ if (packages.supabase.inUse) {
497
+ envVars.push("NEXT_PUBLIC_SUPABASE_URL=", "NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=");
498
+ }
499
+ if (packages.stripe.inUse) {
500
+ envVars.push("STRIPE_SECRET_KEY=", "STRIPE_WEBHOOK_SECRET=", "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=");
501
+ }
502
+ if (packages.clerk.inUse) {
503
+ envVars.push("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=", "CLERK_SECRET_KEY=");
504
+ }
505
+ if (packages.vercelAi.inUse) {
506
+ envVars.push("AI_GATEWAY_API_KEY=");
507
+ }
508
+ return sections.join("\n");
509
+ };
510
+
511
+ // src/utils/getUserPkgManager.ts
512
+ var getUserPkgManager = () => {
513
+ const userAgent = process.env.npm_config_user_agent;
514
+ if (userAgent) {
515
+ if (userAgent.startsWith("yarn")) {
516
+ return "yarn";
517
+ } else if (userAgent.startsWith("pnpm")) {
518
+ return "pnpm";
519
+ } else if (userAgent.startsWith("bun")) {
520
+ return "bun";
521
+ } else {
522
+ return "npm";
523
+ }
524
+ } else {
525
+ return "npm";
526
+ }
527
+ };
528
+
529
+ // src/helpers/createProject.ts
530
+ var createProject = async ({
531
+ projectName,
532
+ scopedAppName,
533
+ packages
534
+ }) => {
535
+ const pkgManager = getUserPkgManager();
536
+ const projectDir = path5.resolve(process.cwd(), projectName);
537
+ await scaffoldProject({
538
+ projectName,
539
+ projectDir,
540
+ pkgManager,
541
+ scopedAppName
542
+ });
543
+ installPackages({
544
+ projectName,
545
+ scopedAppName,
546
+ projectDir,
547
+ pkgManager,
548
+ packages
549
+ });
550
+ generateReadme({ projectDir, projectName, packages });
551
+ return projectDir;
552
+ };
553
+
554
+ // src/helpers/git.ts
555
+ import { execSync } from "child_process";
556
+ import path6 from "path";
557
+ import * as p3 from "@clack/prompts";
558
+ import chalk4 from "chalk";
559
+ import { execa } from "execa";
560
+ import fs4 from "fs-extra";
561
+ import ora3 from "ora";
562
+ var isGitInstalled = (dir) => {
563
+ try {
564
+ execSync("git --version", { cwd: dir });
565
+ return true;
566
+ } catch {
567
+ return false;
568
+ }
569
+ };
570
+ var isRootGitRepo = (dir) => {
571
+ return fs4.existsSync(path6.join(dir, ".git"));
572
+ };
573
+ var isInsideGitRepo = async (dir) => {
574
+ try {
575
+ await execa("git", ["rev-parse", "--is-inside-work-tree"], {
576
+ cwd: dir,
577
+ stdout: "ignore"
578
+ });
579
+ return true;
580
+ } catch {
581
+ return false;
582
+ }
583
+ };
584
+ var getGitVersion = () => {
585
+ const stdout = execSync("git --version").toString().trim();
586
+ const gitVersionTag = stdout.split(" ")[2];
587
+ const major = gitVersionTag?.split(".")[0];
588
+ const minor = gitVersionTag?.split(".")[1];
589
+ return { major: Number(major), minor: Number(minor) };
590
+ };
591
+ var getDefaultBranch = () => {
592
+ const stdout = execSync("git config --global init.defaultBranch || echo main").toString().trim();
593
+ return stdout;
594
+ };
595
+ var initializeGit = async (projectDir) => {
596
+ logger.info("Initializing Git...");
597
+ if (!isGitInstalled(projectDir)) {
598
+ logger.warn("Git is not installed. Skipping Git initialization.");
599
+ return;
600
+ }
601
+ const spinner = ora3("Creating a new git repo...\n").start();
602
+ const isRoot = isRootGitRepo(projectDir);
603
+ const isInside = await isInsideGitRepo(projectDir);
604
+ const dirName = path6.parse(projectDir).name;
605
+ if (isInside && isRoot) {
606
+ spinner.stop();
607
+ const overwriteGit = await p3.confirm({
608
+ message: `${chalk4.redBright.bold(
609
+ "Warning:"
610
+ )} Git is already initialized in "${dirName}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,
611
+ initialValue: false
612
+ });
613
+ if (!overwriteGit) {
614
+ spinner.info("Skipping Git initialization.");
615
+ return;
616
+ }
617
+ fs4.removeSync(path6.join(projectDir, ".git"));
618
+ } else if (isInside && !isRoot) {
619
+ spinner.stop();
620
+ const initializeChildGitRepo = await p3.confirm({
621
+ message: `${chalk4.redBright.bold(
622
+ "Warning:"
623
+ )} "${dirName}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,
624
+ initialValue: false
625
+ });
626
+ if (!initializeChildGitRepo) {
627
+ spinner.info("Skipping Git initialization.");
628
+ return;
629
+ }
630
+ }
631
+ try {
632
+ const branchName = getDefaultBranch();
633
+ const { major, minor } = getGitVersion();
634
+ if (major < 2 || major === 2 && minor < 28) {
635
+ await execa("git", ["init"], { cwd: projectDir });
636
+ await execa("git", ["symbolic-ref", "HEAD", `refs/heads/${branchName}`], {
637
+ cwd: projectDir
638
+ });
639
+ } else {
640
+ await execa("git", ["init", `--initial-branch=${branchName}`], {
641
+ cwd: projectDir
642
+ });
643
+ }
644
+ await execa("git", ["add", "."], { cwd: projectDir });
645
+ spinner.succeed(
646
+ `${chalk4.green("Successfully initialized and staged")} ${chalk4.green.bold(
647
+ "git"
648
+ )}
649
+ `
650
+ );
651
+ } catch {
652
+ spinner.fail(
653
+ `${chalk4.bold.red(
654
+ "Failed:"
655
+ )} could not initialize git. Update git to the latest version!
656
+ `
657
+ );
658
+ }
659
+ };
660
+
661
+ // src/helpers/logNextSteps.ts
662
+ var logNextSteps = async ({
663
+ projectName = DEFAULT_APP_NAME
664
+ }) => {
665
+ const pkgManager = getUserPkgManager();
666
+ logger.info("Next steps:");
667
+ if (projectName !== ".") {
668
+ logger.info(` cd ${projectName}`);
669
+ }
670
+ if (["npm", "bun"].includes(pkgManager)) {
671
+ logger.info(` ${pkgManager} run dev`);
672
+ } else {
673
+ logger.info(` ${pkgManager} dev`);
674
+ }
675
+ };
676
+
677
+ // src/helpers/setImportAlias.ts
678
+ import fs5 from "fs";
679
+ import path7 from "path";
680
+ function replaceTextInFiles(directoryPath, search, replacement) {
681
+ const files = fs5.readdirSync(directoryPath);
682
+ try {
683
+ files.forEach((file) => {
684
+ const filePath = path7.join(directoryPath, file);
685
+ if (fs5.statSync(filePath).isDirectory()) {
686
+ replaceTextInFiles(filePath, search, replacement);
687
+ } else {
688
+ const data = fs5.readFileSync(filePath, "utf8");
689
+ const updatedData = data.replace(new RegExp(search, "g"), replacement);
690
+ fs5.writeFileSync(filePath, updatedData, "utf8");
691
+ }
692
+ });
693
+ } catch (error) {
694
+ logger.error(`Error setting import alias: ${error}`);
695
+ }
696
+ }
697
+ var setImportAlias = (projectDir, alias) => {
698
+ try {
699
+ const normalizedImportAlias = alias.replace(/\*/g, "").replace(/[^/]$/, "$&/");
700
+ replaceTextInFiles(projectDir, `@/`, normalizedImportAlias);
701
+ } catch (error) {
702
+ logger.error(`Error setting import alias: ${error}`);
703
+ }
704
+ };
705
+
706
+ // src/installers/envVars.ts
707
+ import crypto from "crypto";
708
+ import path8 from "path";
709
+ import fs6 from "fs-extra";
710
+ var envVariablesInstaller = ({
711
+ projectDir,
712
+ packages,
713
+ scopedAppName
714
+ }) => {
715
+ const usingBetterAuth = packages?.betterAuth.inUse;
716
+ const usingSupabase = packages?.supabase.inUse;
717
+ const usingDrizzle = packages?.drizzle.inUse;
718
+ const usingStripe = packages?.stripe.inUse;
719
+ const usingClerk = packages?.clerk.inUse;
720
+ const usingNeon = packages?.neon.inUse;
721
+ const usingVercelAi = packages?.vercelAi.inUse;
722
+ const envContent = getEnvContent(
723
+ !!usingBetterAuth,
724
+ !!usingSupabase,
725
+ !!usingDrizzle,
726
+ !!usingStripe,
727
+ !!usingClerk,
728
+ !!usingNeon,
729
+ !!usingVercelAi,
730
+ scopedAppName
731
+ );
732
+ const envDest = path8.join(projectDir, ".env");
733
+ fs6.writeFileSync(envDest, envContent, "utf-8");
734
+ };
735
+ var getEnvContent = (usingBetterAuth, usingSupabase, usingDrizzle, usingStripe, usingClerk, usingNeon, usingVercelAi, scopedAppName) => {
736
+ let content = `
56
737
  # Base variables
57
738
  NEXT_PUBLIC_BASE_URL="http://localhost:3000"
58
739
  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+=`
740
+ `;
741
+ if (usingBetterAuth) {
742
+ const secret = Buffer.from(
743
+ crypto.getRandomValues(new Uint8Array(32))
744
+ ).toString("base64");
745
+ content += `
60
746
  # Better Auth
61
- # Secret used by Better Auth
62
- BETTER_AUTH_SECRET="${f}" # Generated by next-ts-cli.
747
+ BETTER_AUTH_SECRET="${secret}" # Generated by next-ts-cli.
63
748
 
64
749
  # Better Auth GitHub OAuth
65
750
  BETTER_AUTH_GITHUB_CLIENT_ID=""
66
751
  BETTER_AUTH_GITHUB_CLIENT_SECRET=""
67
- `}return n&&(l+=`
752
+
753
+ `;
754
+ }
755
+ if (usingSupabase)
756
+ content += `
68
757
  # Supabase
69
758
  # https://supabase.com/docs/guides/functions/secrets
70
-
71
759
  NEXT_PUBLIC_SUPABASE_URL="https://<app-id>.supabase.co",
72
760
  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+=`
761
+ `;
762
+ if (usingDrizzle) content += "# Drizzle";
763
+ if (usingNeon) content += " + Neon";
764
+ content += "\n";
765
+ if (usingDrizzle || usingNeon) {
766
+ content += `DATABASE_URL="postgresql://postgres:password@localhost:5432/${scopedAppName}"
767
+ `;
768
+ }
769
+ if (usingStripe) {
770
+ content += `
76
771
  # https://dashboard.stripe.com/apikeys
77
772
 
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+=`
773
+ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
774
+ STRIPE_SECRET_KEY=""
775
+ STRIPE_WEBHOOK_SECRET=""
776
+ `;
777
+ }
778
+ if (usingClerk) {
779
+ content += `
82
780
  # Clerk
83
781
  # https://clerk.com/docs/nextjs/getting-started/quickstart
84
-
85
782
  NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
86
783
  CLERK_SECRET_KEY="sk_test_..."
87
784
 
88
- `),a&&(l+=`
785
+ `;
786
+ }
787
+ if (usingVercelAi) {
788
+ content += `
89
789
  # Vercel AI Gateway
90
790
  # https://ai-sdk.dev/docs/getting-started/nextjs-app-router
91
-
92
791
  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)});
792
+ `;
793
+ }
794
+ return content;
795
+ };
796
+
797
+ // src/installers/betterAuth.ts
798
+ import path10 from "path";
799
+ import fs8 from "fs-extra";
800
+
801
+ // src/utils/addPackageDependency.ts
802
+ import path9 from "path";
803
+ import fs7 from "fs-extra";
804
+ import sortPackageJson from "sort-package-json";
805
+
806
+ // src/installers/dependencyVersionMap.ts
807
+ var dependencyVersionMap = {
808
+ // Better-Auth
809
+ "better-auth": "^1.4.10",
810
+ // Drizzle
811
+ "drizzle-kit": "^0.31.5",
812
+ "drizzle-orm": "^0.41.0",
813
+ "postgres": "^3.4.7",
814
+ // TailwindCSS
815
+ "tailwindcss": "^4.0.15",
816
+ "postcss": "^8.5.3",
817
+ "@tailwindcss/postcss": "^4.0.15",
818
+ // biome
819
+ "@biomejs/biome": "^2.3.10",
820
+ // Supabase
821
+ "@supabase/ssr": "^0.8.0",
822
+ "@supabase/supabase-js": "^2.89.0",
823
+ // Stripe
824
+ "@stripe/react-stripe-js": "^5.4.1",
825
+ "@stripe/stripe-js": "^8.6.0",
826
+ "stripe": "^20.1.0",
827
+ // Clerk
828
+ "@clerk/nextjs": "^6.36.5",
829
+ // Neon
830
+ "@neondatabase/serverless": "^1.0.2",
831
+ // Vercel AI Gateway
832
+ "ai": "^6.0.6",
833
+ "@ai-sdk/react": "^3.0.6"
834
+ };
835
+
836
+ // src/utils/addPackageDependency.ts
837
+ var addPackageDependency = (opts) => {
838
+ const { dependencies, devMode, projectDir } = opts;
839
+ const pkgJson = fs7.readJSONSync(
840
+ path9.join(projectDir, "package.json")
841
+ );
842
+ dependencies.forEach((pkgName) => {
843
+ const version = dependencyVersionMap[pkgName];
844
+ if (devMode && pkgJson.devDependencies) {
845
+ pkgJson.devDependencies[pkgName] = version;
846
+ } else if (pkgJson.dependencies) {
847
+ pkgJson.dependencies[pkgName] = version;
848
+ }
849
+ });
850
+ const sortedPkgJson = sortPackageJson(pkgJson);
851
+ fs7.writeJSONSync(path9.join(projectDir, "package.json"), sortedPkgJson, {
852
+ spaces: 2
853
+ });
854
+ };
855
+
856
+ // src/installers/betterAuth.ts
857
+ var betterAuthInstaller = ({ projectDir, packages }) => {
858
+ const usingDrizzle = packages?.drizzle.inUse;
859
+ const deps = ["better-auth"];
860
+ addPackageDependency({
861
+ projectDir,
862
+ dependencies: deps,
863
+ devMode: false
864
+ });
865
+ const extrasDir = path10.join(PKG_ROOT, "template/extras");
866
+ const betterAuthDir = path10.join(extrasDir, "better-auth");
867
+ const apiHandlerRelPath = "api/auth/[...all]/route.ts";
868
+ const apiHandlerSrc = path10.join(betterAuthDir, apiHandlerRelPath);
869
+ const apiHandlerDest = path10.join(projectDir, "app", apiHandlerRelPath);
870
+ fs8.copySync(apiHandlerSrc, apiHandlerDest);
871
+ const authConfigSrc = path10.join(
872
+ betterAuthDir,
873
+ usingDrizzle ? "with-drizzle-auth.ts" : "base-auth.ts"
874
+ );
875
+ const authConfigDest = path10.join(projectDir, "lib", "auth.ts");
876
+ fs8.copySync(authConfigSrc, authConfigDest);
877
+ };
878
+
879
+ // src/installers/drizzle.ts
880
+ import path12 from "path";
881
+ import fs10 from "fs-extra";
882
+
883
+ // src/utils/addPackageScript.ts
884
+ import path11 from "path";
885
+ import fs9 from "fs-extra";
886
+ import sortPackageJson2 from "sort-package-json";
887
+ var addPackageScript = (opts) => {
888
+ const { scripts, projectDir } = opts;
889
+ const packageJsonPath = path11.join(projectDir, "package.json");
890
+ const packageJsonContent = fs9.readJSONSync(packageJsonPath);
891
+ packageJsonContent.scripts = {
892
+ ...packageJsonContent.scripts,
893
+ ...scripts
894
+ };
895
+ const sortedPkgJson = sortPackageJson2(packageJsonContent);
896
+ fs9.writeJSONSync(packageJsonPath, sortedPkgJson, {
897
+ spaces: 2
898
+ });
899
+ };
900
+
901
+ // src/installers/drizzle.ts
902
+ var drizzleInstaller = ({ projectDir }) => {
903
+ addPackageDependency({
904
+ projectDir,
905
+ dependencies: ["drizzle-kit"],
906
+ devMode: true
907
+ });
908
+ addPackageDependency({
909
+ projectDir,
910
+ dependencies: ["drizzle-orm", "postgres"],
911
+ devMode: false
912
+ });
913
+ const extrasDir = path12.join(PKG_ROOT, "template/extras");
914
+ const drizzleDir = path12.join(extrasDir, "drizzle");
915
+ const configFileSrc = path12.join(drizzleDir, "drizzle.config.ts");
916
+ const configFileDest = path12.join(projectDir, "drizzle.config.ts");
917
+ fs10.copySync(configFileSrc, configFileDest);
918
+ const dbFolderSrc = path12.join(drizzleDir, "db");
919
+ const dbFolderDest = path12.join(projectDir, "lib/db");
920
+ fs10.copySync(dbFolderSrc, dbFolderDest);
921
+ addPackageScript({
922
+ projectDir,
923
+ scripts: {
924
+ "db:push": "drizzle-kit push",
925
+ "db:studio": "drizzle-kit studio",
926
+ "db:generate": "drizzle-kit generate",
927
+ "db:migrate": "drizzle-kit migrate"
928
+ }
929
+ });
930
+ };
931
+
932
+ // src/installers/docker.ts
933
+ import path13 from "path";
934
+ import fs11 from "fs-extra";
935
+ var dockerInstaller = ({ projectDir }) => {
936
+ const extrasDir = path13.join(PKG_ROOT, "template/extras");
937
+ const dockerFilesDir = path13.join(extrasDir, "docker");
938
+ fs11.copySync(
939
+ path13.join(dockerFilesDir, "Dockerfile"),
940
+ path13.join(projectDir, "Dockerfile")
941
+ );
942
+ fs11.copySync(
943
+ path13.join(dockerFilesDir, "docker-compose.prod.yml"),
944
+ path13.join(projectDir, "docker-compose.prod.yml")
945
+ );
946
+ fs11.copySync(
947
+ path13.join(dockerFilesDir, ".dockerignore"),
948
+ path13.join(projectDir, ".dockerignore")
949
+ );
950
+ addPackageScript({
951
+ projectDir,
952
+ scripts: {
953
+ "docker:build": "docker compose -f docker-compose.prod.yml build",
954
+ "docker:up": "docker compose -f docker-compose.prod.yml up -d",
955
+ "docker:down": "docker compose -f docker-compose.prod.yml down",
956
+ "docker:logs": "docker compose -f docker-compose.prod.yml logs -f",
957
+ "docker:exec": "docker compose -f docker-compose.prod.yml exec app sh",
958
+ "docker:exec:bash": "docker compose -f docker-compose.prod.yml exec app bash",
959
+ "docker:exec:sh": "docker compose -f docker-compose.prod.yml exec app sh",
960
+ "docker:exec:npm": "docker compose -f docker-compose.prod.yml exec app npm"
961
+ }
962
+ });
963
+ };
964
+
965
+ // src/installers/supabase.ts
966
+ import path15 from "path";
967
+ import fs13 from "fs-extra";
968
+
969
+ // src/utils/addMcpServer.ts
970
+ import path14 from "path";
971
+ import fs12 from "fs-extra";
972
+ var addMcpServer = (opts) => {
973
+ const { serverName, serverConfig, projectDir } = opts;
974
+ addCursorMcpServer({
975
+ serverName,
976
+ serverConfig,
977
+ projectDir
978
+ });
979
+ addVsCodeMcpServer({
980
+ serverName,
981
+ serverConfig,
982
+ projectDir
983
+ });
984
+ };
985
+ var addCursorMcpServer = (opts) => {
986
+ const { serverName, serverConfig, projectDir } = opts;
987
+ const mcpJsonPath = path14.join(projectDir, ".cusror/mpc.json");
988
+ ;
989
+ fs12.ensureDirSync(path14.dirname(mcpJsonPath));
990
+ const cursorConfig = {
991
+ args: serverConfig.args,
992
+ command: serverConfig.command,
993
+ env: serverConfig.env,
994
+ url: serverConfig.url
995
+ };
996
+ let mcpJson = {};
997
+ if (fs12.existsSync(mcpJsonPath)) {
998
+ mcpJson = fs12.readJSONSync(mcpJsonPath);
999
+ }
1000
+ if (!mcpJson.mcpServers) {
1001
+ mcpJson.mcpServers = {};
1002
+ }
1003
+ mcpJson.mcpServers[serverName] = { ...cursorConfig };
1004
+ fs12.writeJSONSync(mcpJsonPath, mcpJson, {
1005
+ spaces: " "
1006
+ });
1007
+ };
1008
+ var addVsCodeMcpServer = (opts) => {
1009
+ const { serverName, serverConfig, projectDir } = opts;
1010
+ const mcpJsonPath = path14.join(projectDir, ".vscode/mcp.json");
1011
+ fs12.ensureDirSync(path14.dirname(mcpJsonPath));
1012
+ let mcpJson = {};
1013
+ if (fs12.existsSync(mcpJsonPath)) {
1014
+ mcpJson = fs12.readJSONSync(mcpJsonPath);
1015
+ }
1016
+ if (!mcpJson.servers) {
1017
+ mcpJson.servers = {};
1018
+ }
1019
+ mcpJson.servers[serverName] = { ...serverConfig };
1020
+ fs12.writeJSONSync(mcpJsonPath, mcpJson, {
1021
+ spaces: " "
1022
+ });
1023
+ };
1024
+
1025
+ // src/installers/supabase.ts
1026
+ var supabaseInstaller = ({ projectDir }) => {
1027
+ addPackageDependency({
1028
+ projectDir,
1029
+ dependencies: ["@supabase/ssr", "@supabase/supabase-js"],
1030
+ devMode: false
1031
+ });
1032
+ const extrasDir = path15.join(PKG_ROOT, "template/extras");
1033
+ const supabaseDir = path15.join(extrasDir, "supabase");
1034
+ const supabaseDest = path15.join(projectDir, "lib", "supabase");
1035
+ fs13.copySync(supabaseDir, supabaseDest);
1036
+ addMcpServer({
1037
+ serverName: "supabase",
1038
+ serverConfig: {
1039
+ type: "http",
1040
+ url: "https://mcp.supabase.com/mcp?project_ref=<YOUR_SUPABASE_PROJECT_ID>"
1041
+ },
1042
+ projectDir
1043
+ });
1044
+ };
1045
+
1046
+ // src/installers/stripe.ts
1047
+ import path16 from "path";
1048
+ import fs14 from "fs-extra";
1049
+ var stripeInstaller = ({ projectDir }) => {
1050
+ addPackageDependency({
1051
+ projectDir,
1052
+ dependencies: ["stripe", "@stripe/stripe-js", "@stripe/react-stripe-js"],
1053
+ devMode: false
1054
+ });
1055
+ const extrasDir = path16.join(PKG_ROOT, "template/extras");
1056
+ const stripeFilesDir = path16.join(extrasDir, "stripe");
1057
+ const checkoutSessionRouteSrc = path16.join(stripeFilesDir, "checkout_session");
1058
+ const checkoutSessionRouteDest = path16.join(
1059
+ projectDir,
1060
+ "app/api/checkout_session"
1061
+ );
1062
+ fs14.copySync(checkoutSessionRouteSrc, checkoutSessionRouteDest);
1063
+ const webhookRouteSrc = path16.join(stripeFilesDir, "webhook/stripe/route.ts");
1064
+ const webhookRouteDest = path16.join(
1065
+ projectDir,
1066
+ "app/api/webhook/stripe/route.ts"
1067
+ );
1068
+ fs14.copySync(webhookRouteSrc, webhookRouteDest);
1069
+ addPackageScript({
1070
+ projectDir,
1071
+ scripts: {
1072
+ "webhook:local": "stripe listen --forward-to localhost:3000/api/webhook/stripe"
1073
+ }
1074
+ });
1075
+ addMcpServer({
1076
+ serverName: "stripe",
1077
+ serverConfig: {
1078
+ type: "http",
1079
+ url: "https://mcp.stripe.com"
1080
+ },
1081
+ projectDir
1082
+ });
1083
+ };
1084
+
1085
+ // src/installers/shadcnui.ts
1086
+ import path17 from "path";
1087
+ import fs15 from "fs-extra";
1088
+ var shadcnUIInstaller = ({ projectDir }) => {
1089
+ const extrasDir = path17.join(PKG_ROOT, "template/extras");
1090
+ const shadcnUIDir = path17.join(extrasDir, "shadcnui");
1091
+ const componentJsonFileSrc = path17.join(shadcnUIDir, "components.json");
1092
+ const componentJsonFileDest = path17.join(projectDir, "components.json");
1093
+ fs15.copySync(componentJsonFileSrc, componentJsonFileDest);
1094
+ const globalCssFileSrc = path17.join(shadcnUIDir, "globals.css");
1095
+ const globalCssFileDest = path17.join(projectDir, "app/globals.css");
1096
+ fs15.writeFileSync(
1097
+ globalCssFileDest,
1098
+ fs15.readFileSync(globalCssFileSrc, "utf8")
1099
+ );
1100
+ addMcpServer({
1101
+ serverName: "shadcn",
1102
+ serverConfig: {
1103
+ command: "npx",
1104
+ args: ["shadcn@latest", "mcp"]
1105
+ },
1106
+ projectDir
1107
+ });
1108
+ };
1109
+
1110
+ // src/installers/clerk.ts
1111
+ import path18 from "path";
1112
+ import fs16 from "fs-extra";
1113
+ var clerkInstaller = ({ projectDir }) => {
1114
+ addPackageDependency({
1115
+ projectDir,
1116
+ dependencies: ["@clerk/nextjs"],
1117
+ devMode: false
1118
+ });
1119
+ const extrasDir = path18.join(PKG_ROOT, "template/extras");
1120
+ const clerkFilesDir = path18.join(extrasDir, "clerk");
1121
+ const proxySrc = path18.join(clerkFilesDir, "proxy.ts");
1122
+ const proxyDest = path18.join(projectDir, "proxy.ts");
1123
+ fs16.copySync(proxySrc, proxyDest);
1124
+ const layoutSrc = path18.join(clerkFilesDir, "layout.tsx");
1125
+ const layoutDest = path18.join(projectDir, "app/layout.tsx");
1126
+ fs16.copySync(layoutSrc, layoutDest);
1127
+ };
1128
+
1129
+ // src/installers/neon.ts
1130
+ import path19 from "path";
1131
+ import fs17 from "fs-extra";
1132
+ var neonInstaller = ({ projectDir, packages }) => {
1133
+ addPackageDependency({
1134
+ projectDir,
1135
+ dependencies: ["@neondatabase/serverless"],
1136
+ devMode: false
1137
+ });
1138
+ if (packages?.drizzle.inUse) {
1139
+ const neonDrizzleSrc = path19.join(PKG_ROOT, "template/extras/neon/index.ts");
1140
+ const neonDrizzleDest = path19.join(projectDir, "lib/db/index.ts");
1141
+ fs17.copySync(neonDrizzleSrc, neonDrizzleDest);
1142
+ }
1143
+ };
1144
+
1145
+ // src/installers/vercelAi.ts
1146
+ import path20 from "path";
1147
+ import fs18 from "fs-extra";
1148
+ var vercelAiInstaller = ({ projectDir }) => {
1149
+ addPackageDependency({
1150
+ projectDir,
1151
+ dependencies: ["ai", "@ai-sdk/react"],
1152
+ devMode: false
1153
+ });
1154
+ const extrasDir = path20.join(PKG_ROOT, "template/extras");
1155
+ const vercelAiFilesDir = path20.join(extrasDir, "vercel-ai");
1156
+ const vercelAiRouteSrc = path20.join(vercelAiFilesDir, "route.ts");
1157
+ const vercelAiRouteDest = path20.join(projectDir, "app/api/chat/route.ts");
1158
+ fs18.copySync(vercelAiRouteSrc, vercelAiRouteDest);
1159
+ };
1160
+
1161
+ // src/installers/index.ts
1162
+ var buildPkgInstallerMap = (packages) => ({
1163
+ docker: {
1164
+ inUse: packages.includes("docker"),
1165
+ installer: dockerInstaller
1166
+ },
1167
+ betterAuth: {
1168
+ inUse: packages.includes("betterAuth"),
1169
+ installer: betterAuthInstaller
1170
+ },
1171
+ drizzle: {
1172
+ inUse: packages.includes("drizzle"),
1173
+ installer: drizzleInstaller
1174
+ },
1175
+ envVariables: {
1176
+ inUse: true,
1177
+ installer: envVariablesInstaller
1178
+ },
1179
+ supabase: {
1180
+ inUse: packages.includes("supabase"),
1181
+ installer: supabaseInstaller
1182
+ },
1183
+ stripe: {
1184
+ inUse: packages.includes("stripe"),
1185
+ installer: stripeInstaller
1186
+ },
1187
+ shadcnui: {
1188
+ inUse: packages.includes("shadcnui"),
1189
+ installer: shadcnUIInstaller
1190
+ },
1191
+ clerk: {
1192
+ inUse: packages.includes("clerk"),
1193
+ installer: clerkInstaller
1194
+ },
1195
+ neon: {
1196
+ inUse: packages.includes("neon"),
1197
+ installer: neonInstaller
1198
+ },
1199
+ vercelAi: {
1200
+ inUse: packages.includes("vercelAi"),
1201
+ installer: vercelAiInstaller
1202
+ }
1203
+ });
1204
+
1205
+ // src/utils/parseNameAndPath.ts
1206
+ import pathModule from "path";
1207
+ var parseNameAndPath = (rawInput) => {
1208
+ const input = removeTrailingSlash(rawInput);
1209
+ const paths = input.split("/");
1210
+ let appName = paths[paths.length - 1];
1211
+ if (appName === ".") {
1212
+ const parsedCwd = pathModule.resolve(process.cwd());
1213
+ appName = pathModule.basename(parsedCwd);
1214
+ }
1215
+ const indexOfDelimiter = paths.findIndex((p4) => p4.startsWith("@"));
1216
+ if (paths.findIndex((p4) => p4.startsWith("@")) !== -1) {
1217
+ appName = paths.slice(indexOfDelimiter).join("/");
1218
+ }
1219
+ const path22 = paths.filter((p4) => !p4.startsWith("@")).join("/");
1220
+ return [appName, path22];
1221
+ };
1222
+
1223
+ // src/utils/renderTitle.ts
1224
+ import gradient from "gradient-string";
1225
+ var colors = [
1226
+ "#003f5b",
1227
+ "#5f5195",
1228
+ "#cc4c91",
1229
+ "#ff6f4e",
1230
+ "#ff7030",
1231
+ "#f10062",
1232
+ "#b1008f",
1233
+ "#2c19a8"
1234
+ ];
1235
+ var renderTitle = () => {
1236
+ const nextTsGradient = gradient(colors);
1237
+ const pkgManager = getUserPkgManager();
1238
+ if (pkgManager === "yarn" || pkgManager === "pnpm") {
1239
+ console.log("");
1240
+ }
1241
+ console.log(nextTsGradient.multiline(TITLE_TEXT));
1242
+ };
1243
+
1244
+ // src/helpers/installDependencies.ts
1245
+ import chalk5 from "chalk";
1246
+ import { execa as execa2 } from "execa";
1247
+ import ora4 from "ora";
1248
+ var execWithSpinner = async (projectDir, pkgManager, options) => {
1249
+ const { onDataHandle, args = ["install"], stdout = "pipe" } = options;
1250
+ const spinner = ora4(`Running ${pkgManager} install...`).start();
1251
+ const subprocess = execa2(pkgManager, args, { cwd: projectDir, stdout });
1252
+ await new Promise((res, rej) => {
1253
+ if (onDataHandle) {
1254
+ subprocess.stdout?.on("data", onDataHandle(spinner));
1255
+ }
1256
+ void subprocess.on("error", (e) => rej(e));
1257
+ void subprocess.on("close", () => res());
1258
+ });
1259
+ return spinner;
1260
+ };
1261
+ var runInstallCommand = async (pkgManager, projectDir) => {
1262
+ switch (pkgManager) {
1263
+ // When using npm, inherit the stderr stream so that the progress bar is shown
1264
+ case "npm":
1265
+ await execa2(pkgManager, ["install"], {
1266
+ cwd: projectDir,
1267
+ stderr: "inherit"
1268
+ });
1269
+ return null;
1270
+ // When using yarn or pnpm, use the stdout stream and ora spinner to show the progress
1271
+ case "pnpm":
1272
+ return execWithSpinner(projectDir, pkgManager, {
1273
+ onDataHandle: (spinner) => (data) => {
1274
+ const text2 = data.toString();
1275
+ if (text2.includes("Progress")) {
1276
+ spinner.text = text2.includes("|") ? text2.split(" | ")[1] ?? "" : text2;
1277
+ }
1278
+ }
1279
+ });
1280
+ case "yarn":
1281
+ return execWithSpinner(projectDir, pkgManager, {
1282
+ onDataHandle: (spinner) => (data) => {
1283
+ spinner.text = data.toString();
1284
+ }
1285
+ });
1286
+ // When using bun, the stdout stream is ignored and the spinner is shown
1287
+ case "bun":
1288
+ return execWithSpinner(projectDir, pkgManager, { stdout: "ignore" });
1289
+ }
1290
+ };
1291
+ var installDependencies = async ({
1292
+ projectDir
1293
+ }) => {
1294
+ logger.info("Installing dependencies...");
1295
+ const pkgManager = getUserPkgManager();
1296
+ const installSpinner = await runInstallCommand(pkgManager, projectDir);
1297
+ (installSpinner ?? ora4()).succeed(
1298
+ chalk5.green("Successfully installed dependencies!\n")
1299
+ );
1300
+ };
1301
+
1302
+ // src/utils/renderVersionWarning.ts
1303
+ import { execSync as execSync2 } from "child_process";
1304
+ import https from "https";
1305
+ var renderVersionWarning = (npmVersion) => {
1306
+ const currentVersion = getVersion();
1307
+ if (currentVersion.includes("beta")) {
1308
+ logger.warn(" You are using a beta version of next-ts-cli.");
1309
+ logger.warn(" Please report any bugs you encounter.");
1310
+ } else if (currentVersion.includes("next")) {
1311
+ logger.warn(
1312
+ " You are running next-ts-cli with the @next tag which is no longer maintained."
1313
+ );
1314
+ logger.warn(" Please run the CLI with @latest instead.");
1315
+ } else if (currentVersion !== npmVersion) {
1316
+ logger.warn(" You are using an outdated version of next-ts-cli.");
1317
+ logger.warn(
1318
+ " Your version:",
1319
+ currentVersion + ".",
1320
+ "Latest version in the npm registry:",
1321
+ npmVersion
1322
+ );
1323
+ logger.warn(" Please run the CLI with @latest to get the latest updates.");
1324
+ }
1325
+ console.log("");
1326
+ };
1327
+ function checkForLatestVersion() {
1328
+ return new Promise((resolve, reject) => {
1329
+ https.get(
1330
+ "https://registry.npmjs.org/-/package/next-ts-cli/dist-tags",
1331
+ (res) => {
1332
+ if (res.statusCode === 200) {
1333
+ let body = "";
1334
+ res.on("data", (data) => {
1335
+ body += data;
1336
+ });
1337
+ res.on("end", () => {
1338
+ resolve(JSON.parse(body).latest);
1339
+ });
1340
+ } else {
1341
+ reject();
1342
+ }
1343
+ }
1344
+ ).on("error", () => {
1345
+ reject();
1346
+ });
1347
+ });
1348
+ }
1349
+ var getNpmVersion = () => (
1350
+ // `fetch` to the registry is faster than `npm view` so we try that first
1351
+ checkForLatestVersion().catch(() => {
1352
+ try {
1353
+ return execSync2("npm view next-ts-cli version").toString().trim();
1354
+ } catch {
1355
+ return null;
1356
+ }
1357
+ })
1358
+ );
1359
+
1360
+ // src/index.ts
1361
+ var main = async () => {
1362
+ const npmVersion = await getNpmVersion();
1363
+ const pkgManager = getUserPkgManager();
1364
+ renderTitle();
1365
+ if (npmVersion) {
1366
+ renderVersionWarning(npmVersion);
1367
+ }
1368
+ const {
1369
+ appName,
1370
+ packages,
1371
+ flags: { alias }
1372
+ } = await runCli();
1373
+ const usePackages = buildPkgInstallerMap(packages);
1374
+ const [scopedAppName, appDir] = parseNameAndPath(appName);
1375
+ const projectDir = await createProject({
1376
+ projectName: appDir,
1377
+ scopedAppName,
1378
+ packages: usePackages,
1379
+ alias
1380
+ });
1381
+ const pkgJson = fs19.readJSONSync(
1382
+ path21.join(projectDir, "package.json")
1383
+ );
1384
+ pkgJson.name = scopedAppName;
1385
+ pkgJson.nextTsMetadata = { initVersion: getVersion() };
1386
+ if (pkgManager !== "bun") {
1387
+ const { stdout } = await execa3(pkgManager, ["-v"], {
1388
+ cwd: projectDir
1389
+ });
1390
+ pkgJson.packageManager = `${pkgManager}@${stdout.trim()}`;
1391
+ }
1392
+ fs19.writeJSONSync(path21.join(projectDir, "package.json"), pkgJson, {
1393
+ spaces: 2
1394
+ });
1395
+ if (alias !== "@/") {
1396
+ setImportAlias(projectDir, alias);
1397
+ }
1398
+ await installDependencies({ projectDir });
1399
+ await initializeGit(projectDir);
1400
+ await logNextSteps({
1401
+ projectName: appDir,
1402
+ packages: usePackages,
1403
+ projectDir
1404
+ });
1405
+ process.exit(0);
1406
+ };
1407
+ main().catch((err) => {
1408
+ logger.error("Aborting installation...");
1409
+ if (err instanceof Error) {
1410
+ logger.error(err);
1411
+ } else {
1412
+ logger.error(
1413
+ "An unknown error has occurred. Please open an issue on github with the below:"
1414
+ );
1415
+ console.log(err);
1416
+ }
1417
+ process.exit(1);
1418
+ });