create-prisma 0.7.1-pr.52.181.1 → 0.7.2
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/README.md +25 -19
- package/dist/cli.mjs +2 -32
- package/dist/index.d.mts +25 -1
- package/dist/index.mjs +2 -2290
- package/dist/src-B3hRLZ0U.mjs +2527 -0
- package/package.json +2 -4
- package/templates/create/astro/README.md.hbs +14 -7
- package/templates/create/astro/astro.config.mjs.hbs +0 -11
- package/templates/create/astro/deno.json.hbs +5 -0
- package/templates/create/astro/prisma/schema.prisma.hbs +5 -0
- package/templates/create/astro/prisma/seed.ts.hbs +5 -1
- package/templates/create/astro/prisma.compute.ts.hbs +10 -0
- package/templates/create/astro/src/lib/prisma.ts.hbs +4 -18
- package/templates/create/astro/src/pages/api/users.ts.hbs +11 -1
- package/templates/create/astro/src/pages/index.astro.hbs +176 -36
- package/templates/create/elysia/README.md.hbs +13 -7
- package/templates/create/elysia/deno.json.hbs +5 -0
- package/templates/create/elysia/package.json.hbs +3 -4
- package/templates/create/elysia/prisma/schema.prisma.hbs +6 -0
- package/templates/create/elysia/prisma/seed.ts.hbs +5 -1
- package/templates/create/elysia/prisma.compute.ts.hbs +11 -0
- package/templates/create/elysia/src/index.ts.hbs +17 -13
- package/templates/create/elysia/src/lib/prisma.ts.hbs +4 -18
- package/templates/create/elysia/tsconfig.json +0 -1
- package/templates/create/hono/README.md.hbs +13 -7
- package/templates/create/hono/deno.json.hbs +5 -0
- package/templates/create/hono/package.json.hbs +2 -2
- package/templates/create/hono/prisma/schema.prisma.hbs +6 -0
- package/templates/create/hono/prisma/seed.ts.hbs +5 -1
- package/templates/create/hono/prisma.compute.ts.hbs +10 -0
- package/templates/create/hono/src/index.ts.hbs +7 -9
- package/templates/create/hono/src/lib/prisma.ts.hbs +4 -18
- package/templates/create/hono/tsconfig.json +0 -1
- package/templates/create/nest/README.md.hbs +12 -6
- package/templates/create/nest/deno.json.hbs +5 -0
- package/templates/create/nest/package.json.hbs +2 -2
- package/templates/create/nest/prisma/schema.prisma.hbs +6 -0
- package/templates/create/nest/prisma/seed.ts.hbs +5 -1
- package/templates/create/nest/prisma.compute.ts.hbs +10 -0
- package/templates/create/nest/src/app.controller.ts.hbs +1 -6
- package/templates/create/nest/src/app.module.ts.hbs +14 -7
- package/templates/create/nest/src/lib/prisma.ts.hbs +4 -17
- package/templates/create/nest/src/main.ts.hbs +6 -2
- package/templates/create/nest/src/prisma.service.ts.hbs +2 -2
- package/templates/create/nest/src/users.controller.ts.hbs +3 -1
- package/templates/create/nest/src/users.service.ts.hbs +3 -1
- package/templates/create/nest/tsconfig.json +0 -1
- package/templates/create/next/README.md.hbs +11 -7
- package/templates/create/next/deno.json.hbs +12 -0
- package/templates/create/next/prisma/schema.prisma.hbs +5 -0
- package/templates/create/next/prisma/seed.ts.hbs +5 -1
- package/templates/create/next/prisma.compute.ts.hbs +9 -0
- package/templates/create/next/src/app/globals.css +122 -0
- package/templates/create/next/src/app/layout.tsx.hbs +2 -2
- package/templates/create/next/src/app/page.tsx.hbs +58 -34
- package/templates/create/next/src/lib/prisma.ts.hbs +4 -18
- package/templates/create/nuxt/README.md.hbs +11 -7
- package/templates/create/nuxt/app/pages/index.vue.hbs +175 -43
- package/templates/create/nuxt/deno.json.hbs +5 -0
- package/templates/create/nuxt/nuxt.config.ts +0 -1
- package/templates/create/nuxt/prisma/schema.prisma.hbs +5 -0
- package/templates/create/nuxt/prisma/seed.ts.hbs +5 -1
- package/templates/create/nuxt/prisma.compute.ts.hbs +9 -0
- package/templates/create/nuxt/server/api/users.get.ts.hbs +6 -0
- package/templates/create/nuxt/server/utils/prisma.ts.hbs +4 -18
- package/templates/create/svelte/README.md.hbs +13 -4
- package/templates/create/svelte/deno.json.hbs +5 -0
- package/templates/create/svelte/package.json.hbs +1 -2
- package/templates/create/svelte/prisma/schema.prisma.hbs +5 -0
- package/templates/create/svelte/prisma/seed.ts.hbs +5 -1
- package/templates/create/svelte/src/lib/server/prisma.ts.hbs +2 -18
- package/templates/create/svelte/src/routes/+layout.svelte +0 -1
- package/templates/create/svelte/src/routes/+page.server.ts.hbs +7 -1
- package/templates/create/svelte/src/routes/+page.svelte.hbs +297 -41
- package/templates/create/svelte/svelte.config.js +13 -0
- package/templates/create/svelte/vite.config.ts +0 -3
- package/templates/create/tanstack-start/README.md.hbs +11 -7
- package/templates/create/tanstack-start/deno.json.hbs +13 -0
- package/templates/create/tanstack-start/prisma/schema.prisma.hbs +5 -0
- package/templates/create/tanstack-start/prisma/seed.ts.hbs +5 -1
- package/templates/create/tanstack-start/prisma.compute.ts.hbs +9 -0
- package/templates/create/tanstack-start/src/lib/prisma.server.ts.hbs +4 -18
- package/templates/create/tanstack-start/src/routes/__root.tsx.hbs +29 -4
- package/templates/create/tanstack-start/src/routes/index.tsx.hbs +76 -35
- package/templates/create/tanstack-start/src/styles.css +146 -0
- package/templates/create/tanstack-start/tsconfig.json +1 -1
- package/templates/create/turborepo/README.md.hbs +9 -7
- package/templates/create/turborepo/apps/api/package.json.hbs +9 -2
- package/templates/create/turborepo/apps/api/src/index.ts.hbs +12 -28
- package/templates/create/turborepo/apps/api/tsconfig.json +0 -1
- package/templates/create/turborepo/deno.json.hbs +5 -0
- package/templates/create/turborepo/package.json.hbs +6 -0
- package/templates/create/turborepo/packages/db/package.json.hbs +5 -4
- package/templates/create/turborepo/packages/db/prisma/schema.prisma.hbs +5 -0
- package/templates/create/turborepo/packages/db/prisma/seed.ts.hbs +5 -1
- package/templates/create/turborepo/packages/db/src/client.ts.hbs +5 -2
- package/templates/create/turborepo/packages/db/tsconfig.json +0 -1
- package/templates/create/turborepo/prisma.compute.ts.hbs +14 -0
- package/templates/create/astro/module.ts.hbs +0 -30
- package/templates/create/astro/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/astro/service.ts.hbs +0 -19
- package/templates/create/elysia/.yarnrc.yml.hbs +0 -3
- package/templates/create/elysia/module.ts.hbs +0 -30
- package/templates/create/elysia/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/elysia/service.ts.hbs +0 -19
- package/templates/create/hono/module.ts.hbs +0 -30
- package/templates/create/hono/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/hono/service.ts.hbs +0 -19
- package/templates/create/nest/module.ts.hbs +0 -30
- package/templates/create/nest/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/nest/src/service.ts.hbs +0 -19
- package/templates/create/next/module.ts.hbs +0 -30
- package/templates/create/next/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/next/service.ts.hbs +0 -19
- package/templates/create/nuxt/module.ts.hbs +0 -30
- package/templates/create/nuxt/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/nuxt/service.ts.hbs +0 -19
- package/templates/create/svelte/module.ts.hbs +0 -30
- package/templates/create/svelte/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/svelte/service.ts.hbs +0 -19
- package/templates/create/svelte/svelte.config.js.hbs +0 -10
- package/templates/create/tanstack-start/module.ts.hbs +0 -30
- package/templates/create/tanstack-start/prisma-composer.config.ts.hbs +0 -8
- package/templates/create/tanstack-start/service.ts.hbs +0 -19
- package/templates/create/turborepo/apps/api/src/service.ts.hbs +0 -19
- package/templates/create/turborepo/module.ts.hbs +0 -30
- package/templates/create/turborepo/prisma-composer.config.ts.hbs +0 -8
- package/templates/shared/setup-composer-postgres.mjs.hbs +0 -179
- package/templates/shared/starter.css +0 -276
package/dist/index.mjs
CHANGED
|
@@ -1,2291 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { cancel, confirm, intro, isCancel, log, multiselect, outro, select, spinner, text } from "@clack/prompts";
|
|
5
|
-
import fs from "fs-extra";
|
|
6
|
-
import path from "node:path";
|
|
7
|
-
import { isMap, isScalar, parseDocument } from "yaml";
|
|
8
|
-
import Handlebars from "handlebars";
|
|
9
|
-
import { existsSync } from "node:fs";
|
|
10
|
-
import { fileURLToPath } from "node:url";
|
|
11
|
-
import { z } from "zod";
|
|
12
|
-
import { execa } from "execa";
|
|
13
|
-
import { randomUUID } from "node:crypto";
|
|
14
|
-
import os$1 from "node:os";
|
|
15
|
-
import { PostHog } from "posthog-node";
|
|
16
|
-
import { styleText } from "node:util";
|
|
17
|
-
//#region src/utils/regexp.ts
|
|
18
|
-
function escapeRegExp(value) {
|
|
19
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
20
|
-
}
|
|
21
|
-
//#endregion
|
|
22
|
-
//#region src/utils/runtime.ts
|
|
23
|
-
function usesNodeStyleRuntime(packageManager) {
|
|
24
|
-
return packageManager !== void 0 && packageManager !== "bun";
|
|
25
|
-
}
|
|
26
|
-
function requiresDotenvConfigImport(packageManager) {
|
|
27
|
-
return usesNodeStyleRuntime(packageManager);
|
|
28
|
-
}
|
|
29
|
-
function requiresPrismaConfigDotenvImport(_packageManager) {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
//#endregion
|
|
33
|
-
//#region src/constants/dependencies.ts
|
|
34
|
-
const dependencyVersionMap = {
|
|
35
|
-
"@elysiajs/node": "^1.4.5",
|
|
36
|
-
"@libsql/client": "^0.17.4",
|
|
37
|
-
"@prisma/client": "^7.8.0",
|
|
38
|
-
"@prisma/adapter-pg": "^7.8.0",
|
|
39
|
-
"@prisma/adapter-libsql": "^7.8.0",
|
|
40
|
-
"@prisma/adapter-mariadb": "^7.8.0",
|
|
41
|
-
"@prisma/adapter-mssql": "^7.8.0",
|
|
42
|
-
"@prisma/composer": "0.6.0",
|
|
43
|
-
"@prisma/composer-prisma-cloud": "0.6.0",
|
|
44
|
-
"@types/node": "^26.0.0",
|
|
45
|
-
arktype: "^2.2.3",
|
|
46
|
-
dotenv: "^17.4.2",
|
|
47
|
-
effect: "4.0.0-beta.93",
|
|
48
|
-
esbuild: "^0.28.2",
|
|
49
|
-
prisma: "^7.8.0",
|
|
50
|
-
tsx: "^4.22.4"
|
|
51
|
-
};
|
|
52
|
-
const prismaPlatformCliPackage = "@prisma/cli@next";
|
|
53
|
-
function getWorkspaceDependencyVersion(packageManager) {
|
|
54
|
-
return packageManager === "npm" ? "*" : "workspace:*";
|
|
55
|
-
}
|
|
56
|
-
function getCreateTemplateDependencies(template, packageManager) {
|
|
57
|
-
const targets = [];
|
|
58
|
-
if (template === "hono" || template === "elysia" || template === "nest") {
|
|
59
|
-
const runtimeDevDependencies = usesNodeStyleRuntime(packageManager) ? ["tsx"] : [];
|
|
60
|
-
if (template === "elysia") targets.push({
|
|
61
|
-
packageJsonPath: "package.json",
|
|
62
|
-
dependencies: ["@elysiajs/node"],
|
|
63
|
-
devDependencies: ["@types/node", ...runtimeDevDependencies]
|
|
64
|
-
});
|
|
65
|
-
else if (runtimeDevDependencies.length > 0) targets.push({
|
|
66
|
-
packageJsonPath: "package.json",
|
|
67
|
-
dependencies: [],
|
|
68
|
-
devDependencies: runtimeDevDependencies
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
if (template === "turborepo") targets.push({
|
|
72
|
-
packageJsonPath: "apps/api/package.json",
|
|
73
|
-
dependencies: ["dotenv"],
|
|
74
|
-
devDependencies: ["tsx"],
|
|
75
|
-
customDependencies: { "@repo/db": getWorkspaceDependencyVersion(packageManager) }
|
|
76
|
-
});
|
|
77
|
-
targets.push({
|
|
78
|
-
packageJsonPath: "package.json",
|
|
79
|
-
dependencies: [
|
|
80
|
-
"@prisma/composer",
|
|
81
|
-
"@prisma/composer-prisma-cloud",
|
|
82
|
-
"arktype",
|
|
83
|
-
"effect",
|
|
84
|
-
...template === "turborepo" ? ["dotenv"] : []
|
|
85
|
-
],
|
|
86
|
-
devDependencies: template === "hono" || template === "elysia" || template === "nest" ? ["esbuild"] : []
|
|
87
|
-
});
|
|
88
|
-
if (template === "turborepo") targets.push({
|
|
89
|
-
packageJsonPath: "apps/api/package.json",
|
|
90
|
-
dependencies: [],
|
|
91
|
-
devDependencies: ["esbuild"]
|
|
92
|
-
});
|
|
93
|
-
return targets;
|
|
94
|
-
}
|
|
95
|
-
//#endregion
|
|
96
|
-
//#region src/types.ts
|
|
97
|
-
const databaseProviders = [
|
|
98
|
-
"postgresql",
|
|
99
|
-
"mysql",
|
|
100
|
-
"sqlite",
|
|
101
|
-
"sqlserver",
|
|
102
|
-
"cockroachdb"
|
|
103
|
-
];
|
|
104
|
-
const packageManagers = [
|
|
105
|
-
"npm",
|
|
106
|
-
"pnpm",
|
|
107
|
-
"yarn",
|
|
108
|
-
"bun"
|
|
109
|
-
];
|
|
110
|
-
const createTemplates = [
|
|
111
|
-
"hono",
|
|
112
|
-
"elysia",
|
|
113
|
-
"nest",
|
|
114
|
-
"next",
|
|
115
|
-
"svelte",
|
|
116
|
-
"astro",
|
|
117
|
-
"nuxt",
|
|
118
|
-
"tanstack-start",
|
|
119
|
-
"turborepo"
|
|
120
|
-
];
|
|
121
|
-
const createAddons = [
|
|
122
|
-
"skills",
|
|
123
|
-
"mcp",
|
|
124
|
-
"extension"
|
|
125
|
-
];
|
|
126
|
-
const addonInstallScopes = ["project", "global"];
|
|
127
|
-
const extensionTargets = [
|
|
128
|
-
"vscode",
|
|
129
|
-
"cursor",
|
|
130
|
-
"windsurf"
|
|
131
|
-
];
|
|
132
|
-
const prismaSkillNames = [
|
|
133
|
-
"prisma-cli",
|
|
134
|
-
"prisma-client-api",
|
|
135
|
-
"prisma-compute",
|
|
136
|
-
"prisma-database-setup",
|
|
137
|
-
"prisma-upgrade-v7",
|
|
138
|
-
"prisma-postgres"
|
|
139
|
-
];
|
|
140
|
-
const DatabaseProviderSchema = z.enum(databaseProviders);
|
|
141
|
-
const PackageManagerSchema = z.enum(packageManagers);
|
|
142
|
-
const CreateTemplateSchema = z.enum(createTemplates);
|
|
143
|
-
z.enum(createAddons);
|
|
144
|
-
z.enum(addonInstallScopes);
|
|
145
|
-
z.enum(extensionTargets);
|
|
146
|
-
z.enum(prismaSkillNames);
|
|
147
|
-
const CommonCommandOptionsSchema = z.object({
|
|
148
|
-
yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
|
|
149
|
-
verbose: z.boolean().optional().describe("Show verbose command output during setup")
|
|
150
|
-
});
|
|
151
|
-
const PrismaSetupOptionsSchema = z.object({
|
|
152
|
-
provider: DatabaseProviderSchema.optional().describe("Database provider"),
|
|
153
|
-
packageManager: PackageManagerSchema.optional().describe("Package manager used for dependency installation"),
|
|
154
|
-
install: z.boolean().optional().describe("Install dependencies with selected package manager")
|
|
155
|
-
});
|
|
156
|
-
const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
|
|
157
|
-
const CreateScaffoldOptionsSchema = z.object({
|
|
158
|
-
name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
|
|
159
|
-
template: CreateTemplateSchema.optional().describe("Project template"),
|
|
160
|
-
skills: z.boolean().optional().describe("Enable skills addon"),
|
|
161
|
-
mcp: z.boolean().optional().describe("Enable MCP addon"),
|
|
162
|
-
extension: z.boolean().optional().describe("Enable extension addon"),
|
|
163
|
-
deploy: z.boolean().optional().describe("Deploy the scaffolded project with Prisma Composer"),
|
|
164
|
-
force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
|
|
165
|
-
});
|
|
166
|
-
const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
|
|
167
|
-
//#endregion
|
|
168
|
-
//#region src/utils/package-manager.ts
|
|
169
|
-
const packageManagerManifestValues = {
|
|
170
|
-
npm: "npm@11.17.0",
|
|
171
|
-
pnpm: "pnpm@11.8.0",
|
|
172
|
-
yarn: "yarn@4.17.0",
|
|
173
|
-
bun: "bun@1.3.14"
|
|
174
|
-
};
|
|
175
|
-
function parseUserAgent(userAgent) {
|
|
176
|
-
if (userAgent?.startsWith("pnpm")) return "pnpm";
|
|
177
|
-
if (userAgent?.startsWith("yarn")) return "yarn";
|
|
178
|
-
if (userAgent?.startsWith("bun")) return "bun";
|
|
179
|
-
if (userAgent?.startsWith("npm")) return "npm";
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
function parsePackageManagerField(packageManagerField) {
|
|
183
|
-
if (typeof packageManagerField !== "string" || packageManagerField.length === 0) return null;
|
|
184
|
-
const managerName = packageManagerField.split("@")[0];
|
|
185
|
-
const parsed = PackageManagerSchema.safeParse(managerName);
|
|
186
|
-
return parsed.success ? parsed.data : null;
|
|
187
|
-
}
|
|
188
|
-
async function detectFromPackageJson(projectDir) {
|
|
189
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
190
|
-
if (!await fs.pathExists(packageJsonPath)) return null;
|
|
191
|
-
return parsePackageManagerField((await fs.readJson(packageJsonPath)).packageManager);
|
|
192
|
-
}
|
|
193
|
-
async function detectFromLockfile(projectDir) {
|
|
194
|
-
for (const check of [
|
|
195
|
-
{
|
|
196
|
-
manager: "pnpm",
|
|
197
|
-
lockfile: "pnpm-lock.yaml"
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
manager: "yarn",
|
|
201
|
-
lockfile: "yarn.lock"
|
|
202
|
-
},
|
|
203
|
-
{
|
|
204
|
-
manager: "bun",
|
|
205
|
-
lockfile: "bun.lockb"
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
manager: "bun",
|
|
209
|
-
lockfile: "bun.lock"
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
manager: "npm",
|
|
213
|
-
lockfile: "package-lock.json"
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
manager: "npm",
|
|
217
|
-
lockfile: "npm-shrinkwrap.json"
|
|
218
|
-
}
|
|
219
|
-
]) if (await fs.pathExists(path.join(projectDir, check.lockfile))) return check.manager;
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
async function detectPackageManager(projectDir = process.cwd()) {
|
|
223
|
-
const fromPackageJson = await detectFromPackageJson(projectDir);
|
|
224
|
-
if (fromPackageJson) return fromPackageJson;
|
|
225
|
-
const fromLockfile = await detectFromLockfile(projectDir);
|
|
226
|
-
if (fromLockfile) return fromLockfile;
|
|
227
|
-
const fromUserAgent = parseUserAgent(process.env.npm_config_user_agent);
|
|
228
|
-
if (fromUserAgent) return fromUserAgent;
|
|
229
|
-
return "npm";
|
|
230
|
-
}
|
|
231
|
-
function getPackageManagerManifestValue(packageManager) {
|
|
232
|
-
if (!packageManager) return;
|
|
233
|
-
return packageManagerManifestValues[packageManager];
|
|
234
|
-
}
|
|
235
|
-
function getInstallCommand(packageManager) {
|
|
236
|
-
return `${packageManager} install`;
|
|
237
|
-
}
|
|
238
|
-
function getRunScriptCommand(packageManager, scriptName) {
|
|
239
|
-
const { command, args } = getRunScriptArgs(packageManager, scriptName);
|
|
240
|
-
return [command, ...args].join(" ");
|
|
241
|
-
}
|
|
242
|
-
function getRunScriptArgs(packageManager, scriptName) {
|
|
243
|
-
switch (packageManager) {
|
|
244
|
-
case "bun": return {
|
|
245
|
-
command: "bun",
|
|
246
|
-
args: ["run", scriptName]
|
|
247
|
-
};
|
|
248
|
-
case "pnpm": return {
|
|
249
|
-
command: "pnpm",
|
|
250
|
-
args: ["run", scriptName]
|
|
251
|
-
};
|
|
252
|
-
case "yarn": return {
|
|
253
|
-
command: "yarn",
|
|
254
|
-
args: ["run", scriptName]
|
|
255
|
-
};
|
|
256
|
-
default: return {
|
|
257
|
-
command: "npm",
|
|
258
|
-
args: ["run", scriptName]
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
function getRunScriptInDirectoryCommand(packageManager, directory, scriptName) {
|
|
263
|
-
switch (packageManager) {
|
|
264
|
-
case "bun": return `bun run --cwd ${directory} ${scriptName}`;
|
|
265
|
-
case "pnpm": return `pnpm --dir ${directory} run ${scriptName}`;
|
|
266
|
-
case "yarn": return `yarn --cwd ${directory} run ${scriptName}`;
|
|
267
|
-
default: return `npm --prefix ${directory} run ${scriptName}`;
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
function getRuntimeScriptCommand(packageManager, kind, options) {
|
|
271
|
-
const { sourceEntrypoint, builtEntrypoint, emit = false } = options;
|
|
272
|
-
if (packageManager === "bun") switch (kind) {
|
|
273
|
-
case "dev": return `bun --watch ${sourceEntrypoint}`;
|
|
274
|
-
case "build": return emit ? "tsc" : "tsc --noEmit";
|
|
275
|
-
case "start": return `bun ${sourceEntrypoint}`;
|
|
276
|
-
}
|
|
277
|
-
switch (kind) {
|
|
278
|
-
case "dev": return `tsx watch ${sourceEntrypoint}`;
|
|
279
|
-
case "build": return "tsc";
|
|
280
|
-
case "start": return builtEntrypoint ? `node ${builtEntrypoint}` : `tsx ${sourceEntrypoint}`;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
function getInstallArgs(packageManager) {
|
|
284
|
-
return {
|
|
285
|
-
command: packageManager,
|
|
286
|
-
args: ["install"]
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
function getPackageExecutionArgs(packageManager, commandArgs, options = {}) {
|
|
290
|
-
switch (packageManager) {
|
|
291
|
-
case "pnpm": return {
|
|
292
|
-
command: "pnpm",
|
|
293
|
-
args: [
|
|
294
|
-
...options.silent ? ["--silent"] : [],
|
|
295
|
-
"dlx",
|
|
296
|
-
...commandArgs
|
|
297
|
-
]
|
|
298
|
-
};
|
|
299
|
-
case "yarn": return {
|
|
300
|
-
command: "yarn",
|
|
301
|
-
args: [
|
|
302
|
-
"dlx",
|
|
303
|
-
...options.silent ? ["--quiet"] : [],
|
|
304
|
-
...commandArgs
|
|
305
|
-
]
|
|
306
|
-
};
|
|
307
|
-
case "bun": return {
|
|
308
|
-
command: "bunx",
|
|
309
|
-
args: [...options.silent ? ["--silent"] : [], ...commandArgs]
|
|
310
|
-
};
|
|
311
|
-
default: return {
|
|
312
|
-
command: "npx",
|
|
313
|
-
args: [...options.silent ? ["--yes"] : [], ...commandArgs]
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function getPackageExecutionCommand(packageManager, commandArgs, options = {}) {
|
|
318
|
-
const execution = getPackageExecutionArgs(packageManager, commandArgs, options);
|
|
319
|
-
return [execution.command, ...execution.args].join(" ");
|
|
320
|
-
}
|
|
321
|
-
function getPrismaCliArgs(packageManager, prismaArgs) {
|
|
322
|
-
if (packageManager === "bun") return {
|
|
323
|
-
command: "bunx",
|
|
324
|
-
args: [
|
|
325
|
-
"--bun",
|
|
326
|
-
"prisma",
|
|
327
|
-
...prismaArgs
|
|
328
|
-
]
|
|
329
|
-
};
|
|
330
|
-
if (packageManager === "pnpm") return {
|
|
331
|
-
command: "pnpm",
|
|
332
|
-
args: [
|
|
333
|
-
"exec",
|
|
334
|
-
"prisma",
|
|
335
|
-
...prismaArgs
|
|
336
|
-
]
|
|
337
|
-
};
|
|
338
|
-
if (packageManager === "yarn") return {
|
|
339
|
-
command: "yarn",
|
|
340
|
-
args: [
|
|
341
|
-
"exec",
|
|
342
|
-
"prisma",
|
|
343
|
-
...prismaArgs
|
|
344
|
-
]
|
|
345
|
-
};
|
|
346
|
-
return getPackageExecutionArgs(packageManager, ["prisma", ...prismaArgs]);
|
|
347
|
-
}
|
|
348
|
-
//#endregion
|
|
349
|
-
//#region src/templates/shared.ts
|
|
350
|
-
function getSeedCommand(packageManager) {
|
|
351
|
-
if (packageManager === "bun") return "bun ./prisma/seed.ts";
|
|
352
|
-
return "tsx ./prisma/seed.ts";
|
|
353
|
-
}
|
|
354
|
-
function getPrismaCommand(packageManager, subcommand) {
|
|
355
|
-
return `prisma ${subcommand}`;
|
|
356
|
-
}
|
|
357
|
-
Handlebars.registerHelper("eq", (left, right) => left === right);
|
|
358
|
-
Handlebars.registerHelper("composerPlatformCliInvocation", (packageManager) => packageManager ? JSON.stringify(getPackageExecutionArgs(packageManager, [prismaPlatformCliPackage], { silent: true })) : "{}");
|
|
359
|
-
Handlebars.registerHelper("prismaAuthLoginCommand", (packageManager) => packageManager ? getPackageExecutionCommand(packageManager, [
|
|
360
|
-
prismaPlatformCliPackage,
|
|
361
|
-
"auth",
|
|
362
|
-
"login"
|
|
363
|
-
], { silent: true }) : "");
|
|
364
|
-
Handlebars.registerHelper("prismaCliInvocation", (packageManager) => packageManager ? JSON.stringify(getPrismaCliArgs(packageManager, [])) : "{}");
|
|
365
|
-
Handlebars.registerHelper("runScriptCommand", (packageManager, scriptName) => packageManager ? getRunScriptCommand(packageManager, scriptName) : "");
|
|
366
|
-
Handlebars.registerHelper("runScriptInDirectoryCommand", (packageManager, directory, scriptName) => packageManager ? getRunScriptInDirectoryCommand(packageManager, directory, scriptName) : "");
|
|
367
|
-
Handlebars.registerHelper("packageManagerManifestValue", (packageManager) => getPackageManagerManifestValue(packageManager) ?? "");
|
|
368
|
-
Handlebars.registerHelper("requiresDotenvConfigImport", (packageManager) => requiresDotenvConfigImport(packageManager));
|
|
369
|
-
Handlebars.registerHelper("requiresPrismaConfigDotenvImport", (packageManager) => requiresPrismaConfigDotenvImport(packageManager));
|
|
370
|
-
Handlebars.registerHelper("sqliteAdapterPackage", () => "@prisma/adapter-libsql");
|
|
371
|
-
Handlebars.registerHelper("sqliteAdapterClass", () => "PrismaLibSql");
|
|
372
|
-
Handlebars.registerHelper("seedCommand", (packageManager) => getSeedCommand(packageManager));
|
|
373
|
-
Handlebars.registerHelper("prismaCommand", (packageManager, subcommand) => getPrismaCommand(packageManager, subcommand));
|
|
374
|
-
Handlebars.registerHelper("runtimeScript", (packageManager, kind, sourceEntrypoint, builtEntrypoint, options) => {
|
|
375
|
-
if (!packageManager) return "";
|
|
376
|
-
const hash = options.hash;
|
|
377
|
-
return getRuntimeScriptCommand(packageManager, kind, {
|
|
378
|
-
sourceEntrypoint,
|
|
379
|
-
builtEntrypoint,
|
|
380
|
-
emit: hash.emit === true
|
|
381
|
-
});
|
|
382
|
-
});
|
|
383
|
-
function findPackageRoot(startDir) {
|
|
384
|
-
let currentDir = startDir;
|
|
385
|
-
while (true) {
|
|
386
|
-
if (existsSync(path.join(currentDir, "package.json"))) return currentDir;
|
|
387
|
-
const parentDir = path.dirname(currentDir);
|
|
388
|
-
if (parentDir === currentDir) break;
|
|
389
|
-
currentDir = parentDir;
|
|
390
|
-
}
|
|
391
|
-
throw new Error(`Unable to locate package root from: ${startDir}`);
|
|
392
|
-
}
|
|
393
|
-
function resolveTemplatesDir(relativeTemplatesDir) {
|
|
394
|
-
const currentFilePath = fileURLToPath(import.meta.url);
|
|
395
|
-
const packageRoot = findPackageRoot(path.dirname(currentFilePath));
|
|
396
|
-
const templatePath = path.join(packageRoot, relativeTemplatesDir);
|
|
397
|
-
if (!existsSync(templatePath)) throw new Error(`Template directory not found at: ${templatePath}`);
|
|
398
|
-
return templatePath;
|
|
399
|
-
}
|
|
400
|
-
async function getTemplateFilesRecursively(dir) {
|
|
401
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
402
|
-
return (await Promise.all(entries.map(async (entry) => {
|
|
403
|
-
const entryPath = path.join(dir, entry.name);
|
|
404
|
-
if (entry.isDirectory()) return getTemplateFilesRecursively(entryPath);
|
|
405
|
-
if (!entry.isFile()) return [];
|
|
406
|
-
return [entryPath];
|
|
407
|
-
}))).flat();
|
|
408
|
-
}
|
|
409
|
-
function stripHbsExtension(filePath) {
|
|
410
|
-
return filePath.endsWith(".hbs") ? filePath.slice(0, -4) : filePath;
|
|
411
|
-
}
|
|
412
|
-
function ensureTrailingNewline(content) {
|
|
413
|
-
return content.endsWith("\n") ? content : `${content}\n`;
|
|
414
|
-
}
|
|
415
|
-
async function renderTemplateFile(opts) {
|
|
416
|
-
const { templateFilePath, outputPath, context } = opts;
|
|
417
|
-
const templateContent = await fs.readFile(templateFilePath, "utf8");
|
|
418
|
-
const outputContent = templateFilePath.endsWith(".hbs") ? Handlebars.compile(templateContent, {
|
|
419
|
-
noEscape: true,
|
|
420
|
-
strict: true
|
|
421
|
-
})(context) : templateContent;
|
|
422
|
-
if (templateFilePath.endsWith(".hbs") && outputContent.trim().length === 0) return;
|
|
423
|
-
await fs.outputFile(outputPath, ensureTrailingNewline(outputContent), "utf8");
|
|
424
|
-
}
|
|
425
|
-
async function renderTemplateTree(opts) {
|
|
426
|
-
const { templateRoot, outputDir, context } = opts;
|
|
427
|
-
const templateFiles = await getTemplateFilesRecursively(templateRoot);
|
|
428
|
-
for (const templateFilePath of templateFiles) {
|
|
429
|
-
const relativeOutputPath = stripHbsExtension(path.relative(templateRoot, templateFilePath));
|
|
430
|
-
await renderTemplateFile({
|
|
431
|
-
templateFilePath,
|
|
432
|
-
outputPath: path.join(outputDir, relativeOutputPath),
|
|
433
|
-
context
|
|
434
|
-
});
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
//#endregion
|
|
438
|
-
//#region src/templates/render-create-template.ts
|
|
439
|
-
const starterStyleOutputPaths = {
|
|
440
|
-
astro: "src/styles.css",
|
|
441
|
-
next: "src/app/globals.css",
|
|
442
|
-
nuxt: "app/assets/css/main.css",
|
|
443
|
-
svelte: "src/app.css",
|
|
444
|
-
"tanstack-start": "src/styles.css"
|
|
445
|
-
};
|
|
446
|
-
function getCreateTemplateDir(template) {
|
|
447
|
-
return resolveTemplatesDir(`templates/create/${template}`);
|
|
448
|
-
}
|
|
449
|
-
function createTemplateContext(projectName, template, provider, packageManager, composerPostgres) {
|
|
450
|
-
return {
|
|
451
|
-
projectName,
|
|
452
|
-
template,
|
|
453
|
-
provider,
|
|
454
|
-
packageManager,
|
|
455
|
-
composerPostgres
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
const pnpmAllowedBuilds = [
|
|
459
|
-
"@prisma/engines",
|
|
460
|
-
"@parcel/watcher",
|
|
461
|
-
"esbuild",
|
|
462
|
-
"msgpackr-extract",
|
|
463
|
-
"prisma",
|
|
464
|
-
"sharp",
|
|
465
|
-
"unrs-resolver",
|
|
466
|
-
"workerd"
|
|
467
|
-
];
|
|
468
|
-
const composerEffectOverrides = {
|
|
469
|
-
"@effect/platform-bun": "4.0.0-beta.93",
|
|
470
|
-
"@effect/platform-node": "4.0.0-beta.93",
|
|
471
|
-
"@effect/platform-node-shared": "4.0.0-beta.93",
|
|
472
|
-
"@effect/sql-d1": "4.0.0-beta.93",
|
|
473
|
-
"@effect/sql-pg": "4.0.0-beta.93",
|
|
474
|
-
"@effect/vitest": "4.0.0-beta.93"
|
|
475
|
-
};
|
|
476
|
-
function renderPnpmAllowBuildLine(packageName) {
|
|
477
|
-
return ` ${packageName.startsWith("@") ? JSON.stringify(packageName) : packageName}: true`;
|
|
478
|
-
}
|
|
479
|
-
function renderPnpmAllowBuilds() {
|
|
480
|
-
return ["allowBuilds:", ...pnpmAllowedBuilds.map(renderPnpmAllowBuildLine)].join("\n");
|
|
481
|
-
}
|
|
482
|
-
function hasPnpmAllowBuild(content, packageName) {
|
|
483
|
-
const key = escapeRegExp(packageName);
|
|
484
|
-
return new RegExp(`^\\s*["']?${key}["']?\\s*:\\s*true\\s*$`, "m").test(content);
|
|
485
|
-
}
|
|
486
|
-
function mergePnpmAllowBuilds(content) {
|
|
487
|
-
const missingBuilds = pnpmAllowedBuilds.filter((packageName) => !hasPnpmAllowBuild(content, packageName));
|
|
488
|
-
if (missingBuilds.length === 0) return content;
|
|
489
|
-
const missingLines = missingBuilds.map(renderPnpmAllowBuildLine);
|
|
490
|
-
const trimmedContent = content.trimEnd();
|
|
491
|
-
const lines = trimmedContent.length > 0 ? trimmedContent.split("\n") : [];
|
|
492
|
-
const allowBuildsIndex = lines.findIndex((line) => /^allowBuilds:\s*$/.test(line));
|
|
493
|
-
if (allowBuildsIndex === -1) {
|
|
494
|
-
const allowBuilds = ["allowBuilds:", ...missingLines].join("\n");
|
|
495
|
-
return trimmedContent.length > 0 ? `${trimmedContent}\n\n${allowBuilds}\n` : `${allowBuilds}\n`;
|
|
496
|
-
}
|
|
497
|
-
lines.splice(allowBuildsIndex + 1, 0, ...missingLines);
|
|
498
|
-
return `${lines.join("\n")}\n`;
|
|
499
|
-
}
|
|
500
|
-
function mergePnpmComposerOverrides(content) {
|
|
501
|
-
const document = parseDocument(content);
|
|
502
|
-
if (document.errors.length > 0) throw new Error(`Could not update pnpm-workspace.yaml: ${document.errors[0]?.message}`);
|
|
503
|
-
if (!document.has("overrides")) document.set("overrides", document.createNode({}));
|
|
504
|
-
let overrides = document.get("overrides", true);
|
|
505
|
-
if (isScalar(overrides) && overrides.value === null) {
|
|
506
|
-
document.set("overrides", document.createNode({}));
|
|
507
|
-
overrides = document.get("overrides", true);
|
|
508
|
-
}
|
|
509
|
-
if (!isMap(overrides)) throw new Error("Could not update pnpm-workspace.yaml: \"overrides\" must be a mapping.");
|
|
510
|
-
const missingOverrides = Object.entries(composerEffectOverrides).filter(([packageName]) => !overrides.has(packageName));
|
|
511
|
-
if (missingOverrides.length === 0) return content;
|
|
512
|
-
for (const [packageName, version] of missingOverrides) overrides.set(packageName, version);
|
|
513
|
-
return document.toString();
|
|
514
|
-
}
|
|
515
|
-
async function ensurePnpmWorkspace(projectDir) {
|
|
516
|
-
const workspacePath = path.join(projectDir, "pnpm-workspace.yaml");
|
|
517
|
-
if (!await fs.pathExists(workspacePath)) {
|
|
518
|
-
const content = mergePnpmComposerOverrides(`${renderPnpmAllowBuilds()}\n`);
|
|
519
|
-
await fs.writeFile(workspacePath, content, "utf8");
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
const existingContent = await fs.readFile(workspacePath, "utf8");
|
|
523
|
-
const nextContent = mergePnpmComposerOverrides(mergePnpmAllowBuilds(existingContent));
|
|
524
|
-
if (nextContent !== existingContent) await fs.writeFile(workspacePath, nextContent, "utf8");
|
|
525
|
-
}
|
|
526
|
-
async function scaffoldCreateTemplate(opts) {
|
|
527
|
-
const { projectDir, projectName, template, provider, packageManager } = opts;
|
|
528
|
-
const templateRoot = getCreateTemplateDir(template);
|
|
529
|
-
const context = createTemplateContext(projectName, template, provider, packageManager, opts.composerPostgres === true);
|
|
530
|
-
await renderTemplateTree({
|
|
531
|
-
templateRoot,
|
|
532
|
-
outputDir: projectDir,
|
|
533
|
-
context
|
|
534
|
-
});
|
|
535
|
-
const starterStyleOutputPath = starterStyleOutputPaths[template];
|
|
536
|
-
if (starterStyleOutputPath) await renderTemplateFile({
|
|
537
|
-
templateFilePath: resolveTemplatesDir("templates/shared/starter.css"),
|
|
538
|
-
outputPath: path.join(projectDir, starterStyleOutputPath),
|
|
539
|
-
context
|
|
540
|
-
});
|
|
541
|
-
if (context.composerPostgres) await renderTemplateFile({
|
|
542
|
-
templateFilePath: resolveTemplatesDir("templates/shared/setup-composer-postgres.mjs.hbs"),
|
|
543
|
-
outputPath: path.join(projectDir, "scripts/setup-composer-postgres.mjs"),
|
|
544
|
-
context
|
|
545
|
-
});
|
|
546
|
-
if (packageManager === "pnpm") await ensurePnpmWorkspace(projectDir);
|
|
547
|
-
}
|
|
548
|
-
//#endregion
|
|
549
|
-
//#region src/constants/db-packages.ts
|
|
550
|
-
function getDbPackages(provider, _packageManager) {
|
|
551
|
-
switch (provider) {
|
|
552
|
-
case "postgresql":
|
|
553
|
-
case "cockroachdb": return "@prisma/adapter-pg";
|
|
554
|
-
case "mysql": return "@prisma/adapter-mariadb";
|
|
555
|
-
case "sqlite": return "@prisma/adapter-libsql";
|
|
556
|
-
case "sqlserver": return "@prisma/adapter-mssql";
|
|
557
|
-
default: throw new Error(`Unsupported database provider: ${String(provider)}`);
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
//#endregion
|
|
561
|
-
//#region src/tasks/install.ts
|
|
562
|
-
function getPrismaScriptMap() {
|
|
563
|
-
return {
|
|
564
|
-
"db:generate": "prisma generate",
|
|
565
|
-
"db:push": "prisma db push",
|
|
566
|
-
"db:migrate": "prisma migrate dev",
|
|
567
|
-
"db:migrate:deploy": "prisma migrate deploy",
|
|
568
|
-
"db:seed": "prisma db seed"
|
|
569
|
-
};
|
|
570
|
-
}
|
|
571
|
-
function getVersion(packageName) {
|
|
572
|
-
return dependencyVersionMap[packageName];
|
|
573
|
-
}
|
|
574
|
-
function unique(items) {
|
|
575
|
-
return [...new Set(items)];
|
|
576
|
-
}
|
|
577
|
-
function sortRecord(record) {
|
|
578
|
-
return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
|
|
579
|
-
}
|
|
580
|
-
async function projectContainsText(projectDir, text) {
|
|
581
|
-
const directories = [projectDir];
|
|
582
|
-
while (directories.length > 0) {
|
|
583
|
-
const currentDirectory = directories.pop();
|
|
584
|
-
if (!currentDirectory) continue;
|
|
585
|
-
const entries = await fs.readdir(currentDirectory, { withFileTypes: true });
|
|
586
|
-
for (const entry of entries) {
|
|
587
|
-
if (entry.name === "node_modules" || entry.name === ".git") continue;
|
|
588
|
-
const entryPath = path.join(currentDirectory, entry.name);
|
|
589
|
-
if (entry.isDirectory()) {
|
|
590
|
-
directories.push(entryPath);
|
|
591
|
-
continue;
|
|
592
|
-
}
|
|
593
|
-
if (!entry.isFile() || !/\.(c|m)?[jt]sx?$/.test(entry.name)) continue;
|
|
594
|
-
if ((await fs.readFile(entryPath, "utf8")).includes(text)) return true;
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return false;
|
|
598
|
-
}
|
|
599
|
-
async function addPackageDependency(opts) {
|
|
600
|
-
const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir } = opts;
|
|
601
|
-
const pkgJsonPath = path.join(projectDir, "package.json");
|
|
602
|
-
if (!await fs.pathExists(pkgJsonPath)) throw new Error(`No package.json found in ${projectDir}. Run this command inside an existing JavaScript/TypeScript project.`);
|
|
603
|
-
const pkgJson = await fs.readJson(pkgJsonPath);
|
|
604
|
-
if (!pkgJson.dependencies) pkgJson.dependencies = {};
|
|
605
|
-
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
606
|
-
if (!pkgJson.scripts) pkgJson.scripts = {};
|
|
607
|
-
for (const pkgName of unique(dependencies)) {
|
|
608
|
-
const version = getVersion(pkgName);
|
|
609
|
-
if (version) pkgJson.dependencies[pkgName] = version;
|
|
610
|
-
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
611
|
-
}
|
|
612
|
-
for (const pkgName of unique(devDependencies)) {
|
|
613
|
-
const version = getVersion(pkgName);
|
|
614
|
-
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
615
|
-
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
616
|
-
}
|
|
617
|
-
for (const [pkgName, version] of Object.entries(customDependencies)) pkgJson.dependencies[pkgName] = version;
|
|
618
|
-
for (const [scriptName, command] of Object.entries(scripts)) {
|
|
619
|
-
if (scriptMode === "if-missing") {
|
|
620
|
-
if (typeof pkgJson.scripts[scriptName] !== "string" || pkgJson.scripts[scriptName].trim().length === 0) pkgJson.scripts[scriptName] = command;
|
|
621
|
-
continue;
|
|
622
|
-
}
|
|
623
|
-
pkgJson.scripts[scriptName] = command;
|
|
624
|
-
}
|
|
625
|
-
pkgJson.dependencies = sortRecord(pkgJson.dependencies);
|
|
626
|
-
pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
|
|
627
|
-
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
628
|
-
}
|
|
629
|
-
async function writePrismaDependencies(provider, packageManager, projectDir = process.cwd()) {
|
|
630
|
-
const dependencies = ["@prisma/client"];
|
|
631
|
-
const devDependencies = ["prisma"];
|
|
632
|
-
dependencies.push(getDbPackages(provider, packageManager));
|
|
633
|
-
if (provider === "sqlite") dependencies.push("@libsql/client");
|
|
634
|
-
if (requiresDotenvConfigImport(packageManager) || await projectContainsText(projectDir, "dotenv/config")) dependencies.push("dotenv");
|
|
635
|
-
await addPackageDependency({
|
|
636
|
-
dependencies,
|
|
637
|
-
devDependencies,
|
|
638
|
-
scripts: getPrismaScriptMap(),
|
|
639
|
-
scriptMode: "if-missing",
|
|
640
|
-
projectDir
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
async function writeCreateTemplateDependencies(opts) {
|
|
644
|
-
const { template, packageManager, projectDir = process.cwd() } = opts;
|
|
645
|
-
const targets = getCreateTemplateDependencies(template, packageManager);
|
|
646
|
-
for (const dependencyTarget of targets) {
|
|
647
|
-
const targetDirectory = path.join(projectDir, path.dirname(dependencyTarget.packageJsonPath));
|
|
648
|
-
await addPackageDependency({
|
|
649
|
-
dependencies: dependencyTarget.dependencies,
|
|
650
|
-
devDependencies: dependencyTarget.devDependencies,
|
|
651
|
-
customDependencies: dependencyTarget.customDependencies,
|
|
652
|
-
projectDir: targetDirectory
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
const packageJsonPath = path.join(projectDir, "package.json");
|
|
656
|
-
const pkgJson = await fs.readJson(packageJsonPath);
|
|
657
|
-
const effectVersions = {
|
|
658
|
-
"@effect/platform-bun": "4.0.0-beta.93",
|
|
659
|
-
"@effect/platform-node": "4.0.0-beta.93",
|
|
660
|
-
"@effect/platform-node-shared": "4.0.0-beta.93",
|
|
661
|
-
"@effect/sql-d1": "4.0.0-beta.93",
|
|
662
|
-
"@effect/sql-pg": "4.0.0-beta.93",
|
|
663
|
-
"@effect/vitest": "4.0.0-beta.93"
|
|
664
|
-
};
|
|
665
|
-
if (packageManager === "yarn") pkgJson.resolutions = {
|
|
666
|
-
...pkgJson.resolutions ?? {},
|
|
667
|
-
...effectVersions
|
|
668
|
-
};
|
|
669
|
-
else if (packageManager !== "pnpm") pkgJson.overrides = {
|
|
670
|
-
...pkgJson.overrides ?? {},
|
|
671
|
-
...effectVersions
|
|
672
|
-
};
|
|
673
|
-
pkgJson.engines = {
|
|
674
|
-
...pkgJson.engines ?? {},
|
|
675
|
-
node: ">=24"
|
|
676
|
-
};
|
|
677
|
-
await fs.writeJson(packageJsonPath, pkgJson, { spaces: 2 });
|
|
678
|
-
}
|
|
679
|
-
async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
|
|
680
|
-
const verbose = options.verbose === true;
|
|
681
|
-
const installCommand = getInstallArgs(packageManager);
|
|
682
|
-
const env = packageManager === "yarn" ? {
|
|
683
|
-
...process.env,
|
|
684
|
-
YARN_ENABLE_IMMUTABLE_INSTALLS: "false"
|
|
685
|
-
} : void 0;
|
|
686
|
-
await execa(installCommand.command, installCommand.args, {
|
|
687
|
-
cwd: projectDir,
|
|
688
|
-
env,
|
|
689
|
-
stdio: verbose ? "inherit" : "pipe"
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
//#endregion
|
|
693
|
-
//#region src/tasks/setup-prisma.ts
|
|
694
|
-
const DEFAULT_DATABASE_PROVIDER = "postgresql";
|
|
695
|
-
const DEFAULT_INSTALL = true;
|
|
696
|
-
const INITIAL_MIGRATION_NAME = "00000000000000_init";
|
|
697
|
-
const requiredPrismaFileGroups = [
|
|
698
|
-
["prisma/schema.prisma", "packages/db/prisma/schema.prisma"],
|
|
699
|
-
["prisma/seed.ts", "packages/db/prisma/seed.ts"],
|
|
700
|
-
["prisma.config.ts", "packages/db/prisma.config.ts"],
|
|
701
|
-
[
|
|
702
|
-
"src/lib/prisma.ts",
|
|
703
|
-
"src/lib/prisma.server.ts",
|
|
704
|
-
"src/lib/server/prisma.ts",
|
|
705
|
-
"server/utils/prisma.ts",
|
|
706
|
-
"packages/db/src/client.ts"
|
|
707
|
-
]
|
|
708
|
-
];
|
|
709
|
-
async function resolvePrismaProjectDir(projectDir) {
|
|
710
|
-
const monorepoDbDir = path.join(projectDir, "packages/db");
|
|
711
|
-
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/schema.prisma"))) return monorepoDbDir;
|
|
712
|
-
return projectDir;
|
|
713
|
-
}
|
|
714
|
-
async function promptForDatabaseProvider() {
|
|
715
|
-
const databaseProvider = await select({
|
|
716
|
-
message: "Select your database",
|
|
717
|
-
initialValue: DEFAULT_DATABASE_PROVIDER,
|
|
718
|
-
options: [
|
|
719
|
-
{
|
|
720
|
-
value: "postgresql",
|
|
721
|
-
label: "PostgreSQL",
|
|
722
|
-
hint: "Default"
|
|
723
|
-
},
|
|
724
|
-
{
|
|
725
|
-
value: "mysql",
|
|
726
|
-
label: "MySQL"
|
|
727
|
-
},
|
|
728
|
-
{
|
|
729
|
-
value: "sqlite",
|
|
730
|
-
label: "SQLite"
|
|
731
|
-
},
|
|
732
|
-
{
|
|
733
|
-
value: "sqlserver",
|
|
734
|
-
label: "SQL Server"
|
|
735
|
-
},
|
|
736
|
-
{
|
|
737
|
-
value: "cockroachdb",
|
|
738
|
-
label: "CockroachDB"
|
|
739
|
-
}
|
|
740
|
-
]
|
|
741
|
-
});
|
|
742
|
-
if (isCancel(databaseProvider)) {
|
|
743
|
-
cancel("Operation cancelled.");
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
return DatabaseProviderSchema.parse(databaseProvider);
|
|
747
|
-
}
|
|
748
|
-
function getPackageManagerHint(option, detected) {
|
|
749
|
-
if (option === detected) return "Detected";
|
|
750
|
-
if (option === "bun") return "Fast runtime + package manager";
|
|
751
|
-
}
|
|
752
|
-
async function promptForPackageManager(detectedPackageManager) {
|
|
753
|
-
const packageManager = await select({
|
|
754
|
-
message: "Choose package manager",
|
|
755
|
-
initialValue: detectedPackageManager,
|
|
756
|
-
options: [
|
|
757
|
-
{
|
|
758
|
-
value: "npm",
|
|
759
|
-
label: "npm",
|
|
760
|
-
hint: getPackageManagerHint("npm", detectedPackageManager)
|
|
761
|
-
},
|
|
762
|
-
{
|
|
763
|
-
value: "pnpm",
|
|
764
|
-
label: "pnpm",
|
|
765
|
-
hint: getPackageManagerHint("pnpm", detectedPackageManager)
|
|
766
|
-
},
|
|
767
|
-
{
|
|
768
|
-
value: "yarn",
|
|
769
|
-
label: "yarn",
|
|
770
|
-
hint: getPackageManagerHint("yarn", detectedPackageManager)
|
|
771
|
-
},
|
|
772
|
-
{
|
|
773
|
-
value: "bun",
|
|
774
|
-
label: "bun",
|
|
775
|
-
hint: getPackageManagerHint("bun", detectedPackageManager)
|
|
776
|
-
}
|
|
777
|
-
]
|
|
778
|
-
});
|
|
779
|
-
if (isCancel(packageManager)) {
|
|
780
|
-
cancel("Operation cancelled.");
|
|
781
|
-
return;
|
|
782
|
-
}
|
|
783
|
-
return PackageManagerSchema.parse(packageManager);
|
|
784
|
-
}
|
|
785
|
-
async function promptForDependencyInstall(packageManager) {
|
|
786
|
-
const shouldInstall = await confirm({
|
|
787
|
-
message: `Install dependencies now with ${getInstallCommand(packageManager)}?`,
|
|
788
|
-
initialValue: true
|
|
789
|
-
});
|
|
790
|
-
if (isCancel(shouldInstall)) {
|
|
791
|
-
cancel("Operation cancelled.");
|
|
792
|
-
return;
|
|
793
|
-
}
|
|
794
|
-
return Boolean(shouldInstall);
|
|
795
|
-
}
|
|
796
|
-
function getCommandErrorMessage(error) {
|
|
797
|
-
if (error instanceof Error && "stderr" in error) {
|
|
798
|
-
const stderr = String(error.stderr ?? "").trim();
|
|
799
|
-
if (stderr.length > 0) return stderr;
|
|
800
|
-
}
|
|
801
|
-
return error instanceof Error ? error.message : String(error);
|
|
802
|
-
}
|
|
803
|
-
async function collectPrismaSetupInitialContext(input, options = {}) {
|
|
804
|
-
const projectDir = path.resolve(options.projectDir ?? process.cwd());
|
|
805
|
-
const useDefaults = input.yes === true;
|
|
806
|
-
const verbose = input.verbose === true;
|
|
807
|
-
const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
|
|
808
|
-
if (!databaseProvider) return;
|
|
809
|
-
const databaseUrl = (process.env.DATABASE_URL ?? "").trim() || void 0;
|
|
810
|
-
const detectedPackageManager = await detectPackageManager(projectDir);
|
|
811
|
-
const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
|
|
812
|
-
if (!packageManager) return;
|
|
813
|
-
const shouldInstall = input.install ?? (useDefaults ? DEFAULT_INSTALL : await promptForDependencyInstall(packageManager));
|
|
814
|
-
if (shouldInstall === void 0) return;
|
|
815
|
-
return {
|
|
816
|
-
projectDir,
|
|
817
|
-
verbose,
|
|
818
|
-
databaseProvider,
|
|
819
|
-
databaseUrl,
|
|
820
|
-
packageManager,
|
|
821
|
-
shouldInstall
|
|
822
|
-
};
|
|
823
|
-
}
|
|
824
|
-
async function completePrismaSetupContext(context, options = {}) {
|
|
825
|
-
const shouldUsePrismaPostgres = context.databaseProvider === "postgresql";
|
|
826
|
-
return {
|
|
827
|
-
...context,
|
|
828
|
-
shouldUsePrismaPostgres,
|
|
829
|
-
shouldMigrateAndSeed: options.shouldMigrateAndSeed === true
|
|
830
|
-
};
|
|
831
|
-
}
|
|
832
|
-
function getDefaultDatabaseUrl(provider) {
|
|
833
|
-
switch (provider) {
|
|
834
|
-
case "postgresql": return "postgresql://johndoe:randompassword@localhost:5432/mydb?schema=public";
|
|
835
|
-
case "cockroachdb": return "postgresql://johndoe:randompassword@localhost:26257/mydb?schema=public";
|
|
836
|
-
case "mysql": return "mysql://johndoe:randompassword@localhost:3306/mydb";
|
|
837
|
-
case "sqlite": return "file:./dev.db";
|
|
838
|
-
case "sqlserver": return "sqlserver://localhost:1433;database=mydb;user=SA;password=randompassword;";
|
|
839
|
-
default: throw new Error(`Unsupported provider: ${String(provider)}`);
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
function hasGitignoreEntry(content, entry) {
|
|
843
|
-
const escapedEntry = escapeRegExp(entry);
|
|
844
|
-
const escapedWithLeadingSlash = escapeRegExp(`/${entry}`);
|
|
845
|
-
const escapedWithTrailingSlash = escapeRegExp(`${entry}/`);
|
|
846
|
-
const escapedWithLeadingAndTrailingSlash = escapeRegExp(`/${entry}/`);
|
|
847
|
-
return new RegExp(`(^|\\n)\\s*(?:${escapedEntry}|${escapedWithLeadingSlash}|${escapedWithTrailingSlash}|${escapedWithLeadingAndTrailingSlash})\\s*(?=\\n|$)`).test(content);
|
|
848
|
-
}
|
|
849
|
-
async function ensureGitignoreEntry(projectDir, entry) {
|
|
850
|
-
const gitignorePath = path.join(projectDir, ".gitignore");
|
|
851
|
-
if (!await fs.pathExists(gitignorePath)) {
|
|
852
|
-
await fs.writeFile(gitignorePath, `${entry}\n`, "utf8");
|
|
853
|
-
return;
|
|
854
|
-
}
|
|
855
|
-
const existingContent = await fs.readFile(gitignorePath, "utf8");
|
|
856
|
-
if (hasGitignoreEntry(existingContent, entry)) return;
|
|
857
|
-
const separator = existingContent.endsWith("\n") ? "" : "\n";
|
|
858
|
-
await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
|
|
859
|
-
}
|
|
860
|
-
async function ensureRequiredPrismaFiles(projectDir) {
|
|
861
|
-
const missingFiles = [];
|
|
862
|
-
for (const candidates of requiredPrismaFileGroups) {
|
|
863
|
-
let foundCandidate = false;
|
|
864
|
-
for (const relativePath of candidates) {
|
|
865
|
-
const absolutePath = path.join(projectDir, relativePath);
|
|
866
|
-
if (await fs.pathExists(absolutePath)) {
|
|
867
|
-
foundCandidate = true;
|
|
868
|
-
break;
|
|
869
|
-
}
|
|
870
|
-
}
|
|
871
|
-
if (!foundCandidate) missingFiles.push(candidates.join(" or "));
|
|
872
|
-
}
|
|
873
|
-
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
|
|
874
|
-
}
|
|
875
|
-
async function finalizePrismaFiles(options) {
|
|
876
|
-
const projectDir = options.projectDir ?? process.cwd();
|
|
877
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
878
|
-
await ensureRequiredPrismaFiles(projectDir);
|
|
879
|
-
const generatedDir = await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? "server/generated" : "src/generated";
|
|
880
|
-
if (options.provider !== "postgresql") {
|
|
881
|
-
const envExamplePath = path.join(prismaProjectDir, ".env.example");
|
|
882
|
-
if (!await fs.pathExists(envExamplePath)) await fs.writeFile(envExamplePath, `# Copy this file to .env and replace the value before deploying.\nDATABASE_URL="${getDefaultDatabaseUrl(options.provider)}"\n`, "utf8");
|
|
883
|
-
}
|
|
884
|
-
await ensureGitignoreEntry(prismaProjectDir, generatedDir);
|
|
885
|
-
}
|
|
886
|
-
async function writeDependenciesForContext(context, projectDir) {
|
|
887
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
888
|
-
try {
|
|
889
|
-
await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
|
|
890
|
-
return true;
|
|
891
|
-
} catch (error) {
|
|
892
|
-
cancel(getCommandErrorMessage(error));
|
|
893
|
-
return false;
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
async function installDependenciesForContext(context, projectDir) {
|
|
897
|
-
if (!context.shouldInstall) return true;
|
|
898
|
-
const installCommand = getInstallCommand(context.packageManager);
|
|
899
|
-
if (context.verbose) {
|
|
900
|
-
log.step(`Running ${installCommand}`);
|
|
901
|
-
try {
|
|
902
|
-
await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
|
|
903
|
-
log.success("Dependencies installed.");
|
|
904
|
-
return true;
|
|
905
|
-
} catch (error) {
|
|
906
|
-
cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
|
|
907
|
-
return false;
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
const installSpinner = spinner();
|
|
911
|
-
installSpinner.start(`Running ${installCommand}...`);
|
|
912
|
-
try {
|
|
913
|
-
await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
|
|
914
|
-
installSpinner.stop("Dependencies installed.");
|
|
915
|
-
return true;
|
|
916
|
-
} catch (error) {
|
|
917
|
-
installSpinner.stop("Could not install dependencies.");
|
|
918
|
-
cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
|
|
919
|
-
return false;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
async function finalizePrismaFilesForContext(context, projectDir) {
|
|
923
|
-
const initSpinner = spinner();
|
|
924
|
-
initSpinner.start("Preparing Prisma files...");
|
|
925
|
-
try {
|
|
926
|
-
await finalizePrismaFiles({
|
|
927
|
-
provider: context.databaseProvider,
|
|
928
|
-
projectDir
|
|
929
|
-
});
|
|
930
|
-
initSpinner.stop("Prisma files ready.");
|
|
931
|
-
return true;
|
|
932
|
-
} catch (error) {
|
|
933
|
-
initSpinner.stop("Could not prepare Prisma files.");
|
|
934
|
-
cancel(getCommandErrorMessage(error));
|
|
935
|
-
return false;
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
async function generatePrismaClientForContext(context, projectDir) {
|
|
939
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
940
|
-
const generateCommand = getRunScriptCommand(context.packageManager, "db:generate");
|
|
941
|
-
if (context.verbose) log.step(`Running ${generateCommand}`);
|
|
942
|
-
const generateSpinner = context.verbose ? void 0 : spinner();
|
|
943
|
-
generateSpinner?.start("Generating Prisma Client...");
|
|
944
|
-
try {
|
|
945
|
-
const generateArgs = getRunScriptArgs(context.packageManager, "db:generate");
|
|
946
|
-
await execa(generateArgs.command, generateArgs.args, {
|
|
947
|
-
cwd: prismaProjectDir,
|
|
948
|
-
env: {
|
|
949
|
-
...process.env,
|
|
950
|
-
DATABASE_URL: context.databaseUrl ?? getDefaultDatabaseUrl(context.databaseProvider)
|
|
951
|
-
},
|
|
952
|
-
stdio: context.verbose ? "inherit" : "pipe"
|
|
953
|
-
});
|
|
954
|
-
if (context.verbose) log.success("Prisma Client generated.");
|
|
955
|
-
else generateSpinner?.stop("Prisma Client generated.");
|
|
956
|
-
return { didGenerateClient: true };
|
|
957
|
-
} catch (error) {
|
|
958
|
-
if (context.verbose) log.warn("Could not generate Prisma Client.");
|
|
959
|
-
else generateSpinner?.stop("Could not generate Prisma Client.");
|
|
960
|
-
return {
|
|
961
|
-
didGenerateClient: false,
|
|
962
|
-
warning: `Prisma generate failed: ${getCommandErrorMessage(error)}`
|
|
963
|
-
};
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
async function generateInitialMigrationForContext(context, projectDir) {
|
|
967
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
968
|
-
const migrationsDir = path.join(prismaProjectDir, "prisma/migrations");
|
|
969
|
-
if (await fs.pathExists(migrationsDir)) {
|
|
970
|
-
if ((await fs.readdir(migrationsDir, { withFileTypes: true })).some((entry) => entry.isDirectory())) return { didGenerateMigration: true };
|
|
971
|
-
}
|
|
972
|
-
if (!context.shouldInstall) return { didGenerateMigration: false };
|
|
973
|
-
const migrationDir = path.join(migrationsDir, INITIAL_MIGRATION_NAME);
|
|
974
|
-
const migrationFile = path.join(migrationDir, "migration.sql");
|
|
975
|
-
const migrationInvocation = getPrismaCliArgs(context.packageManager, [
|
|
976
|
-
"migrate",
|
|
977
|
-
"diff",
|
|
978
|
-
"--from-empty",
|
|
979
|
-
"--to-schema",
|
|
980
|
-
"prisma/schema.prisma",
|
|
981
|
-
"--script",
|
|
982
|
-
"--output",
|
|
983
|
-
migrationFile
|
|
984
|
-
]);
|
|
985
|
-
const migrationSpinner = context.verbose ? void 0 : spinner();
|
|
986
|
-
migrationSpinner?.start("Generating initial migration...");
|
|
987
|
-
try {
|
|
988
|
-
await fs.ensureDir(migrationDir);
|
|
989
|
-
await execa(migrationInvocation.command, migrationInvocation.args, {
|
|
990
|
-
cwd: prismaProjectDir,
|
|
991
|
-
env: {
|
|
992
|
-
...process.env,
|
|
993
|
-
DATABASE_URL: context.databaseUrl ?? getDefaultDatabaseUrl(context.databaseProvider)
|
|
994
|
-
},
|
|
995
|
-
stdio: context.verbose ? "inherit" : "pipe"
|
|
996
|
-
});
|
|
997
|
-
await fs.writeFile(path.join(migrationsDir, "migration_lock.toml"), `# Please do not edit this file manually\nprovider = "${context.databaseProvider}"\n`, "utf8");
|
|
998
|
-
if (context.verbose) log.success("Initial migration generated.");
|
|
999
|
-
else migrationSpinner?.stop("Initial migration generated.");
|
|
1000
|
-
return { didGenerateMigration: true };
|
|
1001
|
-
} catch (error) {
|
|
1002
|
-
await fs.remove(migrationDir);
|
|
1003
|
-
if (context.verbose) log.warn("Could not generate the initial migration.");
|
|
1004
|
-
else migrationSpinner?.stop("Could not generate the initial migration.");
|
|
1005
|
-
return {
|
|
1006
|
-
didGenerateMigration: false,
|
|
1007
|
-
warning: `Initial migration generation failed: ${getCommandErrorMessage(error)}`
|
|
1008
|
-
};
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
function buildWarningLines(generateWarning, migrationGenerationWarning, migrateAndSeedWarning) {
|
|
1012
|
-
const warningLines = [];
|
|
1013
|
-
if (generateWarning) warningLines.push(`- ${generateWarning}`);
|
|
1014
|
-
if (migrationGenerationWarning) warningLines.push(`- ${migrationGenerationWarning}`);
|
|
1015
|
-
if (migrateAndSeedWarning) warningLines.push(`- ${migrateAndSeedWarning}`);
|
|
1016
|
-
return warningLines;
|
|
1017
|
-
}
|
|
1018
|
-
function buildNextStepsForContext(opts) {
|
|
1019
|
-
const { context, options, didGenerateClient, didMigrate, didSeed } = opts;
|
|
1020
|
-
const nextSteps = [...options.prependNextSteps ?? []];
|
|
1021
|
-
if (!context.shouldInstall) nextSteps.push(`- ${getInstallCommand(context.packageManager)}`);
|
|
1022
|
-
if (!didGenerateClient) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:generate")}`);
|
|
1023
|
-
if (options.includeMigrationAndSeedNextSteps !== false && !didMigrate) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:migrate")}`);
|
|
1024
|
-
if (options.includeMigrationAndSeedNextSteps !== false && !didSeed) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:seed")}`);
|
|
1025
|
-
if (options.includeDevNextStep) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "dev")}`);
|
|
1026
|
-
return nextSteps;
|
|
1027
|
-
}
|
|
1028
|
-
async function executePrismaSetupContext(context, options = {}) {
|
|
1029
|
-
const projectDir = path.resolve(options.projectDir ?? context.projectDir);
|
|
1030
|
-
if (!await writeDependenciesForContext(context, projectDir)) return { ok: false };
|
|
1031
|
-
if (!await installDependenciesForContext(context, projectDir)) return { ok: false };
|
|
1032
|
-
if (!await finalizePrismaFilesForContext(context, projectDir)) return { ok: false };
|
|
1033
|
-
const generateResult = await generatePrismaClientForContext(context, projectDir);
|
|
1034
|
-
const migrationGenerationResult = await generateInitialMigrationForContext(context, projectDir);
|
|
1035
|
-
const migrateAndSeedResult = await migrateAndSeedIfRequested(context, projectDir, {
|
|
1036
|
-
databaseUrl: context.databaseUrl,
|
|
1037
|
-
didGenerateClient: generateResult.didGenerateClient
|
|
1038
|
-
});
|
|
1039
|
-
const warningLines = buildWarningLines(generateResult.warning, migrationGenerationResult.warning, migrateAndSeedResult.warning);
|
|
1040
|
-
return {
|
|
1041
|
-
ok: true,
|
|
1042
|
-
nextSteps: buildNextStepsForContext({
|
|
1043
|
-
context,
|
|
1044
|
-
options,
|
|
1045
|
-
didGenerateClient: generateResult.didGenerateClient,
|
|
1046
|
-
didMigrate: migrateAndSeedResult.didMigrate,
|
|
1047
|
-
didSeed: migrateAndSeedResult.didSeed
|
|
1048
|
-
}),
|
|
1049
|
-
warningSection: warningLines.length > 0 ? `\n\n${warningLines.join("\n")}` : "",
|
|
1050
|
-
didGenerateClient: generateResult.didGenerateClient,
|
|
1051
|
-
didGenerateMigration: migrationGenerationResult.didGenerateMigration
|
|
1052
|
-
};
|
|
1053
|
-
}
|
|
1054
|
-
async function migrateAndSeedIfRequested(context, projectDir, options) {
|
|
1055
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
1056
|
-
if (!context.shouldMigrateAndSeed) return {
|
|
1057
|
-
didMigrate: false,
|
|
1058
|
-
didSeed: false
|
|
1059
|
-
};
|
|
1060
|
-
if (!options.didGenerateClient) return {
|
|
1061
|
-
didMigrate: false,
|
|
1062
|
-
didSeed: false,
|
|
1063
|
-
warning: "Skipped migrate + seed because the Prisma Client was not generated."
|
|
1064
|
-
};
|
|
1065
|
-
if (!options.databaseUrl) return {
|
|
1066
|
-
didMigrate: false,
|
|
1067
|
-
didSeed: false,
|
|
1068
|
-
warning: "Skipped migrate + seed because no DATABASE_URL is available."
|
|
1069
|
-
};
|
|
1070
|
-
const migrateInvocation = getPrismaCliArgs(context.packageManager, ["migrate", "deploy"]);
|
|
1071
|
-
const seedInvocation = getPrismaCliArgs(context.packageManager, ["db", "seed"]);
|
|
1072
|
-
const migrateSpinner = spinner();
|
|
1073
|
-
migrateSpinner.start("Applying migrations...");
|
|
1074
|
-
let didMigrate = false;
|
|
1075
|
-
try {
|
|
1076
|
-
await execa(migrateInvocation.command, migrateInvocation.args, {
|
|
1077
|
-
cwd: prismaProjectDir,
|
|
1078
|
-
env: {
|
|
1079
|
-
...process.env,
|
|
1080
|
-
DATABASE_URL: options.databaseUrl
|
|
1081
|
-
},
|
|
1082
|
-
stdio: context.verbose ? "inherit" : "pipe"
|
|
1083
|
-
});
|
|
1084
|
-
migrateSpinner.stop("Migrations applied.");
|
|
1085
|
-
didMigrate = true;
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
migrateSpinner.stop(`Migration failed${error instanceof Error ? `: ${error.message}` : "."}`);
|
|
1088
|
-
return {
|
|
1089
|
-
didMigrate: false,
|
|
1090
|
-
didSeed: false,
|
|
1091
|
-
warning: `Migration failed; run \`${getRunScriptCommand(context.packageManager, "db:migrate:deploy")}\` manually.`
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
const seedSpinner = spinner();
|
|
1095
|
-
seedSpinner.start("Seeding database...");
|
|
1096
|
-
let didSeed = false;
|
|
1097
|
-
try {
|
|
1098
|
-
await execa(seedInvocation.command, seedInvocation.args, {
|
|
1099
|
-
cwd: prismaProjectDir,
|
|
1100
|
-
env: {
|
|
1101
|
-
...process.env,
|
|
1102
|
-
DATABASE_URL: options.databaseUrl
|
|
1103
|
-
},
|
|
1104
|
-
stdio: context.verbose ? "inherit" : "pipe"
|
|
1105
|
-
});
|
|
1106
|
-
seedSpinner.stop("Database seeded.");
|
|
1107
|
-
didSeed = true;
|
|
1108
|
-
} catch (error) {
|
|
1109
|
-
seedSpinner.stop(`Seed failed${error instanceof Error ? `: ${error.message}` : "."}`);
|
|
1110
|
-
return {
|
|
1111
|
-
didMigrate,
|
|
1112
|
-
didSeed: false,
|
|
1113
|
-
warning: `Seed failed; run \`${getRunScriptCommand(context.packageManager, "db:seed")}\` manually.`
|
|
1114
|
-
};
|
|
1115
|
-
}
|
|
1116
|
-
return {
|
|
1117
|
-
didMigrate,
|
|
1118
|
-
didSeed
|
|
1119
|
-
};
|
|
1120
|
-
}
|
|
1121
|
-
//#endregion
|
|
1122
|
-
//#region src/tasks/setup-addons.ts
|
|
1123
|
-
const DEFAULT_ADDON_SCOPE = "project";
|
|
1124
|
-
const DEFAULT_SKILLS_AGENTS = [
|
|
1125
|
-
"claude-code",
|
|
1126
|
-
"codex",
|
|
1127
|
-
"cursor"
|
|
1128
|
-
];
|
|
1129
|
-
const DEFAULT_MCP_AGENTS = [
|
|
1130
|
-
"claude-code",
|
|
1131
|
-
"codex",
|
|
1132
|
-
"cursor"
|
|
1133
|
-
];
|
|
1134
|
-
const DEFAULT_EXTENSION_TARGETS = ["vscode", "cursor"];
|
|
1135
|
-
const PRISMA_MCP_SERVER = "https://mcp.prisma.io/mcp";
|
|
1136
|
-
const ADDON_OPTIONS = [
|
|
1137
|
-
{
|
|
1138
|
-
value: "skills",
|
|
1139
|
-
label: "Skills",
|
|
1140
|
-
hint: "Install curated Prisma skills to your selected coding agents"
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
value: "mcp",
|
|
1144
|
-
label: "MCP",
|
|
1145
|
-
hint: "Configure Prisma MCP server in agent MCP config files"
|
|
1146
|
-
},
|
|
1147
|
-
{
|
|
1148
|
-
value: "extension",
|
|
1149
|
-
label: "IDE Extension",
|
|
1150
|
-
hint: "Install Prisma extension in selected IDEs (VS Code, Cursor, Windsurf)"
|
|
1151
|
-
}
|
|
1152
|
-
];
|
|
1153
|
-
const SKILLS_AGENT_OPTIONS = [
|
|
1154
|
-
{
|
|
1155
|
-
value: "cursor",
|
|
1156
|
-
label: "Cursor"
|
|
1157
|
-
},
|
|
1158
|
-
{
|
|
1159
|
-
value: "claude-code",
|
|
1160
|
-
label: "Claude Code"
|
|
1161
|
-
},
|
|
1162
|
-
{
|
|
1163
|
-
value: "cline",
|
|
1164
|
-
label: "Cline"
|
|
1165
|
-
},
|
|
1166
|
-
{
|
|
1167
|
-
value: "github-copilot",
|
|
1168
|
-
label: "GitHub Copilot"
|
|
1169
|
-
},
|
|
1170
|
-
{
|
|
1171
|
-
value: "codex",
|
|
1172
|
-
label: "Codex"
|
|
1173
|
-
},
|
|
1174
|
-
{
|
|
1175
|
-
value: "opencode",
|
|
1176
|
-
label: "OpenCode"
|
|
1177
|
-
},
|
|
1178
|
-
{
|
|
1179
|
-
value: "windsurf",
|
|
1180
|
-
label: "Windsurf"
|
|
1181
|
-
},
|
|
1182
|
-
{
|
|
1183
|
-
value: "goose",
|
|
1184
|
-
label: "Goose"
|
|
1185
|
-
},
|
|
1186
|
-
{
|
|
1187
|
-
value: "roo",
|
|
1188
|
-
label: "Roo Code"
|
|
1189
|
-
},
|
|
1190
|
-
{
|
|
1191
|
-
value: "kilo",
|
|
1192
|
-
label: "Kilo Code"
|
|
1193
|
-
},
|
|
1194
|
-
{
|
|
1195
|
-
value: "gemini-cli",
|
|
1196
|
-
label: "Gemini CLI"
|
|
1197
|
-
},
|
|
1198
|
-
{
|
|
1199
|
-
value: "antigravity",
|
|
1200
|
-
label: "Antigravity"
|
|
1201
|
-
},
|
|
1202
|
-
{
|
|
1203
|
-
value: "openhands",
|
|
1204
|
-
label: "OpenHands"
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
value: "trae",
|
|
1208
|
-
label: "Trae"
|
|
1209
|
-
},
|
|
1210
|
-
{
|
|
1211
|
-
value: "amp",
|
|
1212
|
-
label: "Amp"
|
|
1213
|
-
},
|
|
1214
|
-
{
|
|
1215
|
-
value: "pi",
|
|
1216
|
-
label: "Pi"
|
|
1217
|
-
},
|
|
1218
|
-
{
|
|
1219
|
-
value: "qoder",
|
|
1220
|
-
label: "Qoder"
|
|
1221
|
-
},
|
|
1222
|
-
{
|
|
1223
|
-
value: "qwen-code",
|
|
1224
|
-
label: "Qwen Code"
|
|
1225
|
-
},
|
|
1226
|
-
{
|
|
1227
|
-
value: "kiro-cli",
|
|
1228
|
-
label: "Kiro CLI"
|
|
1229
|
-
},
|
|
1230
|
-
{
|
|
1231
|
-
value: "droid",
|
|
1232
|
-
label: "Droid"
|
|
1233
|
-
},
|
|
1234
|
-
{
|
|
1235
|
-
value: "command-code",
|
|
1236
|
-
label: "Command Code"
|
|
1237
|
-
},
|
|
1238
|
-
{
|
|
1239
|
-
value: "clawdbot",
|
|
1240
|
-
label: "Clawdbot"
|
|
1241
|
-
},
|
|
1242
|
-
{
|
|
1243
|
-
value: "zencoder",
|
|
1244
|
-
label: "Zencoder"
|
|
1245
|
-
},
|
|
1246
|
-
{
|
|
1247
|
-
value: "neovate",
|
|
1248
|
-
label: "Neovate"
|
|
1249
|
-
},
|
|
1250
|
-
{
|
|
1251
|
-
value: "mcpjam",
|
|
1252
|
-
label: "MCPJam"
|
|
1253
|
-
}
|
|
1254
|
-
];
|
|
1255
|
-
const MCP_AGENT_OPTIONS = [
|
|
1256
|
-
{
|
|
1257
|
-
value: "claude-code",
|
|
1258
|
-
label: "Claude Code"
|
|
1259
|
-
},
|
|
1260
|
-
{
|
|
1261
|
-
value: "codex",
|
|
1262
|
-
label: "Codex"
|
|
1263
|
-
},
|
|
1264
|
-
{
|
|
1265
|
-
value: "cursor",
|
|
1266
|
-
label: "Cursor"
|
|
1267
|
-
},
|
|
1268
|
-
{
|
|
1269
|
-
value: "vscode",
|
|
1270
|
-
label: "VS Code"
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
value: "github-copilot-cli",
|
|
1274
|
-
label: "GitHub Copilot CLI"
|
|
1275
|
-
},
|
|
1276
|
-
{
|
|
1277
|
-
value: "opencode",
|
|
1278
|
-
label: "OpenCode"
|
|
1279
|
-
},
|
|
1280
|
-
{
|
|
1281
|
-
value: "gemini-cli",
|
|
1282
|
-
label: "Gemini CLI"
|
|
1283
|
-
},
|
|
1284
|
-
{
|
|
1285
|
-
value: "goose",
|
|
1286
|
-
label: "Goose"
|
|
1287
|
-
},
|
|
1288
|
-
{
|
|
1289
|
-
value: "zed",
|
|
1290
|
-
label: "Zed"
|
|
1291
|
-
},
|
|
1292
|
-
{
|
|
1293
|
-
value: "antigravity",
|
|
1294
|
-
label: "Antigravity"
|
|
1295
|
-
},
|
|
1296
|
-
{
|
|
1297
|
-
value: "cline",
|
|
1298
|
-
label: "Cline VS Code Extension"
|
|
1299
|
-
},
|
|
1300
|
-
{
|
|
1301
|
-
value: "cline-cli",
|
|
1302
|
-
label: "Cline CLI"
|
|
1303
|
-
},
|
|
1304
|
-
{
|
|
1305
|
-
value: "claude-desktop",
|
|
1306
|
-
label: "Claude Desktop"
|
|
1307
|
-
},
|
|
1308
|
-
{
|
|
1309
|
-
value: "mcporter",
|
|
1310
|
-
label: "MCPorter"
|
|
1311
|
-
}
|
|
1312
|
-
];
|
|
1313
|
-
const SHARED_PRISMA_SKILLS = [
|
|
1314
|
-
"prisma-cli",
|
|
1315
|
-
"prisma-client-api",
|
|
1316
|
-
"prisma-database-setup",
|
|
1317
|
-
"prisma-upgrade-v7"
|
|
1318
|
-
];
|
|
1319
|
-
function getAvailablePrismaSkills(provider) {
|
|
1320
|
-
const skills = [...SHARED_PRISMA_SKILLS, "prisma-compute"];
|
|
1321
|
-
if (provider === "postgresql") return [...skills, "prisma-postgres"];
|
|
1322
|
-
return skills;
|
|
1323
|
-
}
|
|
1324
|
-
function getSkillOptions(provider) {
|
|
1325
|
-
const available = getAvailablePrismaSkills(provider);
|
|
1326
|
-
const options = {
|
|
1327
|
-
"prisma-cli": {
|
|
1328
|
-
value: "prisma-cli",
|
|
1329
|
-
label: "prisma-cli",
|
|
1330
|
-
hint: "Prisma CLI reference"
|
|
1331
|
-
},
|
|
1332
|
-
"prisma-client-api": {
|
|
1333
|
-
value: "prisma-client-api",
|
|
1334
|
-
label: "prisma-client-api",
|
|
1335
|
-
hint: "Prisma Client query patterns"
|
|
1336
|
-
},
|
|
1337
|
-
"prisma-compute": {
|
|
1338
|
-
value: "prisma-compute",
|
|
1339
|
-
label: "prisma-compute",
|
|
1340
|
-
hint: "Prisma Compute deploy and hosting workflows"
|
|
1341
|
-
},
|
|
1342
|
-
"prisma-database-setup": {
|
|
1343
|
-
value: "prisma-database-setup",
|
|
1344
|
-
label: "prisma-database-setup",
|
|
1345
|
-
hint: "Database provider setup guides"
|
|
1346
|
-
},
|
|
1347
|
-
"prisma-upgrade-v7": {
|
|
1348
|
-
value: "prisma-upgrade-v7",
|
|
1349
|
-
label: "prisma-upgrade-v7",
|
|
1350
|
-
hint: "v6 to v7 migration guide"
|
|
1351
|
-
},
|
|
1352
|
-
"prisma-postgres": {
|
|
1353
|
-
value: "prisma-postgres",
|
|
1354
|
-
label: "prisma-postgres",
|
|
1355
|
-
hint: "Prisma Postgres workflows"
|
|
1356
|
-
}
|
|
1357
|
-
};
|
|
1358
|
-
return available.map((skill) => options[skill]);
|
|
1359
|
-
}
|
|
1360
|
-
function collectAddonsFromInput(input) {
|
|
1361
|
-
const addons = [];
|
|
1362
|
-
if (input.skills === true) addons.push("skills");
|
|
1363
|
-
if (input.mcp === true) addons.push("mcp");
|
|
1364
|
-
if (input.extension === true) addons.push("extension");
|
|
1365
|
-
return uniqueValues(addons);
|
|
1366
|
-
}
|
|
1367
|
-
const EXTENSION_TARGET_OPTIONS = [
|
|
1368
|
-
{
|
|
1369
|
-
value: "vscode",
|
|
1370
|
-
label: "VS Code",
|
|
1371
|
-
hint: "Uses the `code` CLI"
|
|
1372
|
-
},
|
|
1373
|
-
{
|
|
1374
|
-
value: "cursor",
|
|
1375
|
-
label: "Cursor",
|
|
1376
|
-
hint: "Uses the `cursor` CLI"
|
|
1377
|
-
},
|
|
1378
|
-
{
|
|
1379
|
-
value: "windsurf",
|
|
1380
|
-
label: "Windsurf",
|
|
1381
|
-
hint: "Uses the `windsurf` CLI"
|
|
1382
|
-
}
|
|
1383
|
-
];
|
|
1384
|
-
function uniqueValues(values) {
|
|
1385
|
-
return Array.from(new Set(values));
|
|
1386
|
-
}
|
|
1387
|
-
function getRecommendedPrismaSkills(provider, shouldUsePrismaPostgres) {
|
|
1388
|
-
const skills = [...SHARED_PRISMA_SKILLS];
|
|
1389
|
-
if (provider === "postgresql" && shouldUsePrismaPostgres) skills.push("prisma-postgres");
|
|
1390
|
-
return uniqueValues(skills);
|
|
1391
|
-
}
|
|
1392
|
-
async function promptForAddons() {
|
|
1393
|
-
const selectedAddons = await multiselect({
|
|
1394
|
-
message: "Select add-ons (optional)",
|
|
1395
|
-
options: ADDON_OPTIONS,
|
|
1396
|
-
required: false
|
|
1397
|
-
});
|
|
1398
|
-
if (isCancel(selectedAddons)) {
|
|
1399
|
-
cancel("Operation cancelled.");
|
|
1400
|
-
return;
|
|
1401
|
-
}
|
|
1402
|
-
return uniqueValues(selectedAddons);
|
|
1403
|
-
}
|
|
1404
|
-
async function promptForAddonScope() {
|
|
1405
|
-
const selectedScope = await select({
|
|
1406
|
-
message: "Where should add-ons write config?",
|
|
1407
|
-
initialValue: DEFAULT_ADDON_SCOPE,
|
|
1408
|
-
options: [{
|
|
1409
|
-
value: "project",
|
|
1410
|
-
label: "Project",
|
|
1411
|
-
hint: "Recommended for teams (checked into the project when applicable)"
|
|
1412
|
-
}, {
|
|
1413
|
-
value: "global",
|
|
1414
|
-
label: "Global",
|
|
1415
|
-
hint: "Personal machine-level setup"
|
|
1416
|
-
}]
|
|
1417
|
-
});
|
|
1418
|
-
if (isCancel(selectedScope)) {
|
|
1419
|
-
cancel("Operation cancelled.");
|
|
1420
|
-
return;
|
|
1421
|
-
}
|
|
1422
|
-
return selectedScope;
|
|
1423
|
-
}
|
|
1424
|
-
async function promptForPrismaSkills(provider, recommendedSkills) {
|
|
1425
|
-
const options = getSkillOptions(provider);
|
|
1426
|
-
const optionValues = new Set(options.map((option) => option.value));
|
|
1427
|
-
const selectedSkills = await multiselect({
|
|
1428
|
-
message: "Select Prisma skills",
|
|
1429
|
-
options,
|
|
1430
|
-
required: false,
|
|
1431
|
-
initialValues: recommendedSkills.filter((skill) => optionValues.has(skill))
|
|
1432
|
-
});
|
|
1433
|
-
if (isCancel(selectedSkills)) {
|
|
1434
|
-
cancel("Operation cancelled.");
|
|
1435
|
-
return;
|
|
1436
|
-
}
|
|
1437
|
-
return uniqueValues(selectedSkills);
|
|
1438
|
-
}
|
|
1439
|
-
async function promptForSkillsAgents() {
|
|
1440
|
-
const selectedAgents = await multiselect({
|
|
1441
|
-
message: "Select agents for skills",
|
|
1442
|
-
options: SKILLS_AGENT_OPTIONS,
|
|
1443
|
-
required: false,
|
|
1444
|
-
initialValues: [...DEFAULT_SKILLS_AGENTS]
|
|
1445
|
-
});
|
|
1446
|
-
if (isCancel(selectedAgents)) {
|
|
1447
|
-
cancel("Operation cancelled.");
|
|
1448
|
-
return;
|
|
1449
|
-
}
|
|
1450
|
-
return uniqueValues(selectedAgents);
|
|
1451
|
-
}
|
|
1452
|
-
async function promptForMcpAgents() {
|
|
1453
|
-
const selectedAgents = await multiselect({
|
|
1454
|
-
message: "Select agents for MCP",
|
|
1455
|
-
options: MCP_AGENT_OPTIONS,
|
|
1456
|
-
required: false,
|
|
1457
|
-
initialValues: [...DEFAULT_MCP_AGENTS]
|
|
1458
|
-
});
|
|
1459
|
-
if (isCancel(selectedAgents)) {
|
|
1460
|
-
cancel("Operation cancelled.");
|
|
1461
|
-
return;
|
|
1462
|
-
}
|
|
1463
|
-
return uniqueValues(selectedAgents);
|
|
1464
|
-
}
|
|
1465
|
-
async function promptForExtensionTargets() {
|
|
1466
|
-
const selectedTargets = await multiselect({
|
|
1467
|
-
message: "Select IDEs for extension install",
|
|
1468
|
-
options: EXTENSION_TARGET_OPTIONS,
|
|
1469
|
-
required: false,
|
|
1470
|
-
initialValues: DEFAULT_EXTENSION_TARGETS
|
|
1471
|
-
});
|
|
1472
|
-
if (isCancel(selectedTargets)) {
|
|
1473
|
-
cancel("Operation cancelled.");
|
|
1474
|
-
return;
|
|
1475
|
-
}
|
|
1476
|
-
return uniqueValues(selectedTargets);
|
|
1477
|
-
}
|
|
1478
|
-
async function collectCreateAddonSetupContext(input, options) {
|
|
1479
|
-
const hasExplicitAddonSelection = input.skills !== void 0 || input.mcp !== void 0 || input.extension !== void 0;
|
|
1480
|
-
const selectedFromInput = collectAddonsFromInput(input);
|
|
1481
|
-
const selectedAddons = selectedFromInput.length > 0 ? selectedFromInput : hasExplicitAddonSelection ? [] : options.useDefaults ? [] : await promptForAddons();
|
|
1482
|
-
if (!selectedAddons) return;
|
|
1483
|
-
const addons = uniqueValues(selectedAddons);
|
|
1484
|
-
if (addons.length === 0) return null;
|
|
1485
|
-
const scope = addons.includes("skills") || addons.includes("mcp") ? options.useDefaults ? DEFAULT_ADDON_SCOPE : await promptForAddonScope() : DEFAULT_ADDON_SCOPE;
|
|
1486
|
-
if (!scope) return;
|
|
1487
|
-
const recommendedSkills = getRecommendedPrismaSkills(options.provider, options.shouldUsePrismaPostgres);
|
|
1488
|
-
const skills = !addons.includes("skills") ? [] : options.useDefaults ? recommendedSkills : await promptForPrismaSkills(options.provider, recommendedSkills);
|
|
1489
|
-
if (!skills) return;
|
|
1490
|
-
const skillsAgents = !addons.includes("skills") ? [] : options.useDefaults ? [...DEFAULT_SKILLS_AGENTS] : await promptForSkillsAgents();
|
|
1491
|
-
if (!skillsAgents) return;
|
|
1492
|
-
const mcpAgents = !addons.includes("mcp") ? [] : options.useDefaults ? [...DEFAULT_MCP_AGENTS] : await promptForMcpAgents();
|
|
1493
|
-
if (!mcpAgents) return;
|
|
1494
|
-
const extensionTargets = !addons.includes("extension") ? [] : options.useDefaults ? [...DEFAULT_EXTENSION_TARGETS] : await promptForExtensionTargets();
|
|
1495
|
-
if (!extensionTargets) return;
|
|
1496
|
-
return {
|
|
1497
|
-
addons,
|
|
1498
|
-
scope,
|
|
1499
|
-
skills,
|
|
1500
|
-
skillsAgents: uniqueValues(skillsAgents),
|
|
1501
|
-
mcpAgents: uniqueValues(mcpAgents),
|
|
1502
|
-
extensionTargets: uniqueValues(extensionTargets)
|
|
1503
|
-
};
|
|
1504
|
-
}
|
|
1505
|
-
async function executeExternalCommand(params) {
|
|
1506
|
-
await execa(params.command, params.args, {
|
|
1507
|
-
cwd: params.cwd,
|
|
1508
|
-
stdio: params.verbose ? "inherit" : "pipe",
|
|
1509
|
-
env: {
|
|
1510
|
-
...process.env,
|
|
1511
|
-
CI: "true"
|
|
1512
|
-
}
|
|
1513
|
-
});
|
|
1514
|
-
}
|
|
1515
|
-
async function installSkillsAddon(params) {
|
|
1516
|
-
if (params.agents.length === 0 || params.skills.length === 0) return "Skipped skills addon because no skills or agents were selected.";
|
|
1517
|
-
const scopeArgs = params.scope === "global" ? ["-g"] : [];
|
|
1518
|
-
const skillArgs = params.skills.flatMap((skill) => ["-s", skill]);
|
|
1519
|
-
const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
|
|
1520
|
-
const commandArgs = [
|
|
1521
|
-
"skills@latest",
|
|
1522
|
-
"add",
|
|
1523
|
-
"prisma/skills",
|
|
1524
|
-
...scopeArgs,
|
|
1525
|
-
...skillArgs,
|
|
1526
|
-
...agentArgs,
|
|
1527
|
-
"-y"
|
|
1528
|
-
];
|
|
1529
|
-
const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
|
|
1530
|
-
try {
|
|
1531
|
-
await executeExternalCommand({
|
|
1532
|
-
command: execution.command,
|
|
1533
|
-
args: execution.args,
|
|
1534
|
-
cwd: params.projectDir,
|
|
1535
|
-
verbose: params.verbose
|
|
1536
|
-
});
|
|
1537
|
-
return;
|
|
1538
|
-
} catch (error) {
|
|
1539
|
-
return `Skills addon failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1540
|
-
}
|
|
1541
|
-
}
|
|
1542
|
-
async function installMcpAddon(params) {
|
|
1543
|
-
if (params.agents.length === 0) return "Skipped MCP addon because no agents were selected.";
|
|
1544
|
-
const scopeArgs = params.scope === "global" ? ["-g"] : [];
|
|
1545
|
-
const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
|
|
1546
|
-
const commandArgs = [
|
|
1547
|
-
"add-mcp@latest",
|
|
1548
|
-
PRISMA_MCP_SERVER,
|
|
1549
|
-
...scopeArgs,
|
|
1550
|
-
...agentArgs,
|
|
1551
|
-
"--name",
|
|
1552
|
-
"prisma",
|
|
1553
|
-
"--gitignore",
|
|
1554
|
-
"-y"
|
|
1555
|
-
];
|
|
1556
|
-
const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
|
|
1557
|
-
try {
|
|
1558
|
-
await executeExternalCommand({
|
|
1559
|
-
command: execution.command,
|
|
1560
|
-
args: execution.args,
|
|
1561
|
-
cwd: params.projectDir,
|
|
1562
|
-
verbose: params.verbose
|
|
1563
|
-
});
|
|
1564
|
-
return;
|
|
1565
|
-
} catch (error) {
|
|
1566
|
-
return `MCP addon failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
function getExtensionInstallBinary(target) {
|
|
1570
|
-
switch (target) {
|
|
1571
|
-
case "vscode": return "code";
|
|
1572
|
-
case "cursor": return "cursor";
|
|
1573
|
-
case "windsurf": return "windsurf";
|
|
1574
|
-
default: throw new Error(`Unsupported extension target: ${String(target)}`);
|
|
1575
|
-
}
|
|
1576
|
-
}
|
|
1577
|
-
async function installExtensionAddon(params) {
|
|
1578
|
-
if (params.targets.length === 0) return ["Skipped extension addon because no IDE targets were selected."];
|
|
1579
|
-
const warnings = [];
|
|
1580
|
-
for (const target of params.targets) {
|
|
1581
|
-
const binary = getExtensionInstallBinary(target);
|
|
1582
|
-
try {
|
|
1583
|
-
await executeExternalCommand({
|
|
1584
|
-
command: binary,
|
|
1585
|
-
args: ["--version"],
|
|
1586
|
-
cwd: params.projectDir,
|
|
1587
|
-
verbose: false
|
|
1588
|
-
});
|
|
1589
|
-
} catch {
|
|
1590
|
-
warnings.push(`Skipped ${target} extension install because the \`${binary}\` CLI is not available.`);
|
|
1591
|
-
continue;
|
|
1592
|
-
}
|
|
1593
|
-
try {
|
|
1594
|
-
await executeExternalCommand({
|
|
1595
|
-
command: binary,
|
|
1596
|
-
args: [
|
|
1597
|
-
"--install-extension",
|
|
1598
|
-
"Prisma.prisma",
|
|
1599
|
-
"--force"
|
|
1600
|
-
],
|
|
1601
|
-
cwd: params.projectDir,
|
|
1602
|
-
verbose: params.verbose
|
|
1603
|
-
});
|
|
1604
|
-
} catch (error) {
|
|
1605
|
-
warnings.push(`${target} extension install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
return warnings;
|
|
1609
|
-
}
|
|
1610
|
-
async function executeCreateAddonSetupContext(params) {
|
|
1611
|
-
const { context, packageManager, projectDir, verbose } = params;
|
|
1612
|
-
const addonSpinner = spinner();
|
|
1613
|
-
addonSpinner.start("Applying selected add-ons...");
|
|
1614
|
-
const warnings = [];
|
|
1615
|
-
if (context.addons.includes("skills")) {
|
|
1616
|
-
const warning = await installSkillsAddon({
|
|
1617
|
-
packageManager,
|
|
1618
|
-
projectDir,
|
|
1619
|
-
scope: context.scope,
|
|
1620
|
-
skills: context.skills,
|
|
1621
|
-
agents: context.skillsAgents,
|
|
1622
|
-
verbose
|
|
1623
|
-
});
|
|
1624
|
-
if (warning) warnings.push(warning);
|
|
1625
|
-
}
|
|
1626
|
-
if (context.addons.includes("mcp")) {
|
|
1627
|
-
const warning = await installMcpAddon({
|
|
1628
|
-
packageManager,
|
|
1629
|
-
projectDir,
|
|
1630
|
-
scope: context.scope,
|
|
1631
|
-
agents: context.mcpAgents,
|
|
1632
|
-
verbose
|
|
1633
|
-
});
|
|
1634
|
-
if (warning) warnings.push(warning);
|
|
1635
|
-
}
|
|
1636
|
-
if (context.addons.includes("extension")) {
|
|
1637
|
-
const extensionWarnings = await installExtensionAddon({
|
|
1638
|
-
projectDir,
|
|
1639
|
-
verbose,
|
|
1640
|
-
targets: context.extensionTargets
|
|
1641
|
-
});
|
|
1642
|
-
warnings.push(...extensionWarnings);
|
|
1643
|
-
}
|
|
1644
|
-
if (warnings.length > 0) {
|
|
1645
|
-
addonSpinner.stop("Add-ons applied with warnings.");
|
|
1646
|
-
for (const warning of warnings) log.warn(warning);
|
|
1647
|
-
return;
|
|
1648
|
-
}
|
|
1649
|
-
addonSpinner.stop("Add-ons applied.");
|
|
1650
|
-
}
|
|
1651
|
-
//#endregion
|
|
1652
|
-
//#region src/tasks/deploy-with-composer.ts
|
|
1653
|
-
function redactSecrets(message) {
|
|
1654
|
-
return message.replace(/\b((?:prisma\+)?postgres(?:ql)?:\/\/)[^\s'"]+/gi, "$1<redacted>").replace(/(['"])([A-Z0-9_]*(?:DATABASE_URL|DIRECT_URL|TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY|ACCESS_KEY)[A-Z0-9_]*=)(.*?)\1/g, "$1$2<redacted>$1").replace(/\b([A-Z0-9_]*(?:DATABASE_URL|DIRECT_URL|TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE_KEY|ACCESS_KEY)[A-Z0-9_]*=)[^\s]+/g, "$1<redacted>");
|
|
1655
|
-
}
|
|
1656
|
-
function getErrorMessage(error) {
|
|
1657
|
-
if (error instanceof Error) return redactSecrets(error.message);
|
|
1658
|
-
if (typeof error === "object" && error !== null) {
|
|
1659
|
-
const message = Reflect.get(error, "message");
|
|
1660
|
-
const summary = Reflect.get(error, "summary");
|
|
1661
|
-
if (typeof summary === "string") return redactSecrets(summary);
|
|
1662
|
-
if (typeof message === "string") return redactSecrets(message);
|
|
1663
|
-
}
|
|
1664
|
-
return redactSecrets(String(error));
|
|
1665
|
-
}
|
|
1666
|
-
function createExplicitDeployError(reason, error) {
|
|
1667
|
-
const detail = error === void 0 ? "" : `: ${getErrorMessage(error)}`;
|
|
1668
|
-
return /* @__PURE__ */ new Error(`Deploy requested but ${reason}${detail}`);
|
|
1669
|
-
}
|
|
1670
|
-
function parsePrismaCliEnvelope(output) {
|
|
1671
|
-
const lines = output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1672
|
-
for (const line of lines.reverse()) {
|
|
1673
|
-
let parsed;
|
|
1674
|
-
try {
|
|
1675
|
-
parsed = JSON.parse(line);
|
|
1676
|
-
} catch {
|
|
1677
|
-
continue;
|
|
1678
|
-
}
|
|
1679
|
-
if (typeof parsed !== "object" || parsed === null) continue;
|
|
1680
|
-
const record = parsed;
|
|
1681
|
-
const candidate = record.kind === "result" ? record.envelope : parsed;
|
|
1682
|
-
if (typeof candidate !== "object" || candidate === null) continue;
|
|
1683
|
-
const envelope = candidate;
|
|
1684
|
-
if (typeof envelope.ok !== "boolean") continue;
|
|
1685
|
-
return envelope;
|
|
1686
|
-
}
|
|
1687
|
-
throw new Error("Prisma CLI returned output that is not a valid result envelope.");
|
|
1688
|
-
}
|
|
1689
|
-
function getPrismaPlatformCliArgs(packageManager, args) {
|
|
1690
|
-
return getPackageExecutionArgs(packageManager, [prismaPlatformCliPackage, ...args], { silent: true });
|
|
1691
|
-
}
|
|
1692
|
-
function getPrismaAuthLoginCommand(packageManager) {
|
|
1693
|
-
return getPackageExecutionCommand(packageManager, [
|
|
1694
|
-
prismaPlatformCliPackage,
|
|
1695
|
-
"auth",
|
|
1696
|
-
"login"
|
|
1697
|
-
], { silent: true });
|
|
1698
|
-
}
|
|
1699
|
-
async function isAuthenticatedWithPrisma(packageManager, projectDir) {
|
|
1700
|
-
const invocation = getPrismaPlatformCliArgs(packageManager, [
|
|
1701
|
-
"auth",
|
|
1702
|
-
"whoami",
|
|
1703
|
-
"--json",
|
|
1704
|
-
"--no-interactive"
|
|
1705
|
-
]);
|
|
1706
|
-
const result = await execa(invocation.command, invocation.args, {
|
|
1707
|
-
cwd: projectDir,
|
|
1708
|
-
env: process.env,
|
|
1709
|
-
reject: false
|
|
1710
|
-
});
|
|
1711
|
-
let envelope;
|
|
1712
|
-
try {
|
|
1713
|
-
envelope = parsePrismaCliEnvelope(result.stdout);
|
|
1714
|
-
} catch (error) {
|
|
1715
|
-
const detail = result.stderr.trim() || result.stdout.trim() || getErrorMessage(error);
|
|
1716
|
-
throw new Error(redactSecrets(detail));
|
|
1717
|
-
}
|
|
1718
|
-
if (result.exitCode !== 0 || !envelope.ok) throw new Error(envelope.error?.summary ?? envelope.error?.message ?? (result.stderr.trim() || "Prisma authentication check failed."));
|
|
1719
|
-
if (typeof envelope.result !== "object" || envelope.result === null) throw new Error("Prisma CLI authentication output did not include a result.");
|
|
1720
|
-
const authenticated = Reflect.get(envelope.result, "authenticated");
|
|
1721
|
-
if (typeof authenticated !== "boolean") throw new Error("Prisma CLI authentication output did not include an authentication status.");
|
|
1722
|
-
return authenticated;
|
|
1723
|
-
}
|
|
1724
|
-
async function ensurePrismaAuthentication(packageManager, projectDir) {
|
|
1725
|
-
if (await isAuthenticatedWithPrisma(packageManager, projectDir)) return;
|
|
1726
|
-
if (process.stdin.isTTY !== true) throw new Error(`Prisma authentication is required. Run ${getPrismaAuthLoginCommand(packageManager)} in an interactive terminal, then ${getRunScriptCommand(packageManager, "deploy")}. For CI, set PRISMA_SERVICE_TOKEN.`);
|
|
1727
|
-
log.info("Sign in to Prisma to deploy.");
|
|
1728
|
-
const login = getPrismaPlatformCliArgs(packageManager, ["auth", "login"]);
|
|
1729
|
-
await execa(login.command, login.args, {
|
|
1730
|
-
cwd: projectDir,
|
|
1731
|
-
env: process.env,
|
|
1732
|
-
stdio: "inherit"
|
|
1733
|
-
});
|
|
1734
|
-
if (!await isAuthenticatedWithPrisma(packageManager, projectDir)) throw new Error("Prisma sign-in completed without an active workspace session.");
|
|
1735
|
-
}
|
|
1736
|
-
function getComposerDeployScriptMap(context) {
|
|
1737
|
-
const steps = [
|
|
1738
|
-
getRunScriptCommand(context.packageManager, "build"),
|
|
1739
|
-
getRunScriptCommand(context.packageManager, "composer:deploy"),
|
|
1740
|
-
...context.useComposerPostgres ? [getRunScriptCommand(context.packageManager, "composer:database:setup")] : []
|
|
1741
|
-
];
|
|
1742
|
-
return {
|
|
1743
|
-
"composer:deploy": getPackageExecutionCommand(context.packageManager, [
|
|
1744
|
-
prismaPlatformCliPackage,
|
|
1745
|
-
"composer",
|
|
1746
|
-
"deploy",
|
|
1747
|
-
"module.ts"
|
|
1748
|
-
], { silent: true }),
|
|
1749
|
-
...context.useComposerPostgres ? { "composer:database:setup": `${context.packageManager === "bun" ? "bun" : "node"} scripts/setup-composer-postgres.mjs` } : {},
|
|
1750
|
-
deploy: steps.join(" && ")
|
|
1751
|
-
};
|
|
1752
|
-
}
|
|
1753
|
-
async function collectComposerDeployContext(input, options) {
|
|
1754
|
-
if (input.deploy === false) return null;
|
|
1755
|
-
let wantsDeploy;
|
|
1756
|
-
if (input.deploy === true) wantsDeploy = true;
|
|
1757
|
-
else if (options.useDefaults) return null;
|
|
1758
|
-
else {
|
|
1759
|
-
const confirmed = await confirm({
|
|
1760
|
-
message: "Deploy to Prisma now?",
|
|
1761
|
-
initialValue: false
|
|
1762
|
-
});
|
|
1763
|
-
if (isCancel(confirmed)) {
|
|
1764
|
-
cancel("Operation cancelled.");
|
|
1765
|
-
return;
|
|
1766
|
-
}
|
|
1767
|
-
wantsDeploy = confirmed;
|
|
1768
|
-
}
|
|
1769
|
-
if (!wantsDeploy) return null;
|
|
1770
|
-
if (options.databaseProvider !== "postgresql" && (process.env.DATABASE_URL ?? "").trim().length === 0) {
|
|
1771
|
-
const reason = `immediate deployment for ${options.databaseProvider} needs DATABASE_URL`;
|
|
1772
|
-
if (input.deploy === true) throw createExplicitDeployError(reason);
|
|
1773
|
-
log.warn(`${reason}. Scaffolding without deployment; configure the generated .env file and run the deploy script later.`);
|
|
1774
|
-
return null;
|
|
1775
|
-
}
|
|
1776
|
-
return {
|
|
1777
|
-
template: options.template,
|
|
1778
|
-
packageManager: options.packageManager,
|
|
1779
|
-
projectName: options.projectName,
|
|
1780
|
-
useComposerPostgres: false,
|
|
1781
|
-
verbose: options.verbose
|
|
1782
|
-
};
|
|
1783
|
-
}
|
|
1784
|
-
async function executeComposerDeployContext(params) {
|
|
1785
|
-
const deploy = getRunScriptArgs(params.context.packageManager, "deploy");
|
|
1786
|
-
const deploySpinner = params.context.verbose ? void 0 : spinner();
|
|
1787
|
-
let deployStarted = false;
|
|
1788
|
-
try {
|
|
1789
|
-
await ensurePrismaAuthentication(params.context.packageManager, params.projectDir);
|
|
1790
|
-
if (params.context.verbose) log.step("Prisma deployment output follows.");
|
|
1791
|
-
else {
|
|
1792
|
-
deploySpinner?.start("Deploying to Prisma...");
|
|
1793
|
-
deployStarted = true;
|
|
1794
|
-
}
|
|
1795
|
-
await execa(deploy.command, deploy.args, {
|
|
1796
|
-
cwd: params.projectDir,
|
|
1797
|
-
env: process.env,
|
|
1798
|
-
stdio: params.context.verbose ? "inherit" : "pipe"
|
|
1799
|
-
});
|
|
1800
|
-
if (params.context.verbose) log.success("Deployed to Prisma.");
|
|
1801
|
-
else deploySpinner?.stop("Deployed to Prisma.");
|
|
1802
|
-
return {
|
|
1803
|
-
ok: true,
|
|
1804
|
-
result: { appName: params.context.projectName }
|
|
1805
|
-
};
|
|
1806
|
-
} catch (error) {
|
|
1807
|
-
if (deployStarted) deploySpinner?.stop("Deployment failed.");
|
|
1808
|
-
const deployError = new Error(getErrorMessage(error));
|
|
1809
|
-
log.error(`Deploy failed: ${deployError.message}`);
|
|
1810
|
-
return {
|
|
1811
|
-
ok: false,
|
|
1812
|
-
cancelled: false,
|
|
1813
|
-
error: deployError
|
|
1814
|
-
};
|
|
1815
|
-
}
|
|
1816
|
-
}
|
|
1817
|
-
//#endregion
|
|
1818
|
-
//#region src/telemetry/client.ts
|
|
1819
|
-
const TELEMETRY_API_KEY = "";
|
|
1820
|
-
const TELEMETRY_HOST = "https://us.i.posthog.com";
|
|
1821
|
-
const TELEMETRY_CONFIG_FILE = "telemetry.json";
|
|
1822
|
-
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
1823
|
-
function shouldDisableTelemetry() {
|
|
1824
|
-
return true;
|
|
1825
|
-
}
|
|
1826
|
-
function getTelemetryConfigDir() {
|
|
1827
|
-
if (process.platform === "darwin") return path.join(os$1.homedir(), "Library", "Application Support", "create-prisma");
|
|
1828
|
-
if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os$1.homedir(), "AppData", "Roaming"), "create-prisma");
|
|
1829
|
-
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os$1.homedir(), ".config"), "create-prisma");
|
|
1830
|
-
}
|
|
1831
|
-
async function getAnonymousId() {
|
|
1832
|
-
const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
|
|
1833
|
-
try {
|
|
1834
|
-
const config = await fs.readJSON(telemetryConfigPath);
|
|
1835
|
-
if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
|
|
1836
|
-
} catch {}
|
|
1837
|
-
const anonymousId = randomUUID();
|
|
1838
|
-
try {
|
|
1839
|
-
await fs.ensureDir(path.dirname(telemetryConfigPath));
|
|
1840
|
-
await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
|
|
1841
|
-
} catch {}
|
|
1842
|
-
return anonymousId;
|
|
1843
|
-
}
|
|
1844
|
-
function getCommonProperties() {
|
|
1845
|
-
return {
|
|
1846
|
-
"cli-version": "0.7.1-pr.52.181.1",
|
|
1847
|
-
"node-version": process.version,
|
|
1848
|
-
platform: process.platform,
|
|
1849
|
-
arch: process.arch
|
|
1850
|
-
};
|
|
1851
|
-
}
|
|
1852
|
-
function sanitizeProperties(properties) {
|
|
1853
|
-
return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
|
|
1854
|
-
}
|
|
1855
|
-
async function trackCliTelemetry(event, properties) {
|
|
1856
|
-
if (shouldDisableTelemetry()) return;
|
|
1857
|
-
let client;
|
|
1858
|
-
try {
|
|
1859
|
-
const distinctId = await getAnonymousId();
|
|
1860
|
-
const sanitizedProperties = sanitizeProperties({
|
|
1861
|
-
...getCommonProperties(),
|
|
1862
|
-
...properties,
|
|
1863
|
-
$process_person_profile: false
|
|
1864
|
-
});
|
|
1865
|
-
client = new PostHog(TELEMETRY_API_KEY, {
|
|
1866
|
-
host: TELEMETRY_HOST,
|
|
1867
|
-
disableGeoip: true,
|
|
1868
|
-
flushAt: 1,
|
|
1869
|
-
flushInterval: 0
|
|
1870
|
-
});
|
|
1871
|
-
await client.captureImmediate({
|
|
1872
|
-
distinctId,
|
|
1873
|
-
event,
|
|
1874
|
-
properties: sanitizedProperties,
|
|
1875
|
-
disableGeoip: true
|
|
1876
|
-
});
|
|
1877
|
-
} catch {} finally {
|
|
1878
|
-
if (client) await client.shutdown().catch(() => {});
|
|
1879
|
-
}
|
|
1880
|
-
}
|
|
1881
|
-
//#endregion
|
|
1882
|
-
//#region src/telemetry/create.ts
|
|
1883
|
-
function getRequestedAddons(input) {
|
|
1884
|
-
const addons = [];
|
|
1885
|
-
if (input.skills === true) addons.push("skills");
|
|
1886
|
-
if (input.mcp === true) addons.push("mcp");
|
|
1887
|
-
if (input.extension === true) addons.push("extension");
|
|
1888
|
-
return addons;
|
|
1889
|
-
}
|
|
1890
|
-
function getTargetDirectoryState(context) {
|
|
1891
|
-
if (!context.targetPathState.exists) return "new";
|
|
1892
|
-
if (context.targetPathState.isEmptyDirectory) return "empty_directory";
|
|
1893
|
-
return "non_empty_directory";
|
|
1894
|
-
}
|
|
1895
|
-
function getBaseCreateProperties(input, context) {
|
|
1896
|
-
const resolvedAddons = context?.addonSetupContext?.addons ?? getRequestedAddons(input);
|
|
1897
|
-
return {
|
|
1898
|
-
command: "create",
|
|
1899
|
-
"uses-defaults": input.yes === true,
|
|
1900
|
-
verbose: input.verbose === true,
|
|
1901
|
-
force: input.force === true,
|
|
1902
|
-
template: context?.template ?? input.template ?? null,
|
|
1903
|
-
"database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
|
|
1904
|
-
"package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
|
|
1905
|
-
"should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
|
|
1906
|
-
"should-generate": true,
|
|
1907
|
-
"uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? (input.provider === void 0 ? null : input.provider === "postgresql"),
|
|
1908
|
-
addons: resolvedAddons,
|
|
1909
|
-
"addon-count": resolvedAddons.length,
|
|
1910
|
-
"addon-scope": context?.addonSetupContext?.scope ?? null,
|
|
1911
|
-
"skills-count": context?.addonSetupContext?.skills.length ?? null,
|
|
1912
|
-
"skills-agents-count": context?.addonSetupContext?.skillsAgents.length ?? null,
|
|
1913
|
-
"mcp-agents-count": context?.addonSetupContext?.mcpAgents.length ?? null,
|
|
1914
|
-
"extension-target-count": context?.addonSetupContext?.extensionTargets.length ?? null,
|
|
1915
|
-
"target-directory-state": context ? getTargetDirectoryState(context) : null
|
|
1916
|
-
};
|
|
1917
|
-
}
|
|
1918
|
-
function getErrorName(error) {
|
|
1919
|
-
if (error instanceof Error) return error.name;
|
|
1920
|
-
return error === void 0 ? null : "UnknownError";
|
|
1921
|
-
}
|
|
1922
|
-
function getErrorCode(error) {
|
|
1923
|
-
if (typeof error !== "object" || error === null) return null;
|
|
1924
|
-
const exitCode = Reflect.get(error, "exitCode");
|
|
1925
|
-
if (typeof exitCode === "number") return exitCode;
|
|
1926
|
-
const code = Reflect.get(error, "code");
|
|
1927
|
-
return typeof code === "number" || typeof code === "string" ? code : null;
|
|
1928
|
-
}
|
|
1929
|
-
async function trackCreateCompleted(params) {
|
|
1930
|
-
await trackCliTelemetry("cli:create_command_completed", {
|
|
1931
|
-
...getBaseCreateProperties(params.input, params.context),
|
|
1932
|
-
"duration-ms": params.durationMs
|
|
1933
|
-
});
|
|
1934
|
-
}
|
|
1935
|
-
async function trackCreateFailed(params) {
|
|
1936
|
-
await trackCliTelemetry("cli:create_command_failed", {
|
|
1937
|
-
...getBaseCreateProperties(params.input, params.context),
|
|
1938
|
-
"duration-ms": params.durationMs,
|
|
1939
|
-
"failure-stage": params.stage,
|
|
1940
|
-
"error-name": getErrorName(params.error),
|
|
1941
|
-
"error-code": getErrorCode(params.error)
|
|
1942
|
-
});
|
|
1943
|
-
}
|
|
1944
|
-
//#endregion
|
|
1945
|
-
//#region src/ui/branding.ts
|
|
1946
|
-
const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
|
|
1947
|
-
function getCreatePrismaIntro() {
|
|
1948
|
-
return prismaTitle;
|
|
1949
|
-
}
|
|
1950
|
-
//#endregion
|
|
1951
|
-
//#region src/commands/create.ts
|
|
1952
|
-
const DEFAULT_PROJECT_NAME = "my-app";
|
|
1953
|
-
const DEFAULT_TEMPLATE = "hono";
|
|
1954
|
-
function toPackageName(projectName) {
|
|
1955
|
-
return projectName.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+/, "").replace(/-+$/, "") || "app";
|
|
1956
|
-
}
|
|
1957
|
-
function formatPathForDisplay(filePath) {
|
|
1958
|
-
return path.relative(process.cwd(), filePath) || ".";
|
|
1959
|
-
}
|
|
1960
|
-
function validateProjectName(value) {
|
|
1961
|
-
const trimmed = String(value ?? "").trim();
|
|
1962
|
-
if (trimmed.length === 0) return "Please enter a project name.";
|
|
1963
|
-
if (trimmed === "..") return "Project name cannot be '..'.";
|
|
1964
|
-
if (path.isAbsolute(trimmed)) return "Use a relative project name instead of an absolute path.";
|
|
1965
|
-
}
|
|
1966
|
-
async function promptForProjectName() {
|
|
1967
|
-
const projectName = await text({
|
|
1968
|
-
message: "Project name",
|
|
1969
|
-
placeholder: DEFAULT_PROJECT_NAME,
|
|
1970
|
-
initialValue: DEFAULT_PROJECT_NAME,
|
|
1971
|
-
validate: validateProjectName
|
|
1972
|
-
});
|
|
1973
|
-
if (isCancel(projectName)) {
|
|
1974
|
-
cancel("Operation cancelled.");
|
|
1975
|
-
return;
|
|
1976
|
-
}
|
|
1977
|
-
return String(projectName).trim();
|
|
1978
|
-
}
|
|
1979
|
-
async function promptForCreateTemplate() {
|
|
1980
|
-
const template = await select({
|
|
1981
|
-
message: "Select template",
|
|
1982
|
-
initialValue: DEFAULT_TEMPLATE,
|
|
1983
|
-
options: [
|
|
1984
|
-
{
|
|
1985
|
-
value: "hono",
|
|
1986
|
-
label: "Hono",
|
|
1987
|
-
hint: "TypeScript API starter"
|
|
1988
|
-
},
|
|
1989
|
-
{
|
|
1990
|
-
value: "elysia",
|
|
1991
|
-
label: "Elysia",
|
|
1992
|
-
hint: "TypeScript API starter with Elysia's Node adapter"
|
|
1993
|
-
},
|
|
1994
|
-
{
|
|
1995
|
-
value: "nest",
|
|
1996
|
-
label: "NestJS",
|
|
1997
|
-
hint: "Official Nest-style API starter with a Prisma service"
|
|
1998
|
-
},
|
|
1999
|
-
{
|
|
2000
|
-
value: "next",
|
|
2001
|
-
label: "Next.js",
|
|
2002
|
-
hint: "App Router + TypeScript starter"
|
|
2003
|
-
},
|
|
2004
|
-
{
|
|
2005
|
-
value: "svelte",
|
|
2006
|
-
label: "SvelteKit",
|
|
2007
|
-
hint: "Official minimal SvelteKit + TypeScript starter"
|
|
2008
|
-
},
|
|
2009
|
-
{
|
|
2010
|
-
value: "astro",
|
|
2011
|
-
label: "Astro",
|
|
2012
|
-
hint: "Official minimal Astro starter with API route example"
|
|
2013
|
-
},
|
|
2014
|
-
{
|
|
2015
|
-
value: "nuxt",
|
|
2016
|
-
label: "Nuxt",
|
|
2017
|
-
hint: "Official minimal Nuxt starter with Nitro API route example"
|
|
2018
|
-
},
|
|
2019
|
-
{
|
|
2020
|
-
value: "tanstack-start",
|
|
2021
|
-
label: "TanStack Start",
|
|
2022
|
-
hint: "TanStack Start React app with file routes and server functions"
|
|
2023
|
-
},
|
|
2024
|
-
{
|
|
2025
|
-
value: "turborepo",
|
|
2026
|
-
label: "Turborepo",
|
|
2027
|
-
hint: "Monorepo starter with apps + packages/db Prisma package"
|
|
2028
|
-
}
|
|
2029
|
-
]
|
|
2030
|
-
});
|
|
2031
|
-
if (isCancel(template)) {
|
|
2032
|
-
cancel("Operation cancelled.");
|
|
2033
|
-
return;
|
|
2034
|
-
}
|
|
2035
|
-
return CreateTemplateSchema.parse(template);
|
|
2036
|
-
}
|
|
2037
|
-
async function inspectTargetPath(targetPath) {
|
|
2038
|
-
if (!await fs.pathExists(targetPath)) return {
|
|
2039
|
-
exists: false,
|
|
2040
|
-
isDirectory: true,
|
|
2041
|
-
isEmptyDirectory: true
|
|
2042
|
-
};
|
|
2043
|
-
if (!(await fs.stat(targetPath)).isDirectory()) return {
|
|
2044
|
-
exists: true,
|
|
2045
|
-
isDirectory: false,
|
|
2046
|
-
isEmptyDirectory: false
|
|
2047
|
-
};
|
|
2048
|
-
return {
|
|
2049
|
-
exists: true,
|
|
2050
|
-
isDirectory: true,
|
|
2051
|
-
isEmptyDirectory: (await fs.readdir(targetPath)).length === 0
|
|
2052
|
-
};
|
|
2053
|
-
}
|
|
2054
|
-
async function runCreateCommand(rawInput = {}) {
|
|
2055
|
-
const startedAt = Date.now();
|
|
2056
|
-
let input = {};
|
|
2057
|
-
let context;
|
|
2058
|
-
let failureStage = "validate_input";
|
|
2059
|
-
try {
|
|
2060
|
-
input = CreateCommandInputSchema.parse(rawInput);
|
|
2061
|
-
intro(getCreatePrismaIntro());
|
|
2062
|
-
failureStage = "collect_context";
|
|
2063
|
-
context = await collectCreateContext(input);
|
|
2064
|
-
if (!context) return;
|
|
2065
|
-
failureStage = "unknown";
|
|
2066
|
-
const executionResult = await executeCreateContext(context);
|
|
2067
|
-
if (!executionResult.ok) {
|
|
2068
|
-
failureStage = executionResult.stage;
|
|
2069
|
-
throw executionResult.error instanceof Error ? executionResult.error : new Error(executionResult.error === void 0 ? `Create command failed during ${executionResult.stage}` : String(executionResult.error));
|
|
2070
|
-
}
|
|
2071
|
-
await trackCreateCompleted({
|
|
2072
|
-
input,
|
|
2073
|
-
context,
|
|
2074
|
-
durationMs: Date.now() - startedAt
|
|
2075
|
-
});
|
|
2076
|
-
} catch (error) {
|
|
2077
|
-
const commandError = error instanceof Error ? error : new Error(String(error));
|
|
2078
|
-
cancel(`Create command failed: ${commandError.message}`);
|
|
2079
|
-
try {
|
|
2080
|
-
await trackCreateFailed({
|
|
2081
|
-
input,
|
|
2082
|
-
context,
|
|
2083
|
-
durationMs: Date.now() - startedAt,
|
|
2084
|
-
error: commandError,
|
|
2085
|
-
stage: failureStage
|
|
2086
|
-
});
|
|
2087
|
-
} catch {}
|
|
2088
|
-
throw commandError;
|
|
2089
|
-
}
|
|
2090
|
-
}
|
|
2091
|
-
async function collectCreateContext(input) {
|
|
2092
|
-
const useDefaults = input.yes === true;
|
|
2093
|
-
const force = input.force === true;
|
|
2094
|
-
const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
|
|
2095
|
-
if (projectNameInput === void 0) return;
|
|
2096
|
-
const projectName = String(projectNameInput).trim();
|
|
2097
|
-
const projectNameValidationError = validateProjectName(projectName);
|
|
2098
|
-
if (projectNameValidationError) {
|
|
2099
|
-
cancel(projectNameValidationError);
|
|
2100
|
-
return;
|
|
2101
|
-
}
|
|
2102
|
-
const template = input.template ?? (useDefaults ? DEFAULT_TEMPLATE : await promptForCreateTemplate());
|
|
2103
|
-
if (!template) return;
|
|
2104
|
-
const targetDirectory = path.resolve(process.cwd(), projectName);
|
|
2105
|
-
const targetPathState = await inspectTargetPath(targetDirectory);
|
|
2106
|
-
if (targetPathState.exists && !targetPathState.isDirectory) {
|
|
2107
|
-
cancel(`Target path ${formatPathForDisplay(targetDirectory)} already exists and is not a directory. Choose a different project name.`);
|
|
2108
|
-
return;
|
|
2109
|
-
}
|
|
2110
|
-
if (targetPathState.exists && !targetPathState.isEmptyDirectory && !force) {
|
|
2111
|
-
cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
|
|
2112
|
-
return;
|
|
2113
|
-
}
|
|
2114
|
-
const prismaSetupInitialContext = await collectPrismaSetupInitialContext(input, { projectDir: targetDirectory });
|
|
2115
|
-
if (!prismaSetupInitialContext) return;
|
|
2116
|
-
const projectPackageName = toPackageName(path.basename(targetDirectory));
|
|
2117
|
-
const composerDeployContext = await collectComposerDeployContext(input, {
|
|
2118
|
-
template,
|
|
2119
|
-
databaseProvider: prismaSetupInitialContext.databaseProvider,
|
|
2120
|
-
packageManager: prismaSetupInitialContext.packageManager,
|
|
2121
|
-
projectName: projectPackageName,
|
|
2122
|
-
useDefaults,
|
|
2123
|
-
verbose: prismaSetupInitialContext.verbose
|
|
2124
|
-
});
|
|
2125
|
-
if (composerDeployContext === void 0) return;
|
|
2126
|
-
const prismaSetupContext = await completePrismaSetupContext(prismaSetupInitialContext, { shouldMigrateAndSeed: Boolean(composerDeployContext) });
|
|
2127
|
-
if (!prismaSetupContext) return;
|
|
2128
|
-
const useComposerPostgres = prismaSetupContext.shouldUsePrismaPostgres;
|
|
2129
|
-
if (composerDeployContext) composerDeployContext.useComposerPostgres = useComposerPostgres;
|
|
2130
|
-
if (composerDeployContext && useComposerPostgres) log.info("Composer will provision Prisma Postgres and wire it into the application.");
|
|
2131
|
-
const composerScaffoldContext = composerDeployContext ?? {
|
|
2132
|
-
template,
|
|
2133
|
-
packageManager: prismaSetupContext.packageManager,
|
|
2134
|
-
projectName: projectPackageName,
|
|
2135
|
-
useComposerPostgres,
|
|
2136
|
-
verbose: prismaSetupContext.verbose
|
|
2137
|
-
};
|
|
2138
|
-
const addonSetupContext = await collectCreateAddonSetupContext(input, {
|
|
2139
|
-
useDefaults,
|
|
2140
|
-
provider: prismaSetupContext.databaseProvider,
|
|
2141
|
-
shouldUsePrismaPostgres: prismaSetupContext.shouldUsePrismaPostgres
|
|
2142
|
-
});
|
|
2143
|
-
if (addonSetupContext === void 0) return;
|
|
2144
|
-
return {
|
|
2145
|
-
targetDirectory,
|
|
2146
|
-
targetPathState,
|
|
2147
|
-
force,
|
|
2148
|
-
template,
|
|
2149
|
-
projectPackageName,
|
|
2150
|
-
prismaSetupContext,
|
|
2151
|
-
addonSetupContext: addonSetupContext ?? void 0,
|
|
2152
|
-
composerScaffoldContext,
|
|
2153
|
-
composerDeployContext: composerDeployContext ?? void 0,
|
|
2154
|
-
useComposerPostgres
|
|
2155
|
-
};
|
|
2156
|
-
}
|
|
2157
|
-
async function executeCreateContext(context) {
|
|
2158
|
-
const scaffoldSpinner = spinner();
|
|
2159
|
-
scaffoldSpinner.start(`Scaffolding ${context.template} project...`);
|
|
2160
|
-
try {
|
|
2161
|
-
await scaffoldCreateTemplate({
|
|
2162
|
-
projectDir: context.targetDirectory,
|
|
2163
|
-
projectName: context.projectPackageName,
|
|
2164
|
-
template: context.template,
|
|
2165
|
-
provider: context.prismaSetupContext.databaseProvider,
|
|
2166
|
-
packageManager: context.prismaSetupContext.packageManager,
|
|
2167
|
-
composerPostgres: context.useComposerPostgres
|
|
2168
|
-
});
|
|
2169
|
-
scaffoldSpinner.stop("Project files scaffolded.");
|
|
2170
|
-
} catch (error) {
|
|
2171
|
-
scaffoldSpinner.stop("Could not scaffold project files.");
|
|
2172
|
-
return {
|
|
2173
|
-
ok: false,
|
|
2174
|
-
stage: "scaffold_template",
|
|
2175
|
-
error
|
|
2176
|
-
};
|
|
2177
|
-
}
|
|
2178
|
-
try {
|
|
2179
|
-
await writeCreateTemplateDependencies({
|
|
2180
|
-
template: context.template,
|
|
2181
|
-
packageManager: context.prismaSetupContext.packageManager,
|
|
2182
|
-
projectDir: context.targetDirectory
|
|
2183
|
-
});
|
|
2184
|
-
await addPackageDependency({
|
|
2185
|
-
scripts: getComposerDeployScriptMap(context.composerScaffoldContext),
|
|
2186
|
-
scriptMode: "if-missing",
|
|
2187
|
-
projectDir: context.targetDirectory
|
|
2188
|
-
});
|
|
2189
|
-
} catch (error) {
|
|
2190
|
-
return {
|
|
2191
|
-
ok: false,
|
|
2192
|
-
stage: "scaffold_template",
|
|
2193
|
-
error
|
|
2194
|
-
};
|
|
2195
|
-
}
|
|
2196
|
-
if (context.targetPathState.exists && !context.targetPathState.isEmptyDirectory && context.force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(context.targetDirectory)}.`);
|
|
2197
|
-
const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [`- cd ${formatPathForDisplay(context.targetDirectory)}`];
|
|
2198
|
-
if (context.addonSetupContext) try {
|
|
2199
|
-
await executeCreateAddonSetupContext({
|
|
2200
|
-
context: context.addonSetupContext,
|
|
2201
|
-
packageManager: context.prismaSetupContext.packageManager,
|
|
2202
|
-
projectDir: context.targetDirectory,
|
|
2203
|
-
verbose: context.prismaSetupContext.verbose
|
|
2204
|
-
});
|
|
2205
|
-
} catch (error) {
|
|
2206
|
-
return {
|
|
2207
|
-
ok: false,
|
|
2208
|
-
stage: "addons",
|
|
2209
|
-
error
|
|
2210
|
-
};
|
|
2211
|
-
}
|
|
2212
|
-
let prismaResult;
|
|
2213
|
-
try {
|
|
2214
|
-
prismaResult = await executePrismaSetupContext(Boolean(context.composerDeployContext && context.useComposerPostgres) ? {
|
|
2215
|
-
...context.prismaSetupContext,
|
|
2216
|
-
shouldMigrateAndSeed: false
|
|
2217
|
-
} : context.prismaSetupContext, {
|
|
2218
|
-
prependNextSteps: nextSteps,
|
|
2219
|
-
projectDir: context.targetDirectory,
|
|
2220
|
-
includeDevNextStep: true,
|
|
2221
|
-
includeMigrationAndSeedNextSteps: !context.useComposerPostgres
|
|
2222
|
-
});
|
|
2223
|
-
if (!prismaResult.ok) return {
|
|
2224
|
-
ok: false,
|
|
2225
|
-
stage: "prisma_setup"
|
|
2226
|
-
};
|
|
2227
|
-
if (context.composerDeployContext && (!prismaResult.didGenerateClient || !prismaResult.didGenerateMigration)) return {
|
|
2228
|
-
ok: false,
|
|
2229
|
-
stage: "prisma_setup",
|
|
2230
|
-
error: /* @__PURE__ */ new Error("Could not generate Prisma Client and the initial migration required for deployment.")
|
|
2231
|
-
};
|
|
2232
|
-
} catch (error) {
|
|
2233
|
-
return {
|
|
2234
|
-
ok: false,
|
|
2235
|
-
stage: "prisma_setup",
|
|
2236
|
-
error
|
|
2237
|
-
};
|
|
2238
|
-
}
|
|
2239
|
-
let deployResult;
|
|
2240
|
-
if (context.composerDeployContext) try {
|
|
2241
|
-
const result = await executeComposerDeployContext({
|
|
2242
|
-
context: context.composerDeployContext,
|
|
2243
|
-
projectDir: context.targetDirectory
|
|
2244
|
-
});
|
|
2245
|
-
if (!result.ok && !result.cancelled) return {
|
|
2246
|
-
ok: false,
|
|
2247
|
-
stage: "composer_deploy",
|
|
2248
|
-
error: result.error
|
|
2249
|
-
};
|
|
2250
|
-
if (!result.ok) prismaResult.warningSection += "\n\n- Prisma deployment was cancelled.";
|
|
2251
|
-
if (result.ok) deployResult = result.result;
|
|
2252
|
-
} catch (error) {
|
|
2253
|
-
return {
|
|
2254
|
-
ok: false,
|
|
2255
|
-
stage: "composer_deploy",
|
|
2256
|
-
error
|
|
2257
|
-
};
|
|
2258
|
-
}
|
|
2259
|
-
if (!deployResult) {
|
|
2260
|
-
const deployNextStep = `- ${getRunScriptCommand(context.prismaSetupContext.packageManager, "deploy")}`;
|
|
2261
|
-
if (!prismaResult.nextSteps.includes(deployNextStep)) prismaResult.nextSteps.push(deployNextStep);
|
|
2262
|
-
}
|
|
2263
|
-
const summaryLines = [];
|
|
2264
|
-
summaryLines.push(`Setup complete.${prismaResult.warningSection}`);
|
|
2265
|
-
if (deployResult) summaryLines.push("", "Deployed to Prisma:", `- App: ${deployResult.appName}`);
|
|
2266
|
-
summaryLines.push("", "Next steps:", prismaResult.nextSteps.join("\n"));
|
|
2267
|
-
outro(summaryLines.join("\n"));
|
|
2268
|
-
return { ok: true };
|
|
2269
|
-
}
|
|
2270
|
-
//#endregion
|
|
2271
|
-
//#region src/index.ts
|
|
2272
|
-
const CLI_VERSION = "0.7.1-pr.52.181.1";
|
|
2273
|
-
const router = os.router({ create: os.meta({
|
|
2274
|
-
description: "Create a new project with Prisma setup",
|
|
2275
|
-
default: true,
|
|
2276
|
-
negateBooleans: true
|
|
2277
|
-
}).input(CreateCommandInputSchema.optional()).handler(async ({ input }) => {
|
|
2278
|
-
await runCreateCommand(input ?? {});
|
|
2279
|
-
}) });
|
|
2280
|
-
function createCreatePrismaCli() {
|
|
2281
|
-
return createCli({
|
|
2282
|
-
router,
|
|
2283
|
-
name: "create-prisma",
|
|
2284
|
-
version: CLI_VERSION
|
|
2285
|
-
});
|
|
2286
|
-
}
|
|
2287
|
-
async function create(input = {}) {
|
|
2288
|
-
await runCreateCommand(input);
|
|
2289
|
-
}
|
|
2290
|
-
//#endregion
|
|
2291
|
-
export { CreateCommandInputSchema, CreateTemplateSchema, DatabaseProviderSchema, PackageManagerSchema, create, createCreatePrismaCli, router };
|
|
2
|
+
import { a as CreateTemplateSchema, c as PackageManagerSchema, i as CreateCommandInputSchema, l as SchemaPresetSchema, n as createCreatePrismaCli, o as DatabaseProviderSchema, r as router, s as DatabaseUrlSchema, t as create } from "./src-B3hRLZ0U.mjs";
|
|
3
|
+
export { CreateCommandInputSchema, CreateTemplateSchema, DatabaseProviderSchema, DatabaseUrlSchema, PackageManagerSchema, SchemaPresetSchema, create, createCreatePrismaCli, router };
|