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.
- package/dist/index.js +29 -1356
- 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
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
${
|
|
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
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
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="${
|
|
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
|
-
|
|
763
|
-
|
|
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)});
|