create-prisma 0.1.3 → 0.2.0

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 (94) hide show
  1. package/README.md +55 -16
  2. package/dist/cli.mjs +1 -1
  3. package/dist/create-DgN5mAMV.mjs +1677 -0
  4. package/dist/index.d.mts +31 -57
  5. package/dist/index.mjs +10 -21
  6. package/package.json +2 -2
  7. package/templates/create/astro/.yarnrc.yml.hbs +3 -0
  8. package/templates/create/astro/README.md.hbs +35 -0
  9. package/templates/create/astro/astro.config.mjs +5 -0
  10. package/templates/create/astro/deno.json.hbs +5 -0
  11. package/templates/create/astro/package.json.hbs +22 -0
  12. package/templates/{init → create/astro}/prisma/schema.prisma.hbs +5 -2
  13. package/templates/create/astro/prisma/seed.ts.hbs +38 -0
  14. package/templates/{init/prisma.config.ts.hbs → create/astro/prisma.config.ts} +1 -0
  15. package/templates/create/astro/public/favicon.svg +14 -0
  16. package/templates/create/astro/src/env.d.ts +9 -0
  17. package/templates/create/astro/src/lib/prisma.ts.hbs +53 -0
  18. package/templates/create/astro/src/pages/api/users.ts.hbs +42 -0
  19. package/templates/create/astro/src/pages/index.astro.hbs +236 -0
  20. package/templates/create/astro/tsconfig.json +5 -0
  21. package/templates/create/hono/.yarnrc.yml.hbs +3 -0
  22. package/templates/create/hono/README.md.hbs +14 -11
  23. package/templates/create/hono/deno.json.hbs +5 -0
  24. package/templates/create/hono/package.json.hbs +4 -1
  25. package/templates/create/hono/prisma/schema.prisma.hbs +21 -0
  26. package/templates/create/hono/prisma/seed.ts.hbs +38 -0
  27. package/templates/create/hono/prisma.config.ts +13 -0
  28. package/templates/create/hono/src/index.ts.hbs +3 -3
  29. package/templates/create/hono/src/lib/prisma.ts.hbs +53 -0
  30. package/templates/create/hono/tsconfig.json +1 -2
  31. package/templates/create/next/.yarnrc.yml.hbs +3 -0
  32. package/templates/create/next/README.md.hbs +9 -6
  33. package/templates/create/next/deno.json.hbs +12 -0
  34. package/templates/create/next/package.json.hbs +4 -0
  35. package/templates/create/next/prisma/schema.prisma.hbs +21 -0
  36. package/templates/create/next/prisma/seed.ts.hbs +38 -0
  37. package/templates/create/next/prisma.config.ts +13 -0
  38. package/templates/create/next/src/app/globals.css +136 -0
  39. package/templates/create/next/src/app/page.tsx.hbs +104 -0
  40. package/templates/create/next/src/lib/prisma.ts.hbs +53 -0
  41. package/templates/create/nuxt/.yarnrc.yml.hbs +3 -0
  42. package/templates/create/nuxt/README.md.hbs +32 -0
  43. package/templates/create/nuxt/app/app.vue +4 -0
  44. package/templates/create/nuxt/app/pages/index.vue.hbs +230 -0
  45. package/templates/create/nuxt/deno.json.hbs +5 -0
  46. package/templates/create/nuxt/nuxt.config.ts +5 -0
  47. package/templates/create/nuxt/package.json.hbs +27 -0
  48. package/templates/create/nuxt/prisma/schema.prisma.hbs +21 -0
  49. package/templates/create/nuxt/prisma/seed.ts.hbs +38 -0
  50. package/templates/create/nuxt/prisma.config.ts +13 -0
  51. package/templates/create/nuxt/public/robots.txt +2 -0
  52. package/templates/create/nuxt/server/api/users.get.ts.hbs +36 -0
  53. package/templates/create/nuxt/server/utils/prisma.ts.hbs +53 -0
  54. package/templates/create/nuxt/tsconfig.json +17 -0
  55. package/templates/create/svelte/.vscode/extensions.json +3 -0
  56. package/templates/create/svelte/.yarnrc.yml.hbs +3 -0
  57. package/templates/create/svelte/README.md.hbs +34 -0
  58. package/templates/create/svelte/deno.json.hbs +5 -0
  59. package/templates/create/svelte/package.json.hbs +28 -0
  60. package/templates/create/svelte/prisma/schema.prisma.hbs +21 -0
  61. package/templates/create/svelte/prisma/seed.ts.hbs +87 -0
  62. package/templates/create/svelte/prisma.config.ts +13 -0
  63. package/templates/create/svelte/src/app.d.ts +13 -0
  64. package/templates/create/svelte/src/app.html +11 -0
  65. package/templates/create/svelte/src/lib/assets/favicon.svg +1 -0
  66. package/templates/create/svelte/src/lib/index.ts +1 -0
  67. package/templates/create/svelte/src/lib/server/prisma.ts.hbs +53 -0
  68. package/templates/create/svelte/src/routes/+layout.svelte +11 -0
  69. package/templates/create/svelte/src/routes/+page.server.ts.hbs +28 -0
  70. package/templates/create/svelte/src/routes/+page.svelte.hbs +350 -0
  71. package/templates/create/svelte/static/robots.txt +3 -0
  72. package/templates/create/svelte/svelte.config.js +13 -0
  73. package/templates/create/svelte/tsconfig.json +20 -0
  74. package/templates/create/svelte/vite.config.ts +6 -0
  75. package/templates/create/turborepo/.yarnrc.yml.hbs +3 -0
  76. package/templates/create/turborepo/README.md.hbs +29 -0
  77. package/templates/create/turborepo/apps/api/package.json.hbs +21 -0
  78. package/templates/create/turborepo/apps/api/src/index.ts.hbs +45 -0
  79. package/templates/create/turborepo/apps/api/tsconfig.json +17 -0
  80. package/templates/create/turborepo/deno.json.hbs +5 -0
  81. package/templates/create/turborepo/package.json.hbs +24 -0
  82. package/templates/create/turborepo/packages/db/package.json.hbs +17 -0
  83. package/templates/create/turborepo/packages/db/prisma/schema.prisma.hbs +21 -0
  84. package/templates/create/turborepo/packages/db/prisma/seed.ts.hbs +38 -0
  85. package/templates/create/turborepo/packages/db/prisma.config.ts +13 -0
  86. package/templates/create/turborepo/packages/db/src/client.ts.hbs +58 -0
  87. package/templates/create/turborepo/packages/db/src/index.ts +2 -0
  88. package/templates/create/turborepo/packages/db/tsconfig.json +19 -0
  89. package/templates/create/turborepo/turbo.json +34 -0
  90. package/dist/create-fJECj1B0.mjs +0 -1026
  91. package/templates/create/next/app/globals.css +0 -47
  92. package/templates/create/next/app/page.tsx.hbs +0 -40
  93. package/templates/init/prisma/index.ts.hbs +0 -44
  94. /package/templates/create/next/{app → src/app}/layout.tsx.hbs +0 -0
@@ -0,0 +1,1677 @@
1
+ #!/usr/bin/env node
2
+ import { cancel, confirm, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
3
+ import fs from "fs-extra";
4
+ import path from "node:path";
5
+ import Handlebars from "handlebars";
6
+ import { existsSync } from "node:fs";
7
+ import { fileURLToPath } from "node:url";
8
+ import { z } from "zod";
9
+ import { execa } from "execa";
10
+ import { styleText } from "node:util";
11
+
12
+ //#region src/constants/dependencies.ts
13
+ const dependencyVersionMap = {
14
+ "@prisma/client": "^7.4.0",
15
+ "@prisma/adapter-pg": "^7.4.0",
16
+ "@prisma/adapter-mariadb": "^7.4.0",
17
+ "@prisma/adapter-better-sqlite3": "^7.4.0",
18
+ "@prisma/adapter-mssql": "^7.4.0",
19
+ dotenv: "^17.2.3",
20
+ "node-gyp": "^11.5.0",
21
+ prisma: "^7.4.0"
22
+ };
23
+
24
+ //#endregion
25
+ //#region src/types.ts
26
+ const databaseProviders = [
27
+ "postgresql",
28
+ "mysql",
29
+ "sqlite",
30
+ "sqlserver",
31
+ "cockroachdb"
32
+ ];
33
+ const packageManagers = [
34
+ "npm",
35
+ "pnpm",
36
+ "yarn",
37
+ "bun",
38
+ "deno"
39
+ ];
40
+ const schemaPresets = ["empty", "basic"];
41
+ const createTemplates = [
42
+ "hono",
43
+ "next",
44
+ "svelte",
45
+ "astro",
46
+ "nuxt",
47
+ "turborepo"
48
+ ];
49
+ const createAddons = [
50
+ "skills",
51
+ "mcp",
52
+ "extension"
53
+ ];
54
+ const addonInstallScopes = ["project", "global"];
55
+ const extensionTargets = [
56
+ "vscode",
57
+ "cursor",
58
+ "windsurf"
59
+ ];
60
+ const prismaSkillNames = [
61
+ "prisma-cli",
62
+ "prisma-client-api",
63
+ "prisma-database-setup",
64
+ "prisma-upgrade-v7",
65
+ "prisma-postgres"
66
+ ];
67
+ const DatabaseProviderSchema = z.enum(databaseProviders);
68
+ const PackageManagerSchema = z.enum(packageManagers);
69
+ const SchemaPresetSchema = z.enum(schemaPresets);
70
+ const CreateTemplateSchema = z.enum(createTemplates);
71
+ const CreateAddonSchema = z.enum(createAddons);
72
+ const AddonInstallScopeSchema = z.enum(addonInstallScopes);
73
+ const ExtensionTargetSchema = z.enum(extensionTargets);
74
+ const PrismaSkillNameSchema = z.enum(prismaSkillNames);
75
+ const DatabaseUrlSchema = z.string().trim().min(1, "Please enter a valid database URL");
76
+ const CommonCommandOptionsSchema = z.object({
77
+ yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
78
+ verbose: z.boolean().optional().describe("Show verbose command output during setup")
79
+ });
80
+ const PrismaSetupOptionsSchema = z.object({
81
+ provider: DatabaseProviderSchema.optional().describe("Database provider"),
82
+ packageManager: PackageManagerSchema.optional().describe("Package manager used for dependency installation"),
83
+ prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when provider is postgresql"),
84
+ databaseUrl: DatabaseUrlSchema.optional().describe("DATABASE_URL value"),
85
+ install: z.boolean().optional().describe("Install dependencies with selected package manager"),
86
+ generate: z.boolean().optional().describe("Generate Prisma Client after scaffolding"),
87
+ schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/schema.prisma")
88
+ });
89
+ const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
90
+ const CreateScaffoldOptionsSchema = z.object({
91
+ name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
92
+ template: CreateTemplateSchema.optional().describe("Project template"),
93
+ skills: z.boolean().optional().describe("Enable skills addon"),
94
+ mcp: z.boolean().optional().describe("Enable MCP addon"),
95
+ extension: z.boolean().optional().describe("Enable extension addon"),
96
+ force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
97
+ });
98
+ const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
99
+
100
+ //#endregion
101
+ //#region src/utils/package-manager.ts
102
+ const packageManagerManifestValues = {
103
+ npm: "npm@10.9.0",
104
+ pnpm: "pnpm@10.16.1",
105
+ yarn: "yarn@4.13.0",
106
+ bun: "bun@1.3.9"
107
+ };
108
+ function parseUserAgent(userAgent) {
109
+ if (userAgent?.startsWith("pnpm")) return "pnpm";
110
+ if (userAgent?.startsWith("yarn")) return "yarn";
111
+ if (userAgent?.startsWith("bun")) return "bun";
112
+ if (userAgent?.startsWith("deno")) return "deno";
113
+ if (userAgent?.startsWith("npm")) return "npm";
114
+ return null;
115
+ }
116
+ function parsePackageManagerField(packageManagerField) {
117
+ if (typeof packageManagerField !== "string" || packageManagerField.length === 0) return null;
118
+ const managerName = packageManagerField.split("@")[0];
119
+ const parsed = PackageManagerSchema.safeParse(managerName);
120
+ return parsed.success ? parsed.data : null;
121
+ }
122
+ async function detectFromPackageJson(projectDir) {
123
+ const packageJsonPath = path.join(projectDir, "package.json");
124
+ if (!await fs.pathExists(packageJsonPath)) return null;
125
+ return parsePackageManagerField((await fs.readJson(packageJsonPath)).packageManager);
126
+ }
127
+ async function detectFromDenoConfig(projectDir) {
128
+ for (const configFile of ["deno.json", "deno.jsonc"]) if (await fs.pathExists(path.join(projectDir, configFile))) return "deno";
129
+ return null;
130
+ }
131
+ async function detectFromLockfile(projectDir) {
132
+ for (const check of [
133
+ {
134
+ manager: "pnpm",
135
+ lockfile: "pnpm-lock.yaml"
136
+ },
137
+ {
138
+ manager: "yarn",
139
+ lockfile: "yarn.lock"
140
+ },
141
+ {
142
+ manager: "bun",
143
+ lockfile: "bun.lockb"
144
+ },
145
+ {
146
+ manager: "bun",
147
+ lockfile: "bun.lock"
148
+ },
149
+ {
150
+ manager: "npm",
151
+ lockfile: "package-lock.json"
152
+ },
153
+ {
154
+ manager: "npm",
155
+ lockfile: "npm-shrinkwrap.json"
156
+ },
157
+ {
158
+ manager: "deno",
159
+ lockfile: "deno.lock"
160
+ }
161
+ ]) if (await fs.pathExists(path.join(projectDir, check.lockfile))) return check.manager;
162
+ return null;
163
+ }
164
+ async function detectPackageManager(projectDir = process.cwd()) {
165
+ const fromPackageJson = await detectFromPackageJson(projectDir);
166
+ if (fromPackageJson) return fromPackageJson;
167
+ const fromLockfile = await detectFromLockfile(projectDir);
168
+ if (fromLockfile) return fromLockfile;
169
+ const fromDenoConfig = await detectFromDenoConfig(projectDir);
170
+ if (fromDenoConfig) return fromDenoConfig;
171
+ const fromUserAgent = parseUserAgent(process.env.npm_config_user_agent);
172
+ if (fromUserAgent) return fromUserAgent;
173
+ return "npm";
174
+ }
175
+ function getPackageManagerManifestValue(packageManager) {
176
+ if (!packageManager || packageManager === "deno") return;
177
+ return packageManagerManifestValues[packageManager];
178
+ }
179
+ function getDenoPrismaSpecifier() {
180
+ return `npm:prisma@${dependencyVersionMap.prisma.replace(/^[^0-9]*/, "")}`;
181
+ }
182
+ function getInstallCommand(packageManager) {
183
+ if (packageManager === "deno") return "deno install --allow-scripts";
184
+ return `${packageManager} install`;
185
+ }
186
+ function getRunScriptCommand(packageManager, scriptName) {
187
+ switch (packageManager) {
188
+ case "deno": return `deno task ${scriptName}`;
189
+ case "bun": return `bun run ${scriptName}`;
190
+ case "pnpm": return `pnpm run ${scriptName}`;
191
+ case "yarn": return `yarn run ${scriptName}`;
192
+ default: return `npm run ${scriptName}`;
193
+ }
194
+ }
195
+ function getInstallArgs(packageManager) {
196
+ if (packageManager === "deno") return {
197
+ command: "deno",
198
+ args: ["install", "--allow-scripts"]
199
+ };
200
+ return {
201
+ command: packageManager,
202
+ args: ["install"]
203
+ };
204
+ }
205
+ function getPackageExecutionArgs(packageManager, commandArgs) {
206
+ switch (packageManager) {
207
+ case "pnpm": return {
208
+ command: "pnpm",
209
+ args: ["dlx", ...commandArgs]
210
+ };
211
+ case "yarn": return {
212
+ command: "yarn",
213
+ args: ["dlx", ...commandArgs]
214
+ };
215
+ case "bun": return {
216
+ command: "bunx",
217
+ args: [...commandArgs]
218
+ };
219
+ case "deno": {
220
+ const [packageName, ...args] = commandArgs;
221
+ if (!packageName) throw new Error("Package execution requires a package name.");
222
+ return {
223
+ command: "deno",
224
+ args: [
225
+ "run",
226
+ "-A",
227
+ `npm:${packageName}`,
228
+ ...args
229
+ ]
230
+ };
231
+ }
232
+ default: return {
233
+ command: "npx",
234
+ args: [...commandArgs]
235
+ };
236
+ }
237
+ }
238
+ function getPackageExecutionCommand(packageManager, commandArgs) {
239
+ const execution = getPackageExecutionArgs(packageManager, commandArgs);
240
+ return [execution.command, ...execution.args].join(" ");
241
+ }
242
+ function getPrismaCliArgs(packageManager, prismaArgs) {
243
+ if (packageManager === "bun") return getPackageExecutionArgs(packageManager, [
244
+ "--bun",
245
+ "prisma",
246
+ ...prismaArgs
247
+ ]);
248
+ if (packageManager === "deno") return {
249
+ command: "deno",
250
+ args: [
251
+ "run",
252
+ "-A",
253
+ getDenoPrismaSpecifier(),
254
+ ...prismaArgs
255
+ ]
256
+ };
257
+ return getPackageExecutionArgs(packageManager, ["prisma", ...prismaArgs]);
258
+ }
259
+ function getPrismaCliCommand(packageManager, prismaArgs) {
260
+ const execution = getPrismaCliArgs(packageManager, prismaArgs);
261
+ return [execution.command, ...execution.args].join(" ");
262
+ }
263
+
264
+ //#endregion
265
+ //#region src/templates/shared.ts
266
+ Handlebars.registerHelper("eq", (left, right) => left === right);
267
+ Handlebars.registerHelper("installCommand", (packageManager) => packageManager ? getInstallCommand(packageManager) : "");
268
+ Handlebars.registerHelper("runScriptCommand", (packageManager, scriptName) => packageManager ? getRunScriptCommand(packageManager, scriptName) : "");
269
+ Handlebars.registerHelper("packageManagerManifestValue", (packageManager) => getPackageManagerManifestValue(packageManager) ?? "");
270
+ function findPackageRoot(startDir) {
271
+ let currentDir = startDir;
272
+ while (true) {
273
+ if (existsSync(path.join(currentDir, "package.json"))) return currentDir;
274
+ const parentDir = path.dirname(currentDir);
275
+ if (parentDir === currentDir) break;
276
+ currentDir = parentDir;
277
+ }
278
+ throw new Error(`Unable to locate package root from: ${startDir}`);
279
+ }
280
+ function resolveTemplatesDir(relativeTemplatesDir) {
281
+ const currentFilePath = fileURLToPath(import.meta.url);
282
+ const packageRoot = findPackageRoot(path.dirname(currentFilePath));
283
+ const templatePath = path.join(packageRoot, relativeTemplatesDir);
284
+ if (!existsSync(templatePath)) throw new Error(`Template directory not found at: ${templatePath}`);
285
+ return templatePath;
286
+ }
287
+ async function getTemplateFilesRecursively(dir) {
288
+ const entries = await fs.readdir(dir, { withFileTypes: true });
289
+ return (await Promise.all(entries.map(async (entry) => {
290
+ const entryPath = path.join(dir, entry.name);
291
+ if (entry.isDirectory()) return getTemplateFilesRecursively(entryPath);
292
+ if (!entry.isFile()) return [];
293
+ return [entryPath];
294
+ }))).flat();
295
+ }
296
+ function stripHbsExtension(filePath) {
297
+ return filePath.endsWith(".hbs") ? filePath.slice(0, -4) : filePath;
298
+ }
299
+ function ensureTrailingNewline(content) {
300
+ return content.endsWith("\n") ? content : `${content}\n`;
301
+ }
302
+ async function renderTemplateFile(opts) {
303
+ const { templateFilePath, outputPath, context } = opts;
304
+ const templateContent = await fs.readFile(templateFilePath, "utf8");
305
+ const outputContent = templateFilePath.endsWith(".hbs") ? Handlebars.compile(templateContent, {
306
+ noEscape: true,
307
+ strict: true
308
+ })(context) : templateContent;
309
+ if (templateFilePath.endsWith(".hbs") && outputContent.trim().length === 0) return;
310
+ await fs.outputFile(outputPath, ensureTrailingNewline(outputContent), "utf8");
311
+ }
312
+ async function renderTemplateTree(opts) {
313
+ const { templateRoot, outputDir, context } = opts;
314
+ const templateFiles = await getTemplateFilesRecursively(templateRoot);
315
+ for (const templateFilePath of templateFiles) {
316
+ const relativeOutputPath = stripHbsExtension(path.relative(templateRoot, templateFilePath));
317
+ await renderTemplateFile({
318
+ templateFilePath,
319
+ outputPath: path.join(outputDir, relativeOutputPath),
320
+ context
321
+ });
322
+ }
323
+ }
324
+
325
+ //#endregion
326
+ //#region src/templates/render-create-template.ts
327
+ function getCreateTemplateDir(template) {
328
+ return resolveTemplatesDir(`templates/create/${template}`);
329
+ }
330
+ function createTemplateContext(projectName, provider, schemaPreset, packageManager) {
331
+ return {
332
+ projectName,
333
+ provider,
334
+ schemaPreset,
335
+ packageManager
336
+ };
337
+ }
338
+ async function scaffoldCreateTemplate(opts) {
339
+ const { projectDir, projectName, template, provider, schemaPreset, packageManager } = opts;
340
+ await renderTemplateTree({
341
+ templateRoot: getCreateTemplateDir(template),
342
+ outputDir: projectDir,
343
+ context: createTemplateContext(projectName, provider, schemaPreset, packageManager)
344
+ });
345
+ }
346
+
347
+ //#endregion
348
+ //#region src/constants/db-packages.ts
349
+ function getDbPackages(provider) {
350
+ switch (provider) {
351
+ case "postgresql":
352
+ case "cockroachdb": return { adapterPackage: "@prisma/adapter-pg" };
353
+ case "mysql": return { adapterPackage: "@prisma/adapter-mariadb" };
354
+ case "sqlite": return { adapterPackage: "@prisma/adapter-better-sqlite3" };
355
+ case "sqlserver": return { adapterPackage: "@prisma/adapter-mssql" };
356
+ default: {
357
+ const exhaustiveCheck = provider;
358
+ throw new Error(`Unsupported database provider: ${String(exhaustiveCheck)}`);
359
+ }
360
+ }
361
+ }
362
+
363
+ //#endregion
364
+ //#region src/tasks/install.ts
365
+ function getPrismaScriptMap(packageManager) {
366
+ if (packageManager === "deno") {
367
+ const prismaSpecifier = getDenoPrismaSpecifier();
368
+ return {
369
+ "db:generate": `deno run -A ${prismaSpecifier} generate`,
370
+ "db:push": `deno run -A ${prismaSpecifier} db push`,
371
+ "db:migrate": `deno run -A ${prismaSpecifier} migrate dev`,
372
+ "db:seed": `deno run -A ${prismaSpecifier} db seed`
373
+ };
374
+ }
375
+ return {
376
+ "db:generate": "prisma generate",
377
+ "db:push": "prisma db push",
378
+ "db:migrate": "prisma migrate dev",
379
+ "db:seed": "prisma db seed"
380
+ };
381
+ }
382
+ function getVersion(packageName) {
383
+ return dependencyVersionMap[packageName];
384
+ }
385
+ function unique(items) {
386
+ return [...new Set(items)];
387
+ }
388
+ function sortRecord(record) {
389
+ return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
390
+ }
391
+ async function addPackageDependency(opts) {
392
+ const { dependencies = [], devDependencies = [], customDependencies = {}, customDevDependencies = {}, scripts = {}, scriptMode = "upsert", projectDir } = opts;
393
+ const addedScripts = [];
394
+ const existingScripts = [];
395
+ const pkgJsonPath = path.join(projectDir, "package.json");
396
+ if (!await fs.pathExists(pkgJsonPath)) throw new Error(`No package.json found in ${projectDir}. Run this command inside an existing JavaScript/TypeScript project.`);
397
+ const pkgJson = await fs.readJson(pkgJsonPath);
398
+ if (!pkgJson.dependencies) pkgJson.dependencies = {};
399
+ if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
400
+ if (!pkgJson.scripts) pkgJson.scripts = {};
401
+ for (const pkgName of unique(dependencies)) {
402
+ const version = getVersion(pkgName);
403
+ if (version) pkgJson.dependencies[pkgName] = version;
404
+ else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
405
+ }
406
+ for (const pkgName of unique(devDependencies)) {
407
+ const version = getVersion(pkgName);
408
+ if (version) pkgJson.devDependencies[pkgName] = version;
409
+ else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
410
+ }
411
+ for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
412
+ for (const [pkgName, version] of Object.entries(customDevDependencies)) pkgJson.devDependencies[pkgName] = version;
413
+ for (const [scriptName, command] of Object.entries(scripts)) {
414
+ if (scriptMode === "if-missing") {
415
+ if (typeof pkgJson.scripts[scriptName] !== "string" || pkgJson.scripts[scriptName].trim().length === 0) {
416
+ pkgJson.scripts[scriptName] = command;
417
+ addedScripts.push(scriptName);
418
+ } else existingScripts.push(scriptName);
419
+ continue;
420
+ }
421
+ if (pkgJson.scripts[scriptName] === command) existingScripts.push(scriptName);
422
+ else addedScripts.push(scriptName);
423
+ pkgJson.scripts[scriptName] = command;
424
+ }
425
+ pkgJson.dependencies = sortRecord(pkgJson.dependencies);
426
+ pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
427
+ await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
428
+ return {
429
+ addedScripts,
430
+ existingScripts
431
+ };
432
+ }
433
+ async function writePrismaDependencies(provider, packageManager, projectDir = process.cwd()) {
434
+ const dependencies = ["@prisma/client", "dotenv"];
435
+ const devDependencies = ["prisma"];
436
+ const { adapterPackage } = getDbPackages(provider);
437
+ dependencies.push(adapterPackage);
438
+ if (provider === "sqlite" && packageManager === "deno") devDependencies.push("node-gyp");
439
+ const prismaScriptMap = getPrismaScriptMap(packageManager);
440
+ const scriptWriteResult = await addPackageDependency({
441
+ dependencies,
442
+ devDependencies,
443
+ scripts: prismaScriptMap,
444
+ scriptMode: "if-missing",
445
+ projectDir
446
+ });
447
+ return {
448
+ dependencies,
449
+ devDependencies,
450
+ scripts: Object.keys(prismaScriptMap),
451
+ addedScripts: scriptWriteResult.addedScripts,
452
+ existingScripts: scriptWriteResult.existingScripts
453
+ };
454
+ }
455
+ async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
456
+ const verbose = options.verbose === true;
457
+ const installCommand = getInstallArgs(packageManager);
458
+ await execa(installCommand.command, installCommand.args, {
459
+ cwd: projectDir,
460
+ stdio: verbose ? "inherit" : "pipe"
461
+ });
462
+ }
463
+
464
+ //#endregion
465
+ //#region src/tasks/prisma-postgres.ts
466
+ const PRISMA_POSTGRES_TEMPORARY_NOTICE = "Prisma Postgres is temporary for 24 hours. Claim this database before it expires using CLAIM_URL.";
467
+ const CREATE_DB_COMMAND_ARGS = ["create-db@latest", "--json"];
468
+ function parseCreateDbJson(rawOutput) {
469
+ const trimmed = rawOutput.trim();
470
+ if (!trimmed) throw new Error("create-db returned empty output.");
471
+ const jsonCandidates = [trimmed];
472
+ const firstBrace = trimmed.indexOf("{");
473
+ const lastBrace = trimmed.lastIndexOf("}");
474
+ if (firstBrace !== -1 && lastBrace > firstBrace) jsonCandidates.push(trimmed.slice(firstBrace, lastBrace + 1));
475
+ for (const candidate of jsonCandidates) try {
476
+ return JSON.parse(candidate);
477
+ } catch {}
478
+ throw new Error(`Unable to parse create-db JSON output: ${trimmed}`);
479
+ }
480
+ function pickConnectionString(payload) {
481
+ if (typeof payload.connectionString === "string" && payload.connectionString.length > 0) return payload.connectionString;
482
+ if (typeof payload.databaseUrl === "string" && payload.databaseUrl.length > 0) return payload.databaseUrl;
483
+ }
484
+ function extractErrorMessage(payload, fallback) {
485
+ if (typeof payload.message === "string" && payload.message.length > 0) return payload.message;
486
+ if (typeof payload.error === "string" && payload.error.length > 0) return payload.error;
487
+ return fallback;
488
+ }
489
+ async function provisionPrismaPostgres(packageManager, projectDir = process.cwd()) {
490
+ const command = getPackageExecutionArgs(packageManager, [...CREATE_DB_COMMAND_ARGS]);
491
+ const commandString = getCreateDbCommand(packageManager);
492
+ let stdout;
493
+ try {
494
+ stdout = (await execa(command.command, command.args, {
495
+ cwd: projectDir,
496
+ stdio: "pipe"
497
+ })).stdout;
498
+ } catch (error) {
499
+ if (error instanceof Error && "stderr" in error) {
500
+ const stderr = String(error.stderr ?? "").trim();
501
+ const message = stderr.length > 0 ? stderr : error.message;
502
+ throw new Error(`Failed to run ${commandString}: ${message}`);
503
+ }
504
+ throw error;
505
+ }
506
+ const payload = parseCreateDbJson(stdout);
507
+ if (payload.success === false) throw new Error(extractErrorMessage(payload, "create-db reported failure."));
508
+ const databaseUrl = pickConnectionString(payload);
509
+ if (!databaseUrl) throw new Error("create-db did not return a connection string.");
510
+ return {
511
+ databaseUrl,
512
+ claimUrl: typeof payload.claimUrl === "string" && payload.claimUrl.length > 0 ? payload.claimUrl : typeof payload.claimURL === "string" && payload.claimURL.length > 0 ? payload.claimURL : void 0
513
+ };
514
+ }
515
+ function getCreateDbCommand(packageManager) {
516
+ return getPackageExecutionCommand(packageManager, [...CREATE_DB_COMMAND_ARGS]);
517
+ }
518
+
519
+ //#endregion
520
+ //#region src/tasks/setup-prisma.ts
521
+ const DEFAULT_DATABASE_PROVIDER = "postgresql";
522
+ const DEFAULT_SCHEMA_PRESET$1 = "empty";
523
+ const DEFAULT_PRISMA_POSTGRES = true;
524
+ const DEFAULT_INSTALL = true;
525
+ const DEFAULT_GENERATE = true;
526
+ const requiredPrismaFileGroups = [
527
+ ["prisma/schema.prisma", "packages/db/prisma/schema.prisma"],
528
+ ["prisma/seed.ts", "packages/db/prisma/seed.ts"],
529
+ ["prisma.config.ts", "packages/db/prisma.config.ts"],
530
+ [
531
+ "src/lib/prisma.ts",
532
+ "src/lib/server/prisma.ts",
533
+ "server/utils/prisma.ts",
534
+ "packages/db/src/client.ts"
535
+ ]
536
+ ];
537
+ async function resolvePrismaProjectDir(projectDir) {
538
+ const monorepoDbDir = path.join(projectDir, "packages/db");
539
+ if (await fs.pathExists(path.join(monorepoDbDir, "prisma/schema.prisma"))) return monorepoDbDir;
540
+ return projectDir;
541
+ }
542
+ async function promptForDatabaseProvider() {
543
+ const databaseProvider = await select({
544
+ message: "Select your database",
545
+ initialValue: DEFAULT_DATABASE_PROVIDER,
546
+ options: [
547
+ {
548
+ value: "postgresql",
549
+ label: "PostgreSQL",
550
+ hint: "Default"
551
+ },
552
+ {
553
+ value: "mysql",
554
+ label: "MySQL"
555
+ },
556
+ {
557
+ value: "sqlite",
558
+ label: "SQLite"
559
+ },
560
+ {
561
+ value: "sqlserver",
562
+ label: "SQL Server"
563
+ },
564
+ {
565
+ value: "cockroachdb",
566
+ label: "CockroachDB"
567
+ }
568
+ ]
569
+ });
570
+ if (isCancel(databaseProvider)) {
571
+ cancel("Operation cancelled.");
572
+ return;
573
+ }
574
+ return DatabaseProviderSchema.parse(databaseProvider);
575
+ }
576
+ function getPackageManagerHint(option, detected) {
577
+ if (option === detected) return "Detected";
578
+ if (option === "bun") return "Fast runtime + package manager";
579
+ if (option === "deno") return "Runtime + package manager";
580
+ }
581
+ async function promptForPackageManager(detectedPackageManager) {
582
+ const packageManager = await select({
583
+ message: "Choose package manager",
584
+ initialValue: detectedPackageManager,
585
+ options: [
586
+ {
587
+ value: "npm",
588
+ label: "npm",
589
+ hint: getPackageManagerHint("npm", detectedPackageManager)
590
+ },
591
+ {
592
+ value: "pnpm",
593
+ label: "pnpm",
594
+ hint: getPackageManagerHint("pnpm", detectedPackageManager)
595
+ },
596
+ {
597
+ value: "yarn",
598
+ label: "yarn",
599
+ hint: getPackageManagerHint("yarn", detectedPackageManager)
600
+ },
601
+ {
602
+ value: "bun",
603
+ label: "bun",
604
+ hint: getPackageManagerHint("bun", detectedPackageManager)
605
+ },
606
+ {
607
+ value: "deno",
608
+ label: "deno",
609
+ hint: getPackageManagerHint("deno", detectedPackageManager)
610
+ }
611
+ ]
612
+ });
613
+ if (isCancel(packageManager)) {
614
+ cancel("Operation cancelled.");
615
+ return;
616
+ }
617
+ return PackageManagerSchema.parse(packageManager);
618
+ }
619
+ async function promptForDependencyInstall(packageManager) {
620
+ const shouldInstall = await confirm({
621
+ message: `Install dependencies now with ${getInstallCommand(packageManager)}?`,
622
+ initialValue: true
623
+ });
624
+ if (isCancel(shouldInstall)) {
625
+ cancel("Operation cancelled.");
626
+ return;
627
+ }
628
+ return Boolean(shouldInstall);
629
+ }
630
+ async function promptForPrismaPostgres() {
631
+ const shouldUsePrismaPostgres = await confirm({
632
+ message: "Use Prisma Postgres and auto-generate DATABASE_URL with create-db?",
633
+ initialValue: true
634
+ });
635
+ if (isCancel(shouldUsePrismaPostgres)) {
636
+ cancel("Operation cancelled.");
637
+ return;
638
+ }
639
+ return Boolean(shouldUsePrismaPostgres);
640
+ }
641
+ function getCommandErrorMessage(error) {
642
+ if (error instanceof Error && "stderr" in error) {
643
+ const stderr = String(error.stderr ?? "").trim();
644
+ if (stderr.length > 0) return stderr;
645
+ }
646
+ return error instanceof Error ? error.message : String(error);
647
+ }
648
+ async function collectPrismaSetupContext(input, options = {}) {
649
+ const projectDir = path.resolve(options.projectDir ?? process.cwd());
650
+ const useDefaults = input.yes === true;
651
+ const verbose = input.verbose === true;
652
+ const shouldGenerate = input.generate ?? DEFAULT_GENERATE;
653
+ const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
654
+ if (!databaseProvider) return;
655
+ const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
656
+ const databaseUrl = input.databaseUrl;
657
+ let shouldUsePrismaPostgres = false;
658
+ if (databaseProvider === "postgresql" && !databaseUrl) {
659
+ const prismaPostgresChoice = input.prismaPostgres ?? (useDefaults ? DEFAULT_PRISMA_POSTGRES : await promptForPrismaPostgres());
660
+ if (prismaPostgresChoice === void 0) return;
661
+ shouldUsePrismaPostgres = prismaPostgresChoice;
662
+ }
663
+ const detectedPackageManager = await detectPackageManager(projectDir);
664
+ const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
665
+ if (!packageManager) return;
666
+ const shouldInstall = input.install ?? (useDefaults ? DEFAULT_INSTALL : await promptForDependencyInstall(packageManager));
667
+ if (shouldInstall === void 0) return;
668
+ return {
669
+ projectDir,
670
+ verbose,
671
+ shouldGenerate,
672
+ databaseProvider,
673
+ schemaPreset,
674
+ databaseUrl,
675
+ shouldUsePrismaPostgres,
676
+ packageManager,
677
+ shouldInstall
678
+ };
679
+ }
680
+ function getDefaultDatabaseUrl(provider) {
681
+ switch (provider) {
682
+ case "postgresql": return "postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public";
683
+ case "cockroachdb": return "postgresql://johndoe:randompassword@localhost:26257/mydb?schema=public";
684
+ case "mysql": return "mysql://johndoe:randompassword@localhost:3306/mydb";
685
+ case "sqlite": return "file:./dev.db";
686
+ case "sqlserver": return "sqlserver://localhost:1433;database=mydb;user=SA;password=randompassword;";
687
+ default: {
688
+ const exhaustiveCheck = provider;
689
+ throw new Error(`Unsupported provider: ${String(exhaustiveCheck)}`);
690
+ }
691
+ }
692
+ }
693
+ function escapeRegExp(value) {
694
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
695
+ }
696
+ function escapeEnvValue(value) {
697
+ if (/[\r\n]/.test(value)) throw new Error("Environment variable values must be single-line.");
698
+ return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
699
+ }
700
+ function hasEnvVar(content, envVarName) {
701
+ const escapedName = escapeRegExp(envVarName);
702
+ return new RegExp(`(^|\\n)\\s*${escapedName}\\s*=`).test(content);
703
+ }
704
+ function hasEnvComment(content, comment) {
705
+ const escapedComment = escapeRegExp(comment);
706
+ return new RegExp(`(^|\\n)\\s*#\\s*${escapedComment}\\s*(?=\\n|$)`).test(content);
707
+ }
708
+ async function ensureEnvVarInEnv(projectDir, envVarName, envVarValue, opts) {
709
+ const envPath = path.join(projectDir, ".env");
710
+ const envLine = `${envVarName}="${escapeEnvValue(envVarValue)}"`;
711
+ if (!await fs.pathExists(envPath)) {
712
+ const content = opts.comment ? `# ${opts.comment}\n${envLine}\n` : `${envLine}\n`;
713
+ await fs.writeFile(envPath, content, "utf8");
714
+ return {
715
+ envPath,
716
+ status: "created"
717
+ };
718
+ }
719
+ const existingContent = await fs.readFile(envPath, "utf8");
720
+ if (hasEnvVar(existingContent, envVarName)) {
721
+ if (opts.mode === "keep-existing") return {
722
+ envPath,
723
+ status: "existing"
724
+ };
725
+ const escapedName = escapeRegExp(envVarName);
726
+ const lineRegex = new RegExp(`(^|\\n)\\s*${escapedName}\\s*=.*(?=\\n|$)`, "gm");
727
+ const updatedContent = existingContent.replace(lineRegex, `$1${envLine}`);
728
+ if (updatedContent === existingContent) return {
729
+ envPath,
730
+ status: "existing"
731
+ };
732
+ await fs.writeFile(envPath, updatedContent, "utf8");
733
+ return {
734
+ envPath,
735
+ status: "updated"
736
+ };
737
+ }
738
+ const insertion = `${existingContent.endsWith("\n") ? "" : "\n"}${opts.comment ? `\n# ${opts.comment}\n` : "\n"}${envLine}\n`;
739
+ await fs.appendFile(envPath, insertion, "utf8");
740
+ return {
741
+ envPath,
742
+ status: "appended"
743
+ };
744
+ }
745
+ async function ensureEnvComment(projectDir, comment) {
746
+ const envPath = path.join(projectDir, ".env");
747
+ const commentLine = `# ${comment}`;
748
+ if (!await fs.pathExists(envPath)) {
749
+ await fs.writeFile(envPath, `${commentLine}\n`, "utf8");
750
+ return;
751
+ }
752
+ const existingContent = await fs.readFile(envPath, "utf8");
753
+ if (hasEnvComment(existingContent, comment)) return;
754
+ const separator = existingContent.endsWith("\n") ? "" : "\n";
755
+ await fs.appendFile(envPath, `${separator}${commentLine}\n`, "utf8");
756
+ }
757
+ function hasGitignoreEntry(content, entry) {
758
+ const escapedEntry = escapeRegExp(entry);
759
+ const escapedWithLeadingSlash = escapeRegExp(`/${entry}`);
760
+ const escapedWithTrailingSlash = escapeRegExp(`${entry}/`);
761
+ const escapedWithLeadingAndTrailingSlash = escapeRegExp(`/${entry}/`);
762
+ return new RegExp(`(^|\\n)\\s*(?:${escapedEntry}|${escapedWithLeadingSlash}|${escapedWithTrailingSlash}|${escapedWithLeadingAndTrailingSlash})\\s*(?=\\n|$)`).test(content);
763
+ }
764
+ async function ensureGitignoreEntry(projectDir, entry) {
765
+ const gitignorePath = path.join(projectDir, ".gitignore");
766
+ if (!await fs.pathExists(gitignorePath)) {
767
+ await fs.writeFile(gitignorePath, `${entry}\n`, "utf8");
768
+ return {
769
+ gitignorePath,
770
+ status: "created"
771
+ };
772
+ }
773
+ const existingContent = await fs.readFile(gitignorePath, "utf8");
774
+ if (hasGitignoreEntry(existingContent, entry)) return {
775
+ gitignorePath,
776
+ status: "existing"
777
+ };
778
+ const separator = existingContent.endsWith("\n") ? "" : "\n";
779
+ await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
780
+ return {
781
+ gitignorePath,
782
+ status: "appended"
783
+ };
784
+ }
785
+ async function ensureRequiredPrismaFiles(projectDir) {
786
+ const missingFiles = [];
787
+ for (const candidates of requiredPrismaFileGroups) {
788
+ let foundCandidate = false;
789
+ for (const relativePath of candidates) {
790
+ const absolutePath = path.join(projectDir, relativePath);
791
+ if (await fs.pathExists(absolutePath)) {
792
+ foundCandidate = true;
793
+ break;
794
+ }
795
+ }
796
+ if (!foundCandidate) missingFiles.push(candidates.join(" or "));
797
+ }
798
+ if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
799
+ }
800
+ async function finalizePrismaFiles(options) {
801
+ const projectDir = options.projectDir ?? process.cwd();
802
+ const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
803
+ const schemaPath = path.join(prismaProjectDir, "prisma/schema.prisma");
804
+ const configPath = path.join(prismaProjectDir, "prisma.config.ts");
805
+ await ensureRequiredPrismaFiles(projectDir);
806
+ const singletonPath = await fs.pathExists(path.join(prismaProjectDir, "src/lib/prisma.ts")) ? path.join(prismaProjectDir, "src/lib/prisma.ts") : await fs.pathExists(path.join(prismaProjectDir, "src/lib/server/prisma.ts")) ? path.join(prismaProjectDir, "src/lib/server/prisma.ts") : await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? path.join(prismaProjectDir, "server/utils/prisma.ts") : path.join(prismaProjectDir, "src/client.ts");
807
+ const generatedDir = await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? "server/generated" : "src/generated";
808
+ const envResult = await ensureEnvVarInEnv(prismaProjectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
809
+ mode: options.databaseUrl ? "upsert" : "keep-existing",
810
+ comment: "Added by create-prisma"
811
+ });
812
+ let claimEnvStatus;
813
+ if (options.claimUrl) {
814
+ claimEnvStatus = (await ensureEnvVarInEnv(prismaProjectDir, "CLAIM_URL", options.claimUrl, {
815
+ mode: "upsert",
816
+ comment: PRISMA_POSTGRES_TEMPORARY_NOTICE
817
+ })).status;
818
+ await ensureEnvComment(prismaProjectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
819
+ }
820
+ const gitignoreResult = await ensureGitignoreEntry(prismaProjectDir, generatedDir);
821
+ return {
822
+ schemaPath,
823
+ configPath,
824
+ singletonPath,
825
+ envPath: envResult.envPath,
826
+ envStatus: envResult.status,
827
+ gitignorePath: gitignoreResult.gitignorePath,
828
+ gitignoreStatus: gitignoreResult.status,
829
+ claimEnvStatus
830
+ };
831
+ }
832
+ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
833
+ if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
834
+ const createDbCommand = getCreateDbCommand(context.packageManager);
835
+ const prismaPostgresSpinner = spinner();
836
+ prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
837
+ try {
838
+ const prismaPostgresResult = await provisionPrismaPostgres(context.packageManager, projectDir);
839
+ prismaPostgresSpinner.stop("Prisma Postgres database provisioned.");
840
+ return {
841
+ databaseUrl: prismaPostgresResult.databaseUrl,
842
+ claimUrl: prismaPostgresResult.claimUrl
843
+ };
844
+ } catch (error) {
845
+ const errorMessage = error instanceof Error ? error.message : String(error);
846
+ prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
847
+ return {
848
+ databaseUrl: context.databaseUrl,
849
+ warning: `Prisma Postgres provisioning failed: ${errorMessage}`
850
+ };
851
+ }
852
+ }
853
+ async function writeDependenciesForContext(context, projectDir) {
854
+ const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
855
+ try {
856
+ return await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
857
+ } catch (error) {
858
+ cancel(getCommandErrorMessage(error));
859
+ return;
860
+ }
861
+ }
862
+ async function installDependenciesForContext(context, projectDir) {
863
+ if (!context.shouldInstall) return true;
864
+ const installCommand = getInstallCommand(context.packageManager);
865
+ if (context.verbose) {
866
+ log.step(`Running ${installCommand}`);
867
+ try {
868
+ await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
869
+ log.success("Dependencies installed.");
870
+ return true;
871
+ } catch (error) {
872
+ cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
873
+ return false;
874
+ }
875
+ }
876
+ const installSpinner = spinner();
877
+ installSpinner.start(`Running ${installCommand}...`);
878
+ try {
879
+ await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
880
+ installSpinner.stop("Dependencies installed.");
881
+ return true;
882
+ } catch (error) {
883
+ installSpinner.stop("Could not install dependencies.");
884
+ cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
885
+ return false;
886
+ }
887
+ }
888
+ async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
889
+ const initSpinner = spinner();
890
+ initSpinner.start("Preparing Prisma files...");
891
+ try {
892
+ const finalizeResult = await finalizePrismaFiles({
893
+ provider: context.databaseProvider,
894
+ databaseUrl: provisionResult.databaseUrl,
895
+ claimUrl: provisionResult.claimUrl,
896
+ projectDir
897
+ });
898
+ initSpinner.stop("Prisma files ready.");
899
+ return finalizeResult;
900
+ } catch (error) {
901
+ initSpinner.stop("Could not prepare Prisma files.");
902
+ cancel(getCommandErrorMessage(error));
903
+ return;
904
+ }
905
+ }
906
+ async function generatePrismaClientForContext(context, projectDir) {
907
+ const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
908
+ if (!context.shouldGenerate) return { didGenerateClient: false };
909
+ const generateCommand = getPrismaCliCommand(context.packageManager, ["generate"]);
910
+ if (context.verbose) log.step(`Running ${generateCommand}`);
911
+ const generateSpinner = context.verbose ? void 0 : spinner();
912
+ generateSpinner?.start("Generating Prisma Client...");
913
+ try {
914
+ const generateArgs = getPrismaCliArgs(context.packageManager, ["generate"]);
915
+ await execa(generateArgs.command, generateArgs.args, {
916
+ cwd: prismaProjectDir,
917
+ stdio: context.verbose ? "inherit" : "pipe"
918
+ });
919
+ if (context.verbose) log.success("Prisma Client generated.");
920
+ else generateSpinner?.stop("Prisma Client generated.");
921
+ return { didGenerateClient: true };
922
+ } catch (error) {
923
+ if (context.verbose) log.warn("Could not generate Prisma Client.");
924
+ else generateSpinner?.stop("Could not generate Prisma Client.");
925
+ return {
926
+ didGenerateClient: false,
927
+ warning: `Prisma generate failed: ${getCommandErrorMessage(error)}`
928
+ };
929
+ }
930
+ }
931
+ function buildWarningLines(provisionWarning, generateWarning) {
932
+ const warningLines = [];
933
+ if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
934
+ if (generateWarning) warningLines.push(`- ${generateWarning}`);
935
+ return warningLines;
936
+ }
937
+ function buildNextStepsForContext(opts) {
938
+ const { context, options, didGenerateClient } = opts;
939
+ const nextSteps = [...options.prependNextSteps ?? []];
940
+ if (!context.shouldInstall) nextSteps.push(`- ${getInstallCommand(context.packageManager)}`);
941
+ if (!didGenerateClient || !context.shouldGenerate) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:generate")}`);
942
+ nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:migrate")}`);
943
+ nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:seed")}`);
944
+ if (options.includeDevNextStep) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "dev")}`);
945
+ return nextSteps;
946
+ }
947
+ async function executePrismaSetupContext(context, options = {}) {
948
+ const projectDir = path.resolve(options.projectDir ?? context.projectDir);
949
+ const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
950
+ if (!provisionResult) return;
951
+ if (!await writeDependenciesForContext(context, projectDir)) return;
952
+ if (!await installDependenciesForContext(context, projectDir)) return;
953
+ if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) return;
954
+ const generateResult = await generatePrismaClientForContext(context, projectDir);
955
+ const warningLines = buildWarningLines(provisionResult.warning, generateResult.warning);
956
+ const nextSteps = buildNextStepsForContext({
957
+ context,
958
+ options,
959
+ didGenerateClient: generateResult.didGenerateClient
960
+ });
961
+ outro(`Setup complete.${warningLines.length > 0 ? `\n\n${warningLines.join("\n")}` : ""}
962
+
963
+ Next steps:
964
+ ${nextSteps.join("\n")}`);
965
+ return { packageManager: context.packageManager };
966
+ }
967
+
968
+ //#endregion
969
+ //#region src/tasks/setup-addons.ts
970
+ const DEFAULT_ADDON_SCOPE = "project";
971
+ const DEFAULT_SKILLS_AGENTS = [
972
+ "claude-code",
973
+ "codex",
974
+ "cursor"
975
+ ];
976
+ const DEFAULT_MCP_AGENTS = [
977
+ "claude-code",
978
+ "codex",
979
+ "cursor"
980
+ ];
981
+ const DEFAULT_EXTENSION_TARGETS = ["vscode", "cursor"];
982
+ const PRISMA_MCP_SERVER = "https://mcp.prisma.io/mcp";
983
+ const ADDON_OPTIONS = [
984
+ {
985
+ value: "skills",
986
+ label: "Skills",
987
+ hint: "Install curated Prisma skills to your selected coding agents"
988
+ },
989
+ {
990
+ value: "mcp",
991
+ label: "MCP",
992
+ hint: "Configure Prisma MCP server in agent MCP config files"
993
+ },
994
+ {
995
+ value: "extension",
996
+ label: "IDE Extension",
997
+ hint: "Install Prisma extension in selected IDEs (VS Code, Cursor, Windsurf)"
998
+ }
999
+ ];
1000
+ const SKILLS_AGENT_OPTIONS = [
1001
+ {
1002
+ value: "cursor",
1003
+ label: "Cursor"
1004
+ },
1005
+ {
1006
+ value: "claude-code",
1007
+ label: "Claude Code"
1008
+ },
1009
+ {
1010
+ value: "cline",
1011
+ label: "Cline"
1012
+ },
1013
+ {
1014
+ value: "github-copilot",
1015
+ label: "GitHub Copilot"
1016
+ },
1017
+ {
1018
+ value: "codex",
1019
+ label: "Codex"
1020
+ },
1021
+ {
1022
+ value: "opencode",
1023
+ label: "OpenCode"
1024
+ },
1025
+ {
1026
+ value: "windsurf",
1027
+ label: "Windsurf"
1028
+ },
1029
+ {
1030
+ value: "goose",
1031
+ label: "Goose"
1032
+ },
1033
+ {
1034
+ value: "roo",
1035
+ label: "Roo Code"
1036
+ },
1037
+ {
1038
+ value: "kilo",
1039
+ label: "Kilo Code"
1040
+ },
1041
+ {
1042
+ value: "gemini-cli",
1043
+ label: "Gemini CLI"
1044
+ },
1045
+ {
1046
+ value: "antigravity",
1047
+ label: "Antigravity"
1048
+ },
1049
+ {
1050
+ value: "openhands",
1051
+ label: "OpenHands"
1052
+ },
1053
+ {
1054
+ value: "trae",
1055
+ label: "Trae"
1056
+ },
1057
+ {
1058
+ value: "amp",
1059
+ label: "Amp"
1060
+ },
1061
+ {
1062
+ value: "pi",
1063
+ label: "Pi"
1064
+ },
1065
+ {
1066
+ value: "qoder",
1067
+ label: "Qoder"
1068
+ },
1069
+ {
1070
+ value: "qwen-code",
1071
+ label: "Qwen Code"
1072
+ },
1073
+ {
1074
+ value: "kiro-cli",
1075
+ label: "Kiro CLI"
1076
+ },
1077
+ {
1078
+ value: "droid",
1079
+ label: "Droid"
1080
+ },
1081
+ {
1082
+ value: "command-code",
1083
+ label: "Command Code"
1084
+ },
1085
+ {
1086
+ value: "clawdbot",
1087
+ label: "Clawdbot"
1088
+ },
1089
+ {
1090
+ value: "zencoder",
1091
+ label: "Zencoder"
1092
+ },
1093
+ {
1094
+ value: "neovate",
1095
+ label: "Neovate"
1096
+ },
1097
+ {
1098
+ value: "mcpjam",
1099
+ label: "MCPJam"
1100
+ }
1101
+ ];
1102
+ const MCP_AGENT_OPTIONS = [
1103
+ {
1104
+ value: "claude-code",
1105
+ label: "Claude Code"
1106
+ },
1107
+ {
1108
+ value: "codex",
1109
+ label: "Codex"
1110
+ },
1111
+ {
1112
+ value: "cursor",
1113
+ label: "Cursor"
1114
+ },
1115
+ {
1116
+ value: "vscode",
1117
+ label: "VS Code"
1118
+ },
1119
+ {
1120
+ value: "github-copilot-cli",
1121
+ label: "GitHub Copilot CLI"
1122
+ },
1123
+ {
1124
+ value: "opencode",
1125
+ label: "OpenCode"
1126
+ },
1127
+ {
1128
+ value: "gemini-cli",
1129
+ label: "Gemini CLI"
1130
+ },
1131
+ {
1132
+ value: "goose",
1133
+ label: "Goose"
1134
+ },
1135
+ {
1136
+ value: "zed",
1137
+ label: "Zed"
1138
+ },
1139
+ {
1140
+ value: "antigravity",
1141
+ label: "Antigravity"
1142
+ },
1143
+ {
1144
+ value: "cline",
1145
+ label: "Cline VS Code Extension"
1146
+ },
1147
+ {
1148
+ value: "cline-cli",
1149
+ label: "Cline CLI"
1150
+ },
1151
+ {
1152
+ value: "claude-desktop",
1153
+ label: "Claude Desktop"
1154
+ },
1155
+ {
1156
+ value: "mcporter",
1157
+ label: "MCPorter"
1158
+ }
1159
+ ];
1160
+ const SHARED_PRISMA_SKILLS = [
1161
+ "prisma-cli",
1162
+ "prisma-client-api",
1163
+ "prisma-database-setup",
1164
+ "prisma-upgrade-v7"
1165
+ ];
1166
+ function getAvailablePrismaSkills(provider) {
1167
+ if (provider === "postgresql") return [...SHARED_PRISMA_SKILLS, "prisma-postgres"];
1168
+ return [...SHARED_PRISMA_SKILLS];
1169
+ }
1170
+ function getSkillOptions(provider) {
1171
+ const available = getAvailablePrismaSkills(provider);
1172
+ const options = {
1173
+ "prisma-cli": {
1174
+ value: "prisma-cli",
1175
+ label: "prisma-cli",
1176
+ hint: "Prisma CLI reference"
1177
+ },
1178
+ "prisma-client-api": {
1179
+ value: "prisma-client-api",
1180
+ label: "prisma-client-api",
1181
+ hint: "Prisma Client query patterns"
1182
+ },
1183
+ "prisma-database-setup": {
1184
+ value: "prisma-database-setup",
1185
+ label: "prisma-database-setup",
1186
+ hint: "Database provider setup guides"
1187
+ },
1188
+ "prisma-upgrade-v7": {
1189
+ value: "prisma-upgrade-v7",
1190
+ label: "prisma-upgrade-v7",
1191
+ hint: "v6 to v7 migration guide"
1192
+ },
1193
+ "prisma-postgres": {
1194
+ value: "prisma-postgres",
1195
+ label: "prisma-postgres",
1196
+ hint: "Prisma Postgres workflows"
1197
+ }
1198
+ };
1199
+ return available.map((skill) => options[skill]);
1200
+ }
1201
+ function collectAddonsFromInput(input) {
1202
+ const addons = [];
1203
+ if (input.skills === true) addons.push("skills");
1204
+ if (input.mcp === true) addons.push("mcp");
1205
+ if (input.extension === true) addons.push("extension");
1206
+ return uniqueValues(addons);
1207
+ }
1208
+ const EXTENSION_TARGET_OPTIONS = [
1209
+ {
1210
+ value: "vscode",
1211
+ label: "VS Code",
1212
+ hint: "Uses the `code` CLI"
1213
+ },
1214
+ {
1215
+ value: "cursor",
1216
+ label: "Cursor",
1217
+ hint: "Uses the `cursor` CLI"
1218
+ },
1219
+ {
1220
+ value: "windsurf",
1221
+ label: "Windsurf",
1222
+ hint: "Uses the `windsurf` CLI"
1223
+ }
1224
+ ];
1225
+ function uniqueValues(values) {
1226
+ return Array.from(new Set(values));
1227
+ }
1228
+ function getRecommendedPrismaSkills(provider, shouldUsePrismaPostgres) {
1229
+ const skills = [...getAvailablePrismaSkills(provider)];
1230
+ if (!shouldUsePrismaPostgres) return skills.filter((skill) => skill !== "prisma-postgres");
1231
+ return uniqueValues(skills);
1232
+ }
1233
+ async function promptForAddons() {
1234
+ const selectedAddons = await multiselect({
1235
+ message: "Select add-ons (optional)",
1236
+ options: ADDON_OPTIONS,
1237
+ required: false
1238
+ });
1239
+ if (isCancel(selectedAddons)) {
1240
+ cancel("Operation cancelled.");
1241
+ return;
1242
+ }
1243
+ return uniqueValues(selectedAddons);
1244
+ }
1245
+ async function promptForAddonScope() {
1246
+ const selectedScope = await select({
1247
+ message: "Where should add-ons write config?",
1248
+ initialValue: DEFAULT_ADDON_SCOPE,
1249
+ options: [{
1250
+ value: "project",
1251
+ label: "Project",
1252
+ hint: "Recommended for teams (checked into the project when applicable)"
1253
+ }, {
1254
+ value: "global",
1255
+ label: "Global",
1256
+ hint: "Personal machine-level setup"
1257
+ }]
1258
+ });
1259
+ if (isCancel(selectedScope)) {
1260
+ cancel("Operation cancelled.");
1261
+ return;
1262
+ }
1263
+ return selectedScope;
1264
+ }
1265
+ async function promptForPrismaSkills(provider, recommendedSkills) {
1266
+ const options = getSkillOptions(provider);
1267
+ const optionValues = new Set(options.map((option) => option.value));
1268
+ const selectedSkills = await multiselect({
1269
+ message: "Select Prisma skills",
1270
+ options,
1271
+ required: false,
1272
+ initialValues: recommendedSkills.filter((skill) => optionValues.has(skill))
1273
+ });
1274
+ if (isCancel(selectedSkills)) {
1275
+ cancel("Operation cancelled.");
1276
+ return;
1277
+ }
1278
+ return uniqueValues(selectedSkills);
1279
+ }
1280
+ async function promptForSkillsAgents() {
1281
+ const selectedAgents = await multiselect({
1282
+ message: "Select agents for skills",
1283
+ options: SKILLS_AGENT_OPTIONS,
1284
+ required: false,
1285
+ initialValues: [...DEFAULT_SKILLS_AGENTS]
1286
+ });
1287
+ if (isCancel(selectedAgents)) {
1288
+ cancel("Operation cancelled.");
1289
+ return;
1290
+ }
1291
+ return uniqueValues(selectedAgents);
1292
+ }
1293
+ async function promptForMcpAgents() {
1294
+ const selectedAgents = await multiselect({
1295
+ message: "Select agents for MCP",
1296
+ options: MCP_AGENT_OPTIONS,
1297
+ required: false,
1298
+ initialValues: [...DEFAULT_MCP_AGENTS]
1299
+ });
1300
+ if (isCancel(selectedAgents)) {
1301
+ cancel("Operation cancelled.");
1302
+ return;
1303
+ }
1304
+ return uniqueValues(selectedAgents);
1305
+ }
1306
+ async function promptForExtensionTargets() {
1307
+ const selectedTargets = await multiselect({
1308
+ message: "Select IDEs for extension install",
1309
+ options: EXTENSION_TARGET_OPTIONS,
1310
+ required: false,
1311
+ initialValues: DEFAULT_EXTENSION_TARGETS
1312
+ });
1313
+ if (isCancel(selectedTargets)) {
1314
+ cancel("Operation cancelled.");
1315
+ return;
1316
+ }
1317
+ return uniqueValues(selectedTargets);
1318
+ }
1319
+ async function collectCreateAddonSetupContext(input, options) {
1320
+ const hasExplicitAddonSelection = input.skills !== void 0 || input.mcp !== void 0 || input.extension !== void 0;
1321
+ const selectedFromInput = collectAddonsFromInput(input);
1322
+ const selectedAddons = selectedFromInput.length > 0 ? selectedFromInput : hasExplicitAddonSelection ? [] : options.useDefaults ? [] : await promptForAddons();
1323
+ if (!selectedAddons) return;
1324
+ const addons = uniqueValues(selectedAddons);
1325
+ if (addons.length === 0) return null;
1326
+ const scope = addons.includes("skills") || addons.includes("mcp") ? options.useDefaults ? DEFAULT_ADDON_SCOPE : await promptForAddonScope() : DEFAULT_ADDON_SCOPE;
1327
+ if (!scope) return;
1328
+ const recommendedSkills = getRecommendedPrismaSkills(options.provider, options.shouldUsePrismaPostgres);
1329
+ const skills = !addons.includes("skills") ? [] : options.useDefaults ? recommendedSkills : await promptForPrismaSkills(options.provider, recommendedSkills);
1330
+ if (!skills) return;
1331
+ const skillsAgents = !addons.includes("skills") ? [] : options.useDefaults ? [...DEFAULT_SKILLS_AGENTS] : await promptForSkillsAgents();
1332
+ if (!skillsAgents) return;
1333
+ const mcpAgents = !addons.includes("mcp") ? [] : options.useDefaults ? [...DEFAULT_MCP_AGENTS] : await promptForMcpAgents();
1334
+ if (!mcpAgents) return;
1335
+ const extensionTargets = !addons.includes("extension") ? [] : options.useDefaults ? [...DEFAULT_EXTENSION_TARGETS] : await promptForExtensionTargets();
1336
+ if (!extensionTargets) return;
1337
+ return {
1338
+ addons,
1339
+ scope,
1340
+ skills,
1341
+ skillsAgents: uniqueValues(skillsAgents),
1342
+ mcpAgents: uniqueValues(mcpAgents),
1343
+ extensionTargets: uniqueValues(extensionTargets)
1344
+ };
1345
+ }
1346
+ async function executeExternalCommand(params) {
1347
+ await execa(params.command, params.args, {
1348
+ cwd: params.cwd,
1349
+ stdio: params.verbose ? "inherit" : "pipe",
1350
+ env: {
1351
+ ...process.env,
1352
+ CI: "true"
1353
+ }
1354
+ });
1355
+ }
1356
+ async function installSkillsAddon(params) {
1357
+ if (params.agents.length === 0 || params.skills.length === 0) return "Skipped skills addon because no skills or agents were selected.";
1358
+ const scopeArgs = params.scope === "global" ? ["-g"] : [];
1359
+ const skillArgs = params.skills.flatMap((skill) => ["-s", skill]);
1360
+ const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
1361
+ const commandArgs = [
1362
+ "skills@latest",
1363
+ "add",
1364
+ "prisma/skills",
1365
+ ...scopeArgs,
1366
+ ...skillArgs,
1367
+ ...agentArgs,
1368
+ "-y"
1369
+ ];
1370
+ const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
1371
+ try {
1372
+ await executeExternalCommand({
1373
+ command: execution.command,
1374
+ args: execution.args,
1375
+ cwd: params.projectDir,
1376
+ verbose: params.verbose
1377
+ });
1378
+ return;
1379
+ } catch (error) {
1380
+ return `Skills addon failed: ${error instanceof Error ? error.message : String(error)}`;
1381
+ }
1382
+ }
1383
+ async function installMcpAddon(params) {
1384
+ if (params.agents.length === 0) return "Skipped MCP addon because no agents were selected.";
1385
+ const scopeArgs = params.scope === "global" ? ["-g"] : [];
1386
+ const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
1387
+ const commandArgs = [
1388
+ "add-mcp@latest",
1389
+ PRISMA_MCP_SERVER,
1390
+ ...scopeArgs,
1391
+ ...agentArgs,
1392
+ "--name",
1393
+ "prisma",
1394
+ "--gitignore",
1395
+ "-y"
1396
+ ];
1397
+ const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
1398
+ try {
1399
+ await executeExternalCommand({
1400
+ command: execution.command,
1401
+ args: execution.args,
1402
+ cwd: params.projectDir,
1403
+ verbose: params.verbose
1404
+ });
1405
+ return;
1406
+ } catch (error) {
1407
+ return `MCP addon failed: ${error instanceof Error ? error.message : String(error)}`;
1408
+ }
1409
+ }
1410
+ function getExtensionInstallBinary(target) {
1411
+ switch (target) {
1412
+ case "vscode": return "code";
1413
+ case "cursor": return "cursor";
1414
+ case "windsurf": return "windsurf";
1415
+ default: {
1416
+ const exhaustiveCheck = target;
1417
+ throw new Error(`Unsupported extension target: ${String(exhaustiveCheck)}`);
1418
+ }
1419
+ }
1420
+ }
1421
+ async function installExtensionAddon(params) {
1422
+ if (params.targets.length === 0) return ["Skipped extension addon because no IDE targets were selected."];
1423
+ const warnings = [];
1424
+ for (const target of params.targets) {
1425
+ const binary = getExtensionInstallBinary(target);
1426
+ try {
1427
+ await executeExternalCommand({
1428
+ command: binary,
1429
+ args: ["--version"],
1430
+ cwd: params.projectDir,
1431
+ verbose: false
1432
+ });
1433
+ } catch {
1434
+ warnings.push(`Skipped ${target} extension install because the \`${binary}\` CLI is not available.`);
1435
+ continue;
1436
+ }
1437
+ try {
1438
+ await executeExternalCommand({
1439
+ command: binary,
1440
+ args: [
1441
+ "--install-extension",
1442
+ "Prisma.prisma",
1443
+ "--force"
1444
+ ],
1445
+ cwd: params.projectDir,
1446
+ verbose: params.verbose
1447
+ });
1448
+ } catch (error) {
1449
+ warnings.push(`${target} extension install failed: ${error instanceof Error ? error.message : String(error)}`);
1450
+ }
1451
+ }
1452
+ return warnings;
1453
+ }
1454
+ async function executeCreateAddonSetupContext(params) {
1455
+ const { context, packageManager, projectDir, verbose } = params;
1456
+ const addonSpinner = spinner();
1457
+ addonSpinner.start("Applying selected add-ons...");
1458
+ const warnings = [];
1459
+ if (context.addons.includes("skills")) {
1460
+ const warning = await installSkillsAddon({
1461
+ packageManager,
1462
+ projectDir,
1463
+ scope: context.scope,
1464
+ skills: context.skills,
1465
+ agents: context.skillsAgents,
1466
+ verbose
1467
+ });
1468
+ if (warning) warnings.push(warning);
1469
+ }
1470
+ if (context.addons.includes("mcp")) {
1471
+ const warning = await installMcpAddon({
1472
+ packageManager,
1473
+ projectDir,
1474
+ scope: context.scope,
1475
+ agents: context.mcpAgents,
1476
+ verbose
1477
+ });
1478
+ if (warning) warnings.push(warning);
1479
+ }
1480
+ if (context.addons.includes("extension")) {
1481
+ const extensionWarnings = await installExtensionAddon({
1482
+ projectDir,
1483
+ verbose,
1484
+ targets: context.extensionTargets
1485
+ });
1486
+ warnings.push(...extensionWarnings);
1487
+ }
1488
+ if (warnings.length > 0) {
1489
+ addonSpinner.stop("Add-ons applied with warnings.");
1490
+ for (const warning of warnings) log.warn(warning);
1491
+ return;
1492
+ }
1493
+ addonSpinner.stop("Add-ons applied.");
1494
+ }
1495
+
1496
+ //#endregion
1497
+ //#region src/ui/branding.ts
1498
+ const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
1499
+ function getCreatePrismaIntro() {
1500
+ return prismaTitle;
1501
+ }
1502
+
1503
+ //#endregion
1504
+ //#region src/commands/create.ts
1505
+ const DEFAULT_PROJECT_NAME = "my-app";
1506
+ const DEFAULT_TEMPLATE = "hono";
1507
+ const DEFAULT_SCHEMA_PRESET = "basic";
1508
+ function toPackageName(projectName) {
1509
+ return projectName.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+/, "").replace(/-+$/, "") || "app";
1510
+ }
1511
+ function formatPathForDisplay(filePath) {
1512
+ return path.relative(process.cwd(), filePath) || ".";
1513
+ }
1514
+ function validateProjectName(value) {
1515
+ const trimmed = String(value ?? "").trim();
1516
+ if (trimmed.length === 0) return "Please enter a project name.";
1517
+ if (trimmed === "." || trimmed === "..") return "Project name cannot be '.' or '..'.";
1518
+ if (path.isAbsolute(trimmed)) return "Use a relative project name instead of an absolute path.";
1519
+ }
1520
+ async function promptForProjectName() {
1521
+ const projectName = await text({
1522
+ message: "Project name",
1523
+ placeholder: DEFAULT_PROJECT_NAME,
1524
+ initialValue: DEFAULT_PROJECT_NAME,
1525
+ validate: validateProjectName
1526
+ });
1527
+ if (isCancel(projectName)) {
1528
+ cancel("Operation cancelled.");
1529
+ return;
1530
+ }
1531
+ return String(projectName).trim();
1532
+ }
1533
+ async function promptForCreateTemplate() {
1534
+ const template = await select({
1535
+ message: "Select template",
1536
+ initialValue: DEFAULT_TEMPLATE,
1537
+ options: [
1538
+ {
1539
+ value: "hono",
1540
+ label: "Hono",
1541
+ hint: "TypeScript API starter"
1542
+ },
1543
+ {
1544
+ value: "next",
1545
+ label: "Next.js",
1546
+ hint: "App Router + TypeScript starter"
1547
+ },
1548
+ {
1549
+ value: "svelte",
1550
+ label: "SvelteKit",
1551
+ hint: "Official minimal SvelteKit + TypeScript starter"
1552
+ },
1553
+ {
1554
+ value: "astro",
1555
+ label: "Astro",
1556
+ hint: "Official minimal Astro starter with API route example"
1557
+ },
1558
+ {
1559
+ value: "nuxt",
1560
+ label: "Nuxt",
1561
+ hint: "Official minimal Nuxt starter with Nitro API route example"
1562
+ },
1563
+ {
1564
+ value: "turborepo",
1565
+ label: "Turborepo",
1566
+ hint: "Monorepo starter with apps + packages/db Prisma package"
1567
+ }
1568
+ ]
1569
+ });
1570
+ if (isCancel(template)) {
1571
+ cancel("Operation cancelled.");
1572
+ return;
1573
+ }
1574
+ return CreateTemplateSchema.parse(template);
1575
+ }
1576
+ async function inspectTargetPath(targetPath) {
1577
+ if (!await fs.pathExists(targetPath)) return {
1578
+ exists: false,
1579
+ isDirectory: true,
1580
+ isEmptyDirectory: true
1581
+ };
1582
+ if (!(await fs.stat(targetPath)).isDirectory()) return {
1583
+ exists: true,
1584
+ isDirectory: false,
1585
+ isEmptyDirectory: false
1586
+ };
1587
+ return {
1588
+ exists: true,
1589
+ isDirectory: true,
1590
+ isEmptyDirectory: (await fs.readdir(targetPath)).length === 0
1591
+ };
1592
+ }
1593
+ async function runCreateCommand(rawInput = {}) {
1594
+ try {
1595
+ const input = CreateCommandInputSchema.parse(rawInput);
1596
+ intro(getCreatePrismaIntro());
1597
+ const context = await collectCreateContext(input);
1598
+ if (!context) return;
1599
+ await executeCreateContext(context);
1600
+ } catch (error) {
1601
+ cancel(`Create command failed: ${error instanceof Error ? error.message : String(error)}`);
1602
+ }
1603
+ }
1604
+ async function collectCreateContext(input) {
1605
+ const useDefaults = input.yes === true;
1606
+ const force = input.force === true;
1607
+ const projectName = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
1608
+ if (!projectName) return;
1609
+ const template = input.template ?? (useDefaults ? DEFAULT_TEMPLATE : await promptForCreateTemplate());
1610
+ if (!template) return;
1611
+ const targetDirectory = path.resolve(process.cwd(), projectName);
1612
+ const targetPathState = await inspectTargetPath(targetDirectory);
1613
+ if (targetPathState.exists && !targetPathState.isDirectory) {
1614
+ cancel(`Target path ${formatPathForDisplay(targetDirectory)} already exists and is not a directory. Choose a different project name.`);
1615
+ return;
1616
+ }
1617
+ if (targetPathState.exists && !targetPathState.isEmptyDirectory && !force) {
1618
+ cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
1619
+ return;
1620
+ }
1621
+ const prismaSetupContext = await collectPrismaSetupContext(input, {
1622
+ projectDir: targetDirectory,
1623
+ defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
1624
+ });
1625
+ if (!prismaSetupContext) return;
1626
+ const addonSetupContext = await collectCreateAddonSetupContext(input, {
1627
+ useDefaults,
1628
+ provider: prismaSetupContext.databaseProvider,
1629
+ shouldUsePrismaPostgres: prismaSetupContext.shouldUsePrismaPostgres
1630
+ });
1631
+ if (addonSetupContext === void 0) return;
1632
+ return {
1633
+ targetDirectory,
1634
+ targetPathState,
1635
+ force,
1636
+ template,
1637
+ schemaPreset: prismaSetupContext.schemaPreset,
1638
+ projectPackageName: toPackageName(path.basename(targetDirectory)),
1639
+ prismaSetupContext,
1640
+ addonSetupContext: addonSetupContext ?? void 0
1641
+ };
1642
+ }
1643
+ async function executeCreateContext(context) {
1644
+ const scaffoldSpinner = spinner();
1645
+ scaffoldSpinner.start(`Scaffolding ${context.template} project...`);
1646
+ try {
1647
+ await scaffoldCreateTemplate({
1648
+ projectDir: context.targetDirectory,
1649
+ projectName: context.projectPackageName,
1650
+ template: context.template,
1651
+ schemaPreset: context.schemaPreset,
1652
+ provider: context.prismaSetupContext.databaseProvider,
1653
+ packageManager: context.prismaSetupContext.packageManager
1654
+ });
1655
+ scaffoldSpinner.stop("Project files scaffolded.");
1656
+ } catch (error) {
1657
+ scaffoldSpinner.stop("Could not scaffold project files.");
1658
+ cancel(error instanceof Error ? error.message : String(error));
1659
+ return;
1660
+ }
1661
+ if (context.targetPathState.exists && !context.targetPathState.isEmptyDirectory && context.force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(context.targetDirectory)}.`);
1662
+ const cdStep = `- cd ${formatPathForDisplay(context.targetDirectory)}`;
1663
+ if (context.addonSetupContext) await executeCreateAddonSetupContext({
1664
+ context: context.addonSetupContext,
1665
+ packageManager: context.prismaSetupContext.packageManager,
1666
+ projectDir: context.targetDirectory,
1667
+ verbose: context.prismaSetupContext.verbose
1668
+ });
1669
+ await executePrismaSetupContext(context.prismaSetupContext, {
1670
+ prependNextSteps: [cdStep],
1671
+ projectDir: context.targetDirectory,
1672
+ includeDevNextStep: true
1673
+ });
1674
+ }
1675
+
1676
+ //#endregion
1677
+ export { DatabaseUrlSchema as a, DatabaseProviderSchema as i, CreateCommandInputSchema as n, PackageManagerSchema as o, CreateTemplateSchema as r, SchemaPresetSchema as s, runCreateCommand as t };