next-ts-cli 1.0.6 → 1.0.8

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.
Files changed (2) hide show
  1. package/dist/index.js +29 -1356
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,435 +1,20 @@
1
1
  #!/usr/bin/env node
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 = `
2
+ import z from"path";import{execa as At}from"execa";import Y from"fs-extra";import*as m from"@clack/prompts";import{Command as Fe}from"commander";import X from"path";import{fileURLToPath as Le}from"url";var Je=Le(import.meta.url),Ve=X.dirname(Je),c=X.join(Ve,"../"),q=`
19
3
  __ __ __ __
20
4
  .-----.-----.--.--| |_ ______| |_.-----.______.----| |__|
21
5
  | | -__|_ _| _|______| _|__ --|______| __| | |
22
6
  |__|__|_____|__.__|____| |____|_____| |____|__|__|
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,
7
+ `,R="next-ts-cli",L="next-ts-cli";import $e from"path";import Ke from"fs-extra";var _=()=>{let e=$e.join(c,"package.json");return Ke.readJSONSync(e).version??"1.0.0"};var E=class extends Error{constructor(t){super(t)}};import U from"chalk";var a={error(...e){console.log(U.red(...e))},warn(...e){console.log(U.yellow(...e))},info(...e){console.log(U.cyan(...e))},success(...e){console.log(U.green(...e))}};var M=e=>(e.length>1&&e.endsWith("/")&&(e=e.slice(0,-1)),e);var We=/^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/,Q=e=>{if(!e)return;let t=M(e),o=t.split("/"),n=o.findIndex(r=>r.startsWith("@")),s=o[o.length-1];if(o.findIndex(r=>r.startsWith("@"))!==-1&&(s=o.slice(n).join("/")),!(t==="."||We.test(s??"")))return"App name must consist of only lowercase alphanumeric characters, '-', and '_'"};var Z=e=>{if(e.startsWith(".")||e.startsWith("/"))return"Import alias can't start with '.' or '/'"};var C={appName:R,packages:[],flags:{default:!1,cli:!1,drizzle:!1,supabase:!1,betterauth:!1,alias:"@/",docker:!1,stripe:!1,shadcnui:!1,clerk:!1,neon:!1,vercelAi:!1}},ee=async()=>{let e=C,t=new Fe().name(L).description("A CLI for creating production-ready web applications with the Next.js").argument("[dir]","The name of the application, as well as the name of the directory to create").option("--cli","Run in CLI mode (non-interactive)",!1).option("-d, --default","Scaffold the project without any additional integrations",!1).option("-a, --alias [alias]","Explicitly tell the CLI to use a custom import alias",C.flags.alias).option("--drizzle [boolean]","Experimental: Boolean value if we should install Drizzle. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--docker [boolean]","Experimental: Boolean value if we should install Docker. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--stripe [boolean]","Experimental: Boolean value if we should install Stripe. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--shadcnui [boolean]","Experimental: Boolean value if we should install Shadcn UI. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--betterauth [boolean]","Experimental: Boolean value if we should install BetterAuth. Must be used in conjunction with `--cli`. Not valid with --clerk",n=>!!n&&n!=="false").option("--clerk [boolean]","Experimental: Boolean value if we should install Clerk.js . Must be used in conjunction with `--cli`. Not valid with --betterAuth",n=>!!n&&n!=="false").option("--supabase [boolean]","Experimental: Boolean value if we should install Supabase. Must be used in conjunction with `--cli`. Not valid with --betterAuth or --clerk",n=>!!n&&n!=="false").option("--neon [boolean]","Experimental: Boolean value if we should install Neon. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").option("--vercelAi [boolean]","Experimental: Boolean value if we should install Vercel AI Gateway. Must be used in conjunction with `--cli`.",n=>!!n&&n!=="false").version(_(),"-v, --version","Display the version number").parse(process.argv);process.env.npm_config_user_agent?.startsWith("yarn/3")&&a.warn(` WARNING: It looks like you are using Yarn 3. This is currently not supported,
169
8
  and likely to result in a crash. Please run next-ts-cli with another
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}
9
+ package manager such as pnpm, npm, or Yarn Classic.`);let o=t.args[0];if(o&&(e.appName=o),e.flags=t.opts(),e.flags.cli)return e.packages=[],e.flags.docker&&e.packages.push("docker"),e.flags.drizzle&&e.packages.push("drizzle"),e.flags.supabase&&e.packages.push("supabase"),e.flags.betterauth&&e.packages.push("betterAuth"),e.flags.clerk&&e.packages.push("clerk"),e.flags.vercelAi&&e.packages.push("vercelAi"),e.flags.supabase&&e.flags.betterauth&&(a.warn("Incompatible combination Supabase and BetterAuth. Exiting."),process.exit(0)),e.flags.clerk&&e.flags.betterauth&&(a.warn("Incompatible combination Clerk and BetterAuth. Exiting."),process.exit(0)),e.flags.supabase&&e.flags.clerk&&(a.warn("Incompatible combination Supabase and Clerk. Exiting."),process.exit(0)),e.flags.stripe&&e.packages.push("stripe"),e.flags.shadcnui&&e.packages.push("shadcnui"),e;if(e.flags.default)return e;try{if(process.env.TERM_PROGRAM?.toLowerCase().includes("mintty"))throw a.warn(` WARNING: It looks like you are using MinTTY, which is non-interactive. This is most likely because you are
10
+ using Git Bash. If that's that case, please use Git Bash from another terminal, such as Windows Terminal.`),new E("Non-interactive environment");let n=await m.group({...!o&&{name:()=>m.text({message:"Project name",defaultValue:o,placeholder:C.appName,validate:Q})},authentication:()=>m.select({message:"What authentication provider would you like to use?",options:[{value:"none",label:"None"},{value:"better-auth",label:"BetterAuth"},{value:"clerk",label:"Clerk"},{value:"supabase",label:"Supabase"}],initialValue:"none"}),database:({results:i})=>{if(i.authentication!=="supabase")return m.select({message:"What database would you like to use?",options:[{value:"none",label:"None"},{value:"neon",label:"Neon"}],initialValue:"none"})},orm:()=>m.select({message:"What ORM Database would you like to use?",options:[{value:"none",label:"None"},{value:"drizzle",label:"Drizzle"}],initialValue:"none"}),importAlias:()=>m.text({message:"Custom import aliases",defaultValue:C.flags.alias,placeholder:C.flags.alias,validate:Z}),additionalConfig:()=>m.multiselect({message:"Select additional tools to install (press enter to skip)",options:[{value:"docker",label:"Docker"},{value:"stripe",label:"Stripe"},{value:"shadcnui",label:"Shadcn UI"},{value:"vercelAi",label:"Vercel AI Gateway"}],required:!1})},{onCancel(){process.exit(1)}}),s=[],r=n.additionalConfig;return n.orm==="drizzle"&&s.push("drizzle"),n.authentication==="better-auth"&&s.push("betterAuth"),n.authentication==="clerk"&&s.push("clerk"),(r.includes("supabase")||n.authentication==="supabase")&&s.push("supabase"),n.database==="neon"&&s.push("neon"),r.includes("docker")&&s.push("docker"),r.includes("stripe")&&s.push("stripe"),r.includes("shadcnui")&&s.push("shadcnui"),r.includes("vercelAi")&&s.push("vercelAi"),{appName:n.name??e.appName,packages:s,flags:{...e.flags,alias:n.importAlias??e.flags.alias}}}catch(n){if(n instanceof E)a.warn(`
11
+ ${L} needs an interactive terminal to provide options`),await m.confirm({message:"Continue scaffolding a default next-ts-cli?",initialValue:!0})||(a.info("Exiting..."),process.exit(0)),a.info(`Bootstrapping a default next-ts-cli in ./${e.appName}`);else throw n}return e};import et from"path";import te from"chalk";import He from"ora";var ne=e=>{let{packages:t}=e;a.info("Adding boilerplate...");for(let[o,n]of Object.entries(t))if(n.inUse){let s=He(`Boilerplating ${o}...`).start();n.installer(e),s.succeed(te.green(`Successfully setup boilerplate for ${te.green.bold(o)}`))}a.info("")};import Ye from"path";import*as k from"@clack/prompts";import y from"chalk";import O from"fs-extra";import Xe from"ora";var oe=async({projectName:e,projectDir:t,pkgManager:o})=>{let n=Ye.join(c,"template/base");a.info(`
12
+ Using: ${y.cyan.bold(o)}
13
+ `);let s=Xe(`Scaffolding in: ${t}...
14
+ `).start();if(O.existsSync(t))if(O.readdirSync(t).length===0)e!=="."&&s.info(`${y.cyan.bold(e)} exists but is empty, continuing...
15
+ `);else{s.stopAndPersist();let i=await k.select({message:`${y.redBright.bold("Warning:")} ${y.cyan.bold(e)} already exists and isn't empty. How would you like to proceed?`,options:[{label:"Abort installation (recommended)",value:"abort"},{label:"Clear the directory and continue installation",value:"clear"},{label:"Continue installation and overwrite conflicting files",value:"overwrite"}],initialValue:"abort"});(k.isCancel(i)||i==="abort")&&(s.fail("Aborting installation..."),process.exit(1));let p=await k.confirm({message:`Are you sure you want to ${i==="clear"?"clear the directory":"overwrite conflicting files"}?`,initialValue:!1});(k.isCancel(p)||!p)&&(s.fail("Aborting installation..."),process.exit(1)),i==="clear"&&(s.info(`Emptying ${y.cyan.bold(e)} and creating next-ts-cli app..
16
+ `),O.emptyDirSync(t))}s.start(),O.copySync(n,t);let r=e==="."?"App":y.cyan.bold(e);s.succeed(`${r} ${y.green("scaffolded successfully!")}
17
+ `)};import qe from"path";import Qe from"fs-extra";var se=({projectDir:e,projectName:t,packages:o})=>{let n=Ze(t,o);Qe.writeFileSync(qe.join(e,"README.md"),n)},Ze=(e,t)=>{let o=[];o.push(`# ${e}
433
18
 
434
19
  This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/next-ts-cli).
435
20
 
@@ -441,23 +26,11 @@ This project was bootstrapped with [next-ts-cli](https://github.com/RossiFire/ne
441
26
  - **Commitlint** \u2014 Commit message linting
442
27
  - **Lint-staged** \u2014 Run linters on staged files
443
28
  - **Biome** \u2014 Linter and formatter
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(`
29
+ - **Jest** \u2014 Testing framework`);let n=[];t.clerk.inUse&&n.push("- **Clerk** \u2014 Fast authentication"),t.betterAuth.inUse&&n.push("- **BetterAuth** \u2014 Flexible authentication"),t.supabase.inUse&&n.push("- **Supabase** \u2014 Auth & database with real-time capabilities"),t.drizzle.inUse&&n.push("- **Drizzle ORM** \u2014 Type-safe database queries"),t.shadcnui.inUse&&n.push("- **Shadcn UI** \u2014 UI library"),t.stripe.inUse&&n.push("- **Stripe** \u2014 Payment checkout and webhooks boilerplate to easily create SaaS features"),t.docker.inUse&&n.push("- **Docker** \u2014 Containerization"),t.vercelAi.inUse&&n.push("- **Vercel AI Gateway** \u2014 AI capabilities witsh chat API route boilerplate"),n.length>0&&o.push(`
456
30
  ### Selected Integrations
457
31
 
458
- ${features.join("\n")}`);
459
- }
460
- sections.push(`
32
+ ${n.join(`
33
+ `)}`),o.push(`
461
34
  ## Getting Started
462
35
 
463
36
  \`\`\`bash
@@ -475,944 +48,44 @@ Open [http://localhost:3000](http://localhost:3000) in your browser.
475
48
  | \`npm run build\` | Build for production |
476
49
  | \`npm run start\` | Start production server |
477
50
  | \`npm run lint\` | Lint code |
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 = `
51
+ | \`npm run test\` | Run tests |`),t.docker.inUse&&o.push("| `npm run docker:build` | Build Docker image |\n| `npm run docker:up` | Start containers |\n| `npm run docker:down` | Stop containers |"),t.drizzle.inUse&&o.push("| `npm run db:generate` | Generate migrations |\n| `npm run db:migrate` | Run migrations |\n| `npm run db:studio` | Open Drizzle Studio |");let s=[];return t.drizzle.inUse&&s.push("DATABASE_URL="),t.betterAuth.inUse&&s.push("BETTER_AUTH_SECRET=","BETTER_AUTH_URL=http://localhost:3000"),t.supabase.inUse&&s.push("NEXT_PUBLIC_SUPABASE_URL=","NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY="),t.stripe.inUse&&s.push("STRIPE_SECRET_KEY=","STRIPE_WEBHOOK_SECRET=","NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY="),t.clerk.inUse&&s.push("NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=","CLERK_SECRET_KEY="),t.vercelAi.inUse&&s.push("AI_GATEWAY_API_KEY="),o.join(`
52
+ `)};var u=()=>{let e=process.env.npm_config_user_agent;return e?e.startsWith("yarn")?"yarn":e.startsWith("pnpm")?"pnpm":e.startsWith("bun")?"bun":"npm":"npm"};var re=async({projectName:e,scopedAppName:t,packages:o})=>{let n=u(),s=et.resolve(process.cwd(),e);return await oe({projectName:e,projectDir:s,pkgManager:n,scopedAppName:t}),ne({projectName:e,scopedAppName:t,projectDir:s,pkgManager:n,packages:o}),se({projectDir:s,projectName:e,packages:o}),s};import{execSync as $}from"child_process";import J from"path";import*as V from"@clack/prompts";import j from"chalk";import{execa as T}from"execa";import ie from"fs-extra";import tt from"ora";var nt=e=>{try{return $("git --version",{cwd:e}),!0}catch{return!1}},ot=e=>ie.existsSync(J.join(e,".git")),st=async e=>{try{return await T("git",["rev-parse","--is-inside-work-tree"],{cwd:e,stdout:"ignore"}),!0}catch{return!1}},rt=()=>{let t=$("git --version").toString().trim().split(" ")[2],o=t?.split(".")[0],n=t?.split(".")[1];return{major:Number(o),minor:Number(n)}},it=()=>$("git config --global init.defaultBranch || echo main").toString().trim(),ae=async e=>{if(a.info("Initializing Git..."),!nt(e)){a.warn("Git is not installed. Skipping Git initialization.");return}let t=tt(`Creating a new git repo...
53
+ `).start(),o=ot(e),n=await st(e),s=J.parse(e).name;if(n&&o){if(t.stop(),!await V.confirm({message:`${j.redBright.bold("Warning:")} Git is already initialized in "${s}". Initializing a new git repository would delete the previous history. Would you like to continue anyways?`,initialValue:!1})){t.info("Skipping Git initialization.");return}ie.removeSync(J.join(e,".git"))}else if(n&&!o&&(t.stop(),!await V.confirm({message:`${j.redBright.bold("Warning:")} "${s}" is already in a git worktree. Would you still like to initialize a new git repository in this directory?`,initialValue:!1}))){t.info("Skipping Git initialization.");return}try{let r=it(),{major:i,minor:p}=rt();i<2||i===2&&p<28?(await T("git",["init"],{cwd:e}),await T("git",["symbolic-ref","HEAD",`refs/heads/${r}`],{cwd:e})):await T("git",["init",`--initial-branch=${r}`],{cwd:e}),await T("git",["add","."],{cwd:e}),t.succeed(`${j.green("Successfully initialized and staged")} ${j.green.bold("git")}
54
+ `)}catch{t.fail(`${j.bold.red("Failed:")} could not initialize git. Update git to the latest version!
55
+ `)}};var le=async({projectName:e=R})=>{let t=u();a.info("Next steps:"),e!=="."&&a.info(` cd ${e}`),["npm","bun"].includes(t)?a.info(` ${t} run dev`):a.info(` ${t} dev`)};import N from"fs";import at from"path";function ce(e,t,o){let n=N.readdirSync(e);try{n.forEach(s=>{let r=at.join(e,s);if(N.statSync(r).isDirectory())ce(r,t,o);else{let p=N.readFileSync(r,"utf8").replace(new RegExp(t,"g"),o);N.writeFileSync(r,p,"utf8")}})}catch(s){a.error(`Error setting import alias: ${s}`)}}var pe=(e,t)=>{try{let o=t.replace(/\*/g,"").replace(/[^/]$/,"$&/");ce(e,"@/",o)}catch(o){a.error(`Error setting import alias: ${o}`)}};import lt from"crypto";import ct from"path";import pt from"fs-extra";var de=({projectDir:e,packages:t,scopedAppName:o})=>{let n=t?.betterAuth.inUse,s=t?.supabase.inUse,r=t?.drizzle.inUse,i=t?.stripe.inUse,p=t?.clerk.inUse,l=t?.neon.inUse,f=t?.vercelAi.inUse,b=dt(!!n,!!s,!!r,!!i,!!p,!!l,!!f,o),G=ct.join(e,".env");pt.writeFileSync(G,b,"utf-8")},dt=(e,t,o,n,s,r,i,p)=>{let l=`
737
56
  # Base variables
738
57
  NEXT_PUBLIC_BASE_URL="http://localhost:3000"
739
58
  GOOGLE_ANALYTICS_TAG="G-xxxxxxx" # Google Analytics G-Tag ID
740
- `;
741
- if (usingBetterAuth) {
742
- const secret = Buffer.from(
743
- crypto.getRandomValues(new Uint8Array(32))
744
- ).toString("base64");
745
- content += `
59
+ `;if(e){let f=Buffer.from(lt.getRandomValues(new Uint8Array(32))).toString("base64");l+=`
746
60
  # Better Auth
747
- BETTER_AUTH_SECRET="${secret}" # Generated by next-ts-cli.
61
+ BETTER_AUTH_SECRET="${f}" # Generated by next-ts-cli.
748
62
 
749
63
  # Better Auth GitHub OAuth
750
64
  BETTER_AUTH_GITHUB_CLIENT_ID=""
751
65
  BETTER_AUTH_GITHUB_CLIENT_SECRET=""
752
66
 
753
- `;
754
- }
755
- if (usingSupabase)
756
- content += `
67
+ `}return t&&(l+=`
757
68
  # Supabase
758
69
  # https://supabase.com/docs/guides/functions/secrets
759
70
  NEXT_PUBLIC_SUPABASE_URL="https://<app-id>.supabase.co",
760
71
  NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=""
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 += `
72
+ `),o&&(l+="# Drizzle"),r&&(l+=" + Neon"),l+=`
73
+ `,(o||r)&&(l+=`DATABASE_URL="postgresql://postgres:password@localhost:5432/${p}"
74
+ `),n&&(l+=`
771
75
  # https://dashboard.stripe.com/apikeys
772
76
 
773
77
  NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=""
774
78
  STRIPE_SECRET_KEY=""
775
79
  STRIPE_WEBHOOK_SECRET=""
776
- `;
777
- }
778
- if (usingClerk) {
779
- content += `
80
+ `),s&&(l+=`
780
81
  # Clerk
781
82
  # https://clerk.com/docs/nextjs/getting-started/quickstart
782
83
  NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY="pk_test_..."
783
84
  CLERK_SECRET_KEY="sk_test_..."
784
85
 
785
- `;
786
- }
787
- if (usingVercelAi) {
788
- content += `
86
+ `),i&&(l+=`
789
87
  # Vercel AI Gateway
790
88
  # https://ai-sdk.dev/docs/getting-started/nextjs-app-router
791
89
  AI_GATEWAY_API_KEY="sk_..."
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
- });
90
+ `),l};import S from"path";import ge from"fs-extra";import fe from"path";import ue from"fs-extra";import mt from"sort-package-json";var me={"better-auth":"^1.4.10","drizzle-kit":"^0.31.5","drizzle-orm":"^0.41.0",postgres:"^3.4.7",tailwindcss:"^4.0.15",postcss:"^8.5.3","@tailwindcss/postcss":"^4.0.15","@biomejs/biome":"^2.3.10","@supabase/ssr":"^0.8.0","@supabase/supabase-js":"^2.89.0","@stripe/react-stripe-js":"^5.4.1","@stripe/stripe-js":"^8.6.0",stripe:"^20.1.0","@clerk/nextjs":"^6.36.5","@neondatabase/serverless":"^1.0.2",ai:"^6.0.6","@ai-sdk/react":"^3.0.6"};var d=e=>{let{dependencies:t,devMode:o,projectDir:n}=e,s=ue.readJSONSync(fe.join(n,"package.json"));t.forEach(i=>{let p=me[i];o&&s.devDependencies?s.devDependencies[i]=p:s.dependencies&&(s.dependencies[i]=p)});let r=mt(s);ue.writeJSONSync(fe.join(n,"package.json"),r,{spaces:2})};var he=({projectDir:e,packages:t})=>{let o=t?.drizzle.inUse;d({projectDir:e,dependencies:["better-auth"],devMode:!1});let s=S.join(c,"template/extras"),r=S.join(s,"better-auth"),i="api/auth/[...all]/route.ts",p=S.join(r,i),l=S.join(e,"app",i);ge.copySync(p,l);let f=S.join(r,o?"with-drizzle-auth.ts":"base-auth.ts"),b=S.join(e,"lib","auth.ts");ge.copySync(f,b)};import v from"path";import ye from"fs-extra";import ft from"path";import be from"fs-extra";import ut from"sort-package-json";var x=e=>{let{scripts:t,projectDir:o}=e,n=ft.join(o,"package.json"),s=be.readJSONSync(n);s.scripts={...s.scripts,...t};let r=ut(s);be.writeJSONSync(n,r,{spaces:2})};var ke=({projectDir:e})=>{d({projectDir:e,dependencies:["drizzle-kit"],devMode:!0}),d({projectDir:e,dependencies:["drizzle-orm","postgres"],devMode:!1});let t=v.join(c,"template/extras"),o=v.join(t,"drizzle"),n=v.join(o,"drizzle.config.ts"),s=v.join(e,"drizzle.config.ts");ye.copySync(n,s);let r=v.join(o,"db"),i=v.join(e,"lib/db");ye.copySync(r,i),x({projectDir:e,scripts:{"db:push":"drizzle-kit push","db:studio":"drizzle-kit studio","db:generate":"drizzle-kit generate","db:migrate":"drizzle-kit migrate"}})};import g from"path";import K from"fs-extra";var _e=({projectDir:e})=>{let t=g.join(c,"template/extras"),o=g.join(t,"docker");K.copySync(g.join(o,"Dockerfile"),g.join(e,"Dockerfile")),K.copySync(g.join(o,"docker-compose.prod.yml"),g.join(e,"docker-compose.prod.yml")),K.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 W from"path";import bt from"fs-extra";import B from"path";import h from"fs-extra";var w=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e;gt({serverName:t,serverConfig:o,projectDir:n}),ht({serverName:t,serverConfig:o,projectDir:n})},gt=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e,s=B.join(n,".cusror/mpc.json");h.ensureDirSync(B.dirname(s));let r={args:o.args,command:o.command,env:o.env,url:o.url},i={};h.existsSync(s)&&(i=h.readJSONSync(s)),i.mcpServers||(i.mcpServers={}),i.mcpServers[t]={...r},h.writeJSONSync(s,i,{spaces:" "})},ht=e=>{let{serverName:t,serverConfig:o,projectDir:n}=e,s=B.join(n,".vscode/mcp.json");h.ensureDirSync(B.dirname(s));let r={};h.existsSync(s)&&(r=h.readJSONSync(s)),r.servers||(r.servers={}),r.servers[t]={...o},h.writeJSONSync(s,r,{spaces:" "})};var Se=({projectDir:e})=>{d({projectDir:e,dependencies:["@supabase/ssr","@supabase/supabase-js"],devMode:!1});let t=W.join(c,"template/extras"),o=W.join(t,"supabase"),n=W.join(e,"lib","supabase");bt.copySync(o,n),w({serverName:"supabase",serverConfig:{type:"http",url:"https://mcp.supabase.com/mcp?project_ref=<YOUR_SUPABASE_PROJECT_ID>"},projectDir:e})};import P from"path";import xe from"fs-extra";var ve=({projectDir:e})=>{d({projectDir:e,dependencies:["stripe","@stripe/stripe-js","@stripe/react-stripe-js"],devMode:!1});let t=P.join(c,"template/extras"),o=P.join(t,"stripe"),n=P.join(o,"checkout_session"),s=P.join(e,"app/api/checkout_session");xe.copySync(n,s);let r=P.join(o,"webhook/stripe/route.ts"),i=P.join(e,"app/api/webhook/stripe/route.ts");xe.copySync(r,i),x({projectDir:e,scripts:{"webhook:local":"stripe listen --forward-to localhost:3000/api/webhook/stripe"}}),w({serverName:"stripe",serverConfig:{type:"http",url:"https://mcp.stripe.com"},projectDir:e})};import I from"path";import F from"fs-extra";var we=({projectDir:e})=>{let t=I.join(c,"template/extras"),o=I.join(t,"shadcnui"),n=I.join(o,"components.json"),s=I.join(e,"components.json");F.copySync(n,s);let r=I.join(o,"globals.css"),i=I.join(e,"app/globals.css");F.writeFileSync(i,F.readFileSync(r,"utf8")),w({serverName:"shadcn",serverConfig:{command:"npx",args:["shadcn@latest","mcp"]},projectDir:e})};import A from"path";import Pe from"fs-extra";var Ie=({projectDir:e})=>{d({projectDir:e,dependencies:["@clerk/nextjs"],devMode:!1});let t=A.join(c,"template/extras"),o=A.join(t,"clerk"),n=A.join(o,"proxy.ts"),s=A.join(e,"proxy.ts");Pe.copySync(n,s);let r=A.join(o,"layout.tsx"),i=A.join(e,"app/layout.tsx");Pe.copySync(r,i)};import Ae from"path";import yt from"fs-extra";var Ee=({projectDir:e,packages:t})=>{if(d({projectDir:e,dependencies:["@neondatabase/serverless"],devMode:!1}),t?.drizzle.inUse){let o=Ae.join(c,"template/extras/neon/index.ts"),n=Ae.join(e,"lib/db/index.ts");yt.copySync(o,n)}};import D from"path";import kt from"fs-extra";var Ce=({projectDir:e})=>{d({projectDir:e,dependencies:["ai","@ai-sdk/react"],devMode:!1});let t=D.join(c,"template/extras"),o=D.join(t,"vercel-ai"),n=D.join(o,"route.ts"),s=D.join(e,"app/api/chat/route.ts");kt.copySync(n,s)};var je=e=>({docker:{inUse:e.includes("docker"),installer:_e},betterAuth:{inUse:e.includes("betterAuth"),installer:he},drizzle:{inUse:e.includes("drizzle"),installer:ke},envVariables:{inUse:!0,installer:de},supabase:{inUse:e.includes("supabase"),installer:Se},stripe:{inUse:e.includes("stripe"),installer:ve},shadcnui:{inUse:e.includes("shadcnui"),installer:we},clerk:{inUse:e.includes("clerk"),installer:Ie},neon:{inUse:e.includes("neon"),installer:Ee},vercelAi:{inUse:e.includes("vercelAi"),installer:Ce}});import Te from"path";var ze=e=>{let o=M(e).split("/"),n=o[o.length-1];if(n==="."){let i=Te.resolve(process.cwd());n=Te.basename(i)}let s=o.findIndex(i=>i.startsWith("@"));o.findIndex(i=>i.startsWith("@"))!==-1&&(n=o.slice(s).join("/"));let r=o.filter(i=>!i.startsWith("@")).join("/");return[n,r]};import _t from"gradient-string";var St=["#003f5b","#5f5195","#cc4c91","#ff6f4e","#ff7030","#f10062","#b1008f","#2c19a8"],Re=()=>{let e=_t(St),t=u();(t==="yarn"||t==="pnpm")&&console.log(""),console.log(e.multiline(q))};import xt from"chalk";import{execa as Ue}from"execa";import Me from"ora";var H=async(e,t,o)=>{let{onDataHandle:n,args:s=["install"],stdout:r="pipe"}=o,i=Me(`Running ${t} install...`).start(),p=Ue(t,s,{cwd:e,stdout:r});return await new Promise((l,f)=>{n&&p.stdout?.on("data",n(i)),p.on("error",b=>f(b)),p.on("close",()=>l())}),i},vt=async(e,t)=>{switch(e){case"npm":return await Ue(e,["install"],{cwd:t,stderr:"inherit"}),null;case"pnpm":return H(t,e,{onDataHandle:o=>n=>{let s=n.toString();s.includes("Progress")&&(o.text=s.includes("|")?s.split(" | ")[1]??"":s)}});case"yarn":return H(t,e,{onDataHandle:o=>n=>{o.text=n.toString()}});case"bun":return H(t,e,{stdout:"ignore"})}},Oe=async({projectDir:e})=>{a.info("Installing dependencies...");let t=u();(await vt(t,e)??Me()).succeed(xt.green(`Successfully installed dependencies!
91
+ `))};import{execSync as wt}from"child_process";import Pt from"https";var Ne=e=>{let t=_();t.includes("beta")?(a.warn(" You are using a beta version of next-ts-cli."),a.warn(" Please report any bugs you encounter.")):t.includes("next")?(a.warn(" You are running next-ts-cli with the @next tag which is no longer maintained."),a.warn(" Please run the CLI with @latest instead.")):t!==e&&(a.warn(" You are using an outdated version of next-ts-cli."),a.warn(" Your version:",t+".","Latest version in the npm registry:",e),a.warn(" Please run the CLI with @latest to get the latest updates.")),console.log("")};function It(){return new Promise((e,t)=>{Pt.get("https://registry.npmjs.org/-/package/next-ts-cli/dist-tags",o=>{if(o.statusCode===200){let n="";o.on("data",s=>{n+=s}),o.on("end",()=>{e(JSON.parse(n).latest)})}else t()}).on("error",()=>{t()})})}var Be=()=>It().catch(()=>{try{return wt("npm view next-ts-cli version").toString().trim()}catch{return null}});var Et=async()=>{let e=await Be(),t=u();Re(),e&&Ne(e);let{appName:o,packages:n,flags:{alias:s}}=await ee(),r=je(n),[i,p]=ze(o),l=await re({projectName:p,scopedAppName:i,packages:r,alias:s}),f=Y.readJSONSync(z.join(l,"package.json"));if(f.name=i,f.nextTsMetadata={initVersion:_()},t!=="bun"){let{stdout:Ge}=await At(t,["-v"],{cwd:l});f.packageManager=`${t}@${Ge.trim()}`}Y.writeJSONSync(z.join(l,"package.json"),f,{spaces:2}),s!=="@/"&&pe(l,s),await Oe({projectDir:l}),await ae(l),await le({projectName:p});let b=z.join(c,"template/extras"),G=z.join(b,".gitignore"),De=z.join(l,".gitignore");Y.copySync(G,De),process.exit(0)};Et().catch(e=>{a.error("Aborting installation..."),e instanceof Error?a.error(e):(a.error("An unknown error has occurred. Please open an issue on github with the below:"),console.log(e)),process.exit(1)});