create-prisma 0.4.1 → 0.4.2-next.37.80.1
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 +43 -35
- package/dist/cli.mjs +1 -1
- package/dist/{create-H6Tk0JlE.mjs → create-CfH21J9-.mjs} +529 -914
- package/dist/index.d.mts +28 -25
- package/dist/index.mjs +3 -3
- package/package.json +2 -2
- package/templates/create/_shared/packages/db/prisma/seed.ts.hbs +43 -0
- package/templates/create/_shared/prisma/seed.ts.hbs +55 -0
- package/templates/create/astro/README.md.hbs +23 -12
- package/templates/create/astro/deno.json.hbs +1 -1
- package/templates/create/astro/prisma/contract.prisma.hbs +44 -0
- package/templates/create/astro/prisma/contract.ts.hbs +85 -0
- package/templates/create/astro/prisma-next.config.ts.hbs +13 -0
- package/templates/create/astro/src/lib/prisma.ts.hbs +49 -43
- package/templates/create/astro/src/pages/api/users.ts.hbs +5 -12
- package/templates/create/astro/src/pages/index.astro.hbs +19 -21
- package/templates/create/elysia/.yarnrc.yml.hbs +3 -0
- package/templates/create/elysia/README.md.hbs +27 -18
- package/templates/create/elysia/deno.json.hbs +1 -1
- package/templates/create/elysia/prisma/contract.prisma.hbs +44 -0
- package/templates/create/elysia/prisma/contract.ts.hbs +85 -0
- package/templates/create/elysia/prisma-next.config.ts.hbs +13 -0
- package/templates/create/elysia/src/index.ts.hbs +12 -7
- package/templates/create/elysia/src/lib/prisma.ts.hbs +49 -46
- package/templates/create/elysia/tsconfig.json +1 -0
- package/templates/create/hono/README.md.hbs +27 -18
- package/templates/create/hono/deno.json.hbs +1 -1
- package/templates/create/hono/prisma/contract.prisma.hbs +44 -0
- package/templates/create/hono/prisma/contract.ts.hbs +85 -0
- package/templates/create/hono/prisma-next.config.ts.hbs +13 -0
- package/templates/create/hono/src/index.ts.hbs +8 -6
- package/templates/create/hono/src/lib/prisma.ts.hbs +49 -46
- package/templates/create/hono/tsconfig.json +1 -0
- package/templates/create/nest/README.md.hbs +28 -18
- package/templates/create/nest/deno.json.hbs +1 -1
- package/templates/create/nest/prisma/contract.prisma.hbs +44 -0
- package/templates/create/nest/prisma/contract.ts.hbs +85 -0
- package/templates/create/nest/prisma-next.config.ts.hbs +13 -0
- package/templates/create/nest/src/lib/prisma.ts.hbs +49 -48
- package/templates/create/nest/src/prisma.service.ts.hbs +6 -5
- package/templates/create/nest/src/users.service.ts.hbs +1 -6
- package/templates/create/nest/tsconfig.json +1 -0
- package/templates/create/next/README.md.hbs +22 -11
- package/templates/create/next/deno.json.hbs +1 -1
- package/templates/create/next/prisma/contract.prisma.hbs +44 -0
- package/templates/create/next/prisma/contract.ts.hbs +85 -0
- package/templates/create/next/prisma-next.config.ts.hbs +13 -0
- package/templates/create/next/src/app/page.tsx.hbs +21 -26
- package/templates/create/next/src/lib/prisma.ts.hbs +49 -43
- package/templates/create/next/tsconfig.json +1 -0
- package/templates/create/nuxt/README.md.hbs +22 -11
- package/templates/create/nuxt/app/pages/index.vue.hbs +14 -12
- package/templates/create/nuxt/deno.json.hbs +1 -1
- package/templates/create/nuxt/nuxt.config.ts +16 -0
- package/templates/create/nuxt/prisma/contract.prisma.hbs +44 -0
- package/templates/create/nuxt/prisma/contract.ts.hbs +85 -0
- package/templates/create/nuxt/prisma-next.config.ts.hbs +13 -0
- package/templates/create/nuxt/server/api/users.get.ts.hbs +3 -9
- package/templates/create/nuxt/server/utils/prisma.ts.hbs +49 -43
- package/templates/create/svelte/README.md.hbs +23 -12
- package/templates/create/svelte/deno.json.hbs +1 -1
- package/templates/create/svelte/prisma/contract.prisma.hbs +44 -0
- package/templates/create/svelte/prisma/contract.ts.hbs +85 -0
- package/templates/create/svelte/prisma-next.config.ts.hbs +13 -0
- package/templates/create/svelte/src/lib/server/prisma.ts.hbs +50 -44
- package/templates/create/svelte/src/routes/+page.server.ts.hbs +3 -9
- package/templates/create/svelte/src/routes/+page.svelte.hbs +21 -16
- package/templates/create/tanstack-start/README.md.hbs +22 -11
- package/templates/create/tanstack-start/deno.json.hbs +1 -2
- package/templates/create/tanstack-start/prisma/contract.prisma.hbs +44 -0
- package/templates/create/tanstack-start/prisma/contract.ts.hbs +85 -0
- package/templates/create/tanstack-start/prisma-next.config.ts.hbs +13 -0
- package/templates/create/tanstack-start/src/lib/prisma.server.ts.hbs +49 -43
- package/templates/create/tanstack-start/src/routes/__root.tsx.hbs +2 -3
- package/templates/create/tanstack-start/src/routes/index.tsx.hbs +27 -38
- package/templates/create/tanstack-start/tsconfig.json +1 -0
- package/templates/create/turborepo/README.md.hbs +25 -14
- package/templates/create/turborepo/apps/api/deno.json.hbs +6 -0
- package/templates/create/turborepo/apps/api/package.json.hbs +7 -0
- package/templates/create/turborepo/apps/api/src/index.ts.hbs +6 -13
- package/templates/create/turborepo/deno.json.hbs +5 -1
- package/templates/create/turborepo/package.json.hbs +12 -4
- package/templates/create/turborepo/packages/db/deno.json.hbs +6 -0
- package/templates/create/turborepo/packages/db/package.json.hbs +5 -0
- package/templates/create/turborepo/packages/db/prisma/contract.prisma.hbs +44 -0
- package/templates/create/turborepo/packages/db/prisma/contract.ts.hbs +85 -0
- package/templates/create/turborepo/packages/db/prisma-next.config.ts.hbs +13 -0
- package/templates/create/turborepo/packages/db/src/client.ts.hbs +56 -44
- package/templates/create/turborepo/packages/db/src/index.ts +1 -1
- package/templates/create/turborepo/packages/db/tsconfig.json +2 -1
- package/templates/create/turborepo/pnpm-workspace.yaml.hbs +5 -0
- package/templates/create/turborepo/turbo.json +9 -3
- package/templates/create/astro/prisma/schema.prisma.hbs +0 -21
- package/templates/create/astro/prisma/seed.ts.hbs +0 -38
- package/templates/create/astro/prisma.config.ts +0 -13
- package/templates/create/elysia/prisma/schema.prisma.hbs +0 -25
- package/templates/create/elysia/prisma/seed.ts.hbs +0 -42
- package/templates/create/elysia/prisma.config.ts.hbs +0 -15
- package/templates/create/hono/prisma/schema.prisma.hbs +0 -25
- package/templates/create/hono/prisma/seed.ts.hbs +0 -42
- package/templates/create/hono/prisma.config.ts.hbs +0 -15
- package/templates/create/nest/prisma/schema.prisma.hbs +0 -25
- package/templates/create/nest/prisma/seed.ts.hbs +0 -44
- package/templates/create/nest/prisma.config.ts.hbs +0 -15
- package/templates/create/next/prisma/schema.prisma.hbs +0 -21
- package/templates/create/next/prisma/seed.ts.hbs +0 -38
- package/templates/create/next/prisma.config.ts +0 -13
- package/templates/create/nuxt/prisma/schema.prisma.hbs +0 -21
- package/templates/create/nuxt/prisma/seed.ts.hbs +0 -38
- package/templates/create/nuxt/prisma.config.ts +0 -13
- package/templates/create/svelte/prisma/schema.prisma.hbs +0 -21
- package/templates/create/svelte/prisma/seed.ts.hbs +0 -87
- package/templates/create/svelte/prisma.config.ts +0 -13
- package/templates/create/tanstack-start/prisma/schema.prisma.hbs +0 -21
- package/templates/create/tanstack-start/prisma/seed.ts.hbs +0 -37
- package/templates/create/tanstack-start/prisma.config.ts +0 -13
- package/templates/create/turborepo/packages/db/prisma/schema.prisma.hbs +0 -21
- package/templates/create/turborepo/packages/db/prisma/seed.ts.hbs +0 -38
- package/templates/create/turborepo/packages/db/prisma.config.ts +0 -13
|
@@ -1,17 +1,133 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { cancel, confirm, intro, isCancel, log,
|
|
2
|
+
import { cancel, confirm, intro, isCancel, log, note, outro, select, spinner, text } from "@clack/prompts";
|
|
3
3
|
import fs from "fs-extra";
|
|
4
4
|
import path from "node:path";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import { PostHog } from "posthog-node";
|
|
5
8
|
import Handlebars from "handlebars";
|
|
6
9
|
import { existsSync } from "node:fs";
|
|
7
10
|
import { fileURLToPath } from "node:url";
|
|
8
11
|
import { z } from "zod";
|
|
9
12
|
import { execa } from "execa";
|
|
10
|
-
import { randomUUID } from "node:crypto";
|
|
11
|
-
import os from "node:os";
|
|
12
|
-
import { PostHog } from "posthog-node";
|
|
13
13
|
import { styleText } from "node:util";
|
|
14
14
|
|
|
15
|
+
//#region src/telemetry/client.ts
|
|
16
|
+
const TELEMETRY_API_KEY = "";
|
|
17
|
+
const TELEMETRY_HOST = "https://us.i.posthog.com";
|
|
18
|
+
const TELEMETRY_CONFIG_FILE = "telemetry.json";
|
|
19
|
+
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;
|
|
20
|
+
function shouldDisableTelemetry() {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
function getTelemetryConfigDir() {
|
|
24
|
+
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
|
|
25
|
+
if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
|
|
26
|
+
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
|
|
27
|
+
}
|
|
28
|
+
async function getAnonymousId() {
|
|
29
|
+
const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
|
|
30
|
+
try {
|
|
31
|
+
const config = await fs.readJSON(telemetryConfigPath);
|
|
32
|
+
if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
|
|
33
|
+
} catch {}
|
|
34
|
+
const anonymousId = randomUUID();
|
|
35
|
+
try {
|
|
36
|
+
await fs.ensureDir(path.dirname(telemetryConfigPath));
|
|
37
|
+
await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
|
|
38
|
+
} catch {}
|
|
39
|
+
return anonymousId;
|
|
40
|
+
}
|
|
41
|
+
function getCommonProperties() {
|
|
42
|
+
return {
|
|
43
|
+
"cli-version": "0.4.2-next.37.80.1",
|
|
44
|
+
"node-version": process.version,
|
|
45
|
+
platform: process.platform,
|
|
46
|
+
arch: process.arch
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function sanitizeProperties(properties) {
|
|
50
|
+
return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
|
|
51
|
+
}
|
|
52
|
+
async function trackCliTelemetry(event, properties) {
|
|
53
|
+
if (shouldDisableTelemetry()) return;
|
|
54
|
+
let client;
|
|
55
|
+
try {
|
|
56
|
+
const distinctId = await getAnonymousId();
|
|
57
|
+
const sanitizedProperties = sanitizeProperties({
|
|
58
|
+
...getCommonProperties(),
|
|
59
|
+
...properties,
|
|
60
|
+
$process_person_profile: false
|
|
61
|
+
});
|
|
62
|
+
client = new PostHog(TELEMETRY_API_KEY, {
|
|
63
|
+
host: TELEMETRY_HOST,
|
|
64
|
+
disableGeoip: true,
|
|
65
|
+
flushAt: 1,
|
|
66
|
+
flushInterval: 0
|
|
67
|
+
});
|
|
68
|
+
await client.captureImmediate({
|
|
69
|
+
distinctId,
|
|
70
|
+
event,
|
|
71
|
+
properties: sanitizedProperties,
|
|
72
|
+
disableGeoip: true
|
|
73
|
+
});
|
|
74
|
+
} catch {} finally {
|
|
75
|
+
if (client) await client.shutdown().catch(() => {});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
//#endregion
|
|
80
|
+
//#region src/telemetry/create.ts
|
|
81
|
+
function getTargetDirectoryState(context) {
|
|
82
|
+
if (!context.targetPathState.exists) return "new";
|
|
83
|
+
if (context.targetPathState.isEmptyDirectory) return "empty_directory";
|
|
84
|
+
return "non_empty_directory";
|
|
85
|
+
}
|
|
86
|
+
function getBaseCreateProperties(input, context) {
|
|
87
|
+
return {
|
|
88
|
+
command: "create",
|
|
89
|
+
"uses-defaults": input.yes === true,
|
|
90
|
+
verbose: input.verbose === true,
|
|
91
|
+
force: input.force === true,
|
|
92
|
+
template: context?.template ?? input.template ?? null,
|
|
93
|
+
"database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
|
|
94
|
+
"authoring-style": context?.prismaSetupContext.authoring ?? input.authoring ?? null,
|
|
95
|
+
"package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
|
|
96
|
+
"schema-preset": context?.prismaSetupContext.schemaPreset ?? input.schemaPreset ?? null,
|
|
97
|
+
"should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
|
|
98
|
+
"should-emit": context?.prismaSetupContext.shouldEmit ?? input.emit ?? null,
|
|
99
|
+
"uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
|
|
100
|
+
"target-directory-state": context ? getTargetDirectoryState(context) : null
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function getErrorName(error) {
|
|
104
|
+
if (error instanceof Error) return error.name;
|
|
105
|
+
return error === void 0 ? null : "UnknownError";
|
|
106
|
+
}
|
|
107
|
+
function getErrorCode(error) {
|
|
108
|
+
if (typeof error !== "object" || error === null) return null;
|
|
109
|
+
const exitCode = Reflect.get(error, "exitCode");
|
|
110
|
+
if (typeof exitCode === "number") return exitCode;
|
|
111
|
+
const code = Reflect.get(error, "code");
|
|
112
|
+
return typeof code === "number" || typeof code === "string" ? code : null;
|
|
113
|
+
}
|
|
114
|
+
async function trackCreateCompleted(params) {
|
|
115
|
+
await trackCliTelemetry("cli:create_command_completed", {
|
|
116
|
+
...getBaseCreateProperties(params.input, params.context),
|
|
117
|
+
"duration-ms": params.durationMs
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function trackCreateFailed(params) {
|
|
121
|
+
await trackCliTelemetry("cli:create_command_failed", {
|
|
122
|
+
...getBaseCreateProperties(params.input, params.context),
|
|
123
|
+
"duration-ms": params.durationMs,
|
|
124
|
+
"failure-stage": params.stage,
|
|
125
|
+
"error-name": getErrorName(params.error),
|
|
126
|
+
"error-code": getErrorCode(params.error)
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
15
131
|
//#region src/utils/runtime.ts
|
|
16
132
|
function usesNodeStyleRuntime(packageManager) {
|
|
17
133
|
return packageManager !== void 0 && packageManager !== "bun" && packageManager !== "deno";
|
|
@@ -24,15 +140,23 @@ function requiresDotenvConfigImport(packageManager) {
|
|
|
24
140
|
//#region src/constants/dependencies.ts
|
|
25
141
|
const dependencyVersionMap = {
|
|
26
142
|
"@elysiajs/node": "^1.4.5",
|
|
27
|
-
"@prisma/
|
|
28
|
-
"@prisma/adapter-
|
|
29
|
-
"@prisma/
|
|
30
|
-
"@prisma/
|
|
31
|
-
"@prisma/
|
|
32
|
-
"@
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
prisma: "
|
|
143
|
+
"@prisma-next/adapter-mongo": "0.4.4",
|
|
144
|
+
"@prisma-next/adapter-postgres": "0.4.4",
|
|
145
|
+
"@prisma-next/cli": "0.4.4",
|
|
146
|
+
"@prisma-next/contract": "0.4.4",
|
|
147
|
+
"@prisma-next/family-mongo": "0.4.4",
|
|
148
|
+
"@prisma-next/family-sql": "0.4.4",
|
|
149
|
+
"@prisma-next/mongo": "0.4.4",
|
|
150
|
+
"@prisma-next/mongo-contract": "0.4.4",
|
|
151
|
+
"@prisma-next/mongo-contract-ts": "0.4.4",
|
|
152
|
+
"@prisma-next/postgres": "0.4.4",
|
|
153
|
+
"@prisma-next/sql-contract": "0.4.4",
|
|
154
|
+
"@prisma-next/sql-contract-ts": "0.4.4",
|
|
155
|
+
"@prisma-next/target-mongo": "0.4.4",
|
|
156
|
+
"@prisma-next/target-postgres": "0.4.4",
|
|
157
|
+
"@types/node": "^25.6.2",
|
|
158
|
+
dotenv: "^17.4.2",
|
|
159
|
+
"prisma-next": "0.4.4",
|
|
36
160
|
tsx: "^4.21.0"
|
|
37
161
|
};
|
|
38
162
|
function getWorkspaceDependencyVersion(packageManager) {
|
|
@@ -64,12 +188,11 @@ function getCreateTemplateDependencies(template, packageManager) {
|
|
|
64
188
|
|
|
65
189
|
//#endregion
|
|
66
190
|
//#region src/types.ts
|
|
67
|
-
const
|
|
191
|
+
const databaseProviderInputs = [
|
|
192
|
+
"postgres",
|
|
68
193
|
"postgresql",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"sqlserver",
|
|
72
|
-
"cockroachdb"
|
|
194
|
+
"mongo",
|
|
195
|
+
"mongodb"
|
|
73
196
|
];
|
|
74
197
|
const packageManagers = [
|
|
75
198
|
"npm",
|
|
@@ -79,6 +202,7 @@ const packageManagers = [
|
|
|
79
202
|
"deno"
|
|
80
203
|
];
|
|
81
204
|
const schemaPresets = ["empty", "basic"];
|
|
205
|
+
const authoringStyles = ["psl", "typescript"];
|
|
82
206
|
const createTemplates = [
|
|
83
207
|
"hono",
|
|
84
208
|
"elysia",
|
|
@@ -90,53 +214,35 @@ const createTemplates = [
|
|
|
90
214
|
"tanstack-start",
|
|
91
215
|
"turborepo"
|
|
92
216
|
];
|
|
93
|
-
|
|
94
|
-
"
|
|
95
|
-
"
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
const extensionTargets = [
|
|
100
|
-
"vscode",
|
|
101
|
-
"cursor",
|
|
102
|
-
"windsurf"
|
|
103
|
-
];
|
|
104
|
-
const prismaSkillNames = [
|
|
105
|
-
"prisma-cli",
|
|
106
|
-
"prisma-client-api",
|
|
107
|
-
"prisma-database-setup",
|
|
108
|
-
"prisma-upgrade-v7",
|
|
109
|
-
"prisma-postgres"
|
|
110
|
-
];
|
|
111
|
-
const DatabaseProviderSchema = z.enum(databaseProviders);
|
|
217
|
+
function normalizeDatabaseProvider(value) {
|
|
218
|
+
if (value === "postgresql") return "postgres";
|
|
219
|
+
if (value === "mongodb") return "mongo";
|
|
220
|
+
return value;
|
|
221
|
+
}
|
|
222
|
+
const DatabaseProviderSchema = z.enum(databaseProviderInputs).transform(normalizeDatabaseProvider);
|
|
112
223
|
const PackageManagerSchema = z.enum(packageManagers);
|
|
113
224
|
const SchemaPresetSchema = z.enum(schemaPresets);
|
|
225
|
+
const AuthoringStyleSchema = z.enum(authoringStyles);
|
|
114
226
|
const CreateTemplateSchema = z.enum(createTemplates);
|
|
115
|
-
const CreateAddonSchema = z.enum(createAddons);
|
|
116
|
-
const AddonInstallScopeSchema = z.enum(addonInstallScopes);
|
|
117
|
-
const ExtensionTargetSchema = z.enum(extensionTargets);
|
|
118
|
-
const PrismaSkillNameSchema = z.enum(prismaSkillNames);
|
|
119
227
|
const DatabaseUrlSchema = z.string().trim().min(1, "Please enter a valid database URL");
|
|
120
228
|
const CommonCommandOptionsSchema = z.object({
|
|
121
229
|
yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
|
|
122
230
|
verbose: z.boolean().optional().describe("Show verbose command output during setup")
|
|
123
231
|
});
|
|
124
232
|
const PrismaSetupOptionsSchema = z.object({
|
|
125
|
-
provider: DatabaseProviderSchema.optional().describe("
|
|
233
|
+
provider: DatabaseProviderSchema.optional().describe("Prisma Next database target: PostgreSQL relational models or MongoDB document models"),
|
|
234
|
+
authoring: AuthoringStyleSchema.optional().describe("Contract authoring style"),
|
|
126
235
|
packageManager: PackageManagerSchema.optional().describe("Package manager used for dependency installation"),
|
|
127
|
-
prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when
|
|
236
|
+
prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when target is postgres"),
|
|
128
237
|
databaseUrl: DatabaseUrlSchema.optional().describe("DATABASE_URL value"),
|
|
129
238
|
install: z.boolean().optional().describe("Install dependencies with selected package manager"),
|
|
130
|
-
|
|
131
|
-
schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/
|
|
239
|
+
emit: z.boolean().optional().describe("Emit Prisma Next contract artifacts after scaffolding"),
|
|
240
|
+
schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/contract.prisma or prisma/contract.ts")
|
|
132
241
|
});
|
|
133
242
|
const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
|
|
134
243
|
const CreateScaffoldOptionsSchema = z.object({
|
|
135
244
|
name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
|
|
136
245
|
template: CreateTemplateSchema.optional().describe("Project template"),
|
|
137
|
-
skills: z.boolean().optional().describe("Enable skills addon"),
|
|
138
|
-
mcp: z.boolean().optional().describe("Enable MCP addon"),
|
|
139
|
-
extension: z.boolean().optional().describe("Enable extension addon"),
|
|
140
246
|
force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
|
|
141
247
|
});
|
|
142
248
|
const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
|
|
@@ -220,14 +326,11 @@ function getPackageManagerManifestValue(packageManager) {
|
|
|
220
326
|
if (!packageManager || packageManager === "deno") return;
|
|
221
327
|
return packageManagerManifestValues[packageManager];
|
|
222
328
|
}
|
|
223
|
-
function getDenoPrismaSpecifier() {
|
|
224
|
-
return `npm:prisma@${dependencyVersionMap.prisma}`;
|
|
225
|
-
}
|
|
226
329
|
function getDenoAllowedScriptSpecifiers() {
|
|
227
330
|
return [
|
|
228
|
-
`npm:prisma@${dependencyVersionMap
|
|
229
|
-
`npm:@prisma/
|
|
230
|
-
`npm:@prisma/
|
|
331
|
+
`npm:prisma-next@${dependencyVersionMap["prisma-next"]}`,
|
|
332
|
+
`npm:@prisma-next/postgres@${dependencyVersionMap["@prisma-next/postgres"]}`,
|
|
333
|
+
`npm:@prisma-next/mongo@${dependencyVersionMap["@prisma-next/mongo"]}`
|
|
231
334
|
].join(",");
|
|
232
335
|
}
|
|
233
336
|
function getInstallCommand(packageManager) {
|
|
@@ -243,6 +346,19 @@ function getRunScriptCommand(packageManager, scriptName) {
|
|
|
243
346
|
default: return `npm run ${scriptName}`;
|
|
244
347
|
}
|
|
245
348
|
}
|
|
349
|
+
function getRunScriptInDirectoryCommand(packageManager, directory, scriptName) {
|
|
350
|
+
switch (packageManager) {
|
|
351
|
+
case "deno": return `deno task --cwd ${directory} ${scriptName}`;
|
|
352
|
+
case "bun": return `bun run --cwd ${directory} ${scriptName}`;
|
|
353
|
+
case "pnpm": return `pnpm --dir ${directory} run ${scriptName}`;
|
|
354
|
+
case "yarn": return `yarn --cwd ${directory} run ${scriptName}`;
|
|
355
|
+
default: return `npm --prefix ${directory} run ${scriptName}`;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function getRunScriptInDirectoryCommandWithArgsPassthrough(packageManager, directory, scriptName) {
|
|
359
|
+
const command = getRunScriptInDirectoryCommand(packageManager, directory, scriptName);
|
|
360
|
+
return packageManager === "npm" ? `${command} --` : command;
|
|
361
|
+
}
|
|
246
362
|
function joinCommandParts(parts) {
|
|
247
363
|
return parts.filter((part) => typeof part === "string" && part.length > 0).join(" ");
|
|
248
364
|
}
|
|
@@ -326,26 +442,46 @@ function getPackageExecutionCommand(packageManager, commandArgs) {
|
|
|
326
442
|
const execution = getPackageExecutionArgs(packageManager, commandArgs);
|
|
327
443
|
return [execution.command, ...execution.args].join(" ");
|
|
328
444
|
}
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
"
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
"
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
445
|
+
function getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs) {
|
|
446
|
+
switch (packageManager) {
|
|
447
|
+
case "pnpm": return {
|
|
448
|
+
command: "pnpm",
|
|
449
|
+
args: [
|
|
450
|
+
"exec",
|
|
451
|
+
binaryName,
|
|
452
|
+
...binaryArgs
|
|
453
|
+
]
|
|
454
|
+
};
|
|
455
|
+
case "yarn": return {
|
|
456
|
+
command: "yarn",
|
|
457
|
+
args: [binaryName, ...binaryArgs]
|
|
458
|
+
};
|
|
459
|
+
case "bun": return {
|
|
460
|
+
command: "bun",
|
|
461
|
+
args: [binaryName, ...binaryArgs]
|
|
462
|
+
};
|
|
463
|
+
case "deno": return {
|
|
464
|
+
command: "deno",
|
|
465
|
+
args: [
|
|
466
|
+
"run",
|
|
467
|
+
"-A",
|
|
468
|
+
`npm:${binaryName}`,
|
|
469
|
+
...binaryArgs
|
|
470
|
+
]
|
|
471
|
+
};
|
|
472
|
+
default: return {
|
|
473
|
+
command: "npm",
|
|
474
|
+
args: [
|
|
475
|
+
"exec",
|
|
476
|
+
binaryName,
|
|
477
|
+
"--",
|
|
478
|
+
...binaryArgs
|
|
479
|
+
]
|
|
480
|
+
};
|
|
481
|
+
}
|
|
346
482
|
}
|
|
347
|
-
function
|
|
348
|
-
const execution =
|
|
483
|
+
function getLocalPackageBinaryCommand(packageManager, binaryName, binaryArgs) {
|
|
484
|
+
const execution = getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs);
|
|
349
485
|
return [execution.command, ...execution.args].join(" ");
|
|
350
486
|
}
|
|
351
487
|
|
|
@@ -360,6 +496,8 @@ function getOptionalHashStringList(hash, key) {
|
|
|
360
496
|
}
|
|
361
497
|
Handlebars.registerHelper("eq", (left, right) => left === right);
|
|
362
498
|
Handlebars.registerHelper("runScriptCommand", (packageManager, scriptName) => packageManager ? getRunScriptCommand(packageManager, scriptName) : "");
|
|
499
|
+
Handlebars.registerHelper("runScriptInDirectoryCommand", (packageManager, directory, scriptName) => packageManager ? getRunScriptInDirectoryCommand(packageManager, directory, scriptName) : "");
|
|
500
|
+
Handlebars.registerHelper("runScriptInDirectoryCommandWithArgsPassthrough", (packageManager, directory, scriptName) => packageManager ? getRunScriptInDirectoryCommandWithArgsPassthrough(packageManager, directory, scriptName) : "");
|
|
363
501
|
Handlebars.registerHelper("packageManagerManifestValue", (packageManager) => getPackageManagerManifestValue(packageManager) ?? "");
|
|
364
502
|
Handlebars.registerHelper("requiresDotenvConfigImport", (packageManager) => requiresDotenvConfigImport(packageManager));
|
|
365
503
|
Handlebars.registerHelper("runtimeScript", (packageManager, kind, sourceEntrypoint, builtEntrypoint, options) => {
|
|
@@ -431,65 +569,81 @@ async function renderTemplateTree(opts) {
|
|
|
431
569
|
function getCreateTemplateDir(template) {
|
|
432
570
|
return resolveTemplatesDir(`templates/create/${template}`);
|
|
433
571
|
}
|
|
434
|
-
function
|
|
572
|
+
function getCreateSharedTemplateDir() {
|
|
573
|
+
return resolveTemplatesDir("templates/create/_shared");
|
|
574
|
+
}
|
|
575
|
+
function createTemplateContext(projectName, template, provider, authoring, schemaPreset, packageManager) {
|
|
435
576
|
return {
|
|
436
577
|
projectName,
|
|
578
|
+
template,
|
|
437
579
|
provider,
|
|
580
|
+
authoring,
|
|
438
581
|
schemaPreset,
|
|
439
582
|
packageManager
|
|
440
583
|
};
|
|
441
584
|
}
|
|
442
585
|
async function scaffoldCreateTemplate(opts) {
|
|
443
|
-
const { projectDir, projectName, template, provider, schemaPreset, packageManager } = opts;
|
|
586
|
+
const { projectDir, projectName, template, provider, authoring, schemaPreset, packageManager } = opts;
|
|
587
|
+
const templateRoot = getCreateTemplateDir(template);
|
|
588
|
+
const sharedTemplateRoot = getCreateSharedTemplateDir();
|
|
589
|
+
const context = createTemplateContext(projectName, template, provider, authoring, schemaPreset, packageManager);
|
|
590
|
+
await renderTemplateTree({
|
|
591
|
+
templateRoot: sharedTemplateRoot,
|
|
592
|
+
outputDir: projectDir,
|
|
593
|
+
context
|
|
594
|
+
});
|
|
444
595
|
await renderTemplateTree({
|
|
445
|
-
templateRoot
|
|
596
|
+
templateRoot,
|
|
446
597
|
outputDir: projectDir,
|
|
447
|
-
context
|
|
598
|
+
context
|
|
448
599
|
});
|
|
449
600
|
}
|
|
450
601
|
|
|
451
602
|
//#endregion
|
|
452
603
|
//#region src/constants/db-packages.ts
|
|
453
|
-
function getDbPackages(provider) {
|
|
604
|
+
function getDbPackages(provider, _packageManager) {
|
|
454
605
|
switch (provider) {
|
|
455
|
-
case "
|
|
456
|
-
case "
|
|
457
|
-
case "mysql": return "@prisma/adapter-mariadb";
|
|
458
|
-
case "sqlite": return "@prisma/adapter-better-sqlite3";
|
|
459
|
-
case "sqlserver": return "@prisma/adapter-mssql";
|
|
606
|
+
case "postgres": return "@prisma-next/postgres";
|
|
607
|
+
case "mongo": return "@prisma-next/mongo";
|
|
460
608
|
default: {
|
|
461
609
|
const exhaustiveCheck = provider;
|
|
462
|
-
throw new Error(`Unsupported
|
|
610
|
+
throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
|
|
463
611
|
}
|
|
464
612
|
}
|
|
465
613
|
}
|
|
466
614
|
|
|
467
615
|
//#endregion
|
|
468
616
|
//#region src/tasks/install.ts
|
|
469
|
-
function
|
|
617
|
+
function getPrismaNextScriptMap(packageManager) {
|
|
470
618
|
if (packageManager === "deno") {
|
|
471
|
-
const
|
|
619
|
+
const prismaNextCli = `deno run -A --env-file=.env npm:prisma-next@${dependencyVersionMap["prisma-next"]}`;
|
|
472
620
|
return {
|
|
473
|
-
"
|
|
474
|
-
"db:
|
|
475
|
-
"db:
|
|
476
|
-
"db:seed":
|
|
621
|
+
"contract:emit": `${prismaNextCli} contract emit`,
|
|
622
|
+
"db:init": `${prismaNextCli} db init`,
|
|
623
|
+
"db:update": `${prismaNextCli} db update`,
|
|
624
|
+
"db:seed": "deno run -A --env-file=.env prisma/seed.ts",
|
|
625
|
+
"migration:plan": `${prismaNextCli} migration plan`,
|
|
626
|
+
"migration:apply": `${prismaNextCli} migration apply`
|
|
477
627
|
};
|
|
478
628
|
}
|
|
479
629
|
if (packageManager === "bun") {
|
|
480
|
-
const
|
|
630
|
+
const prismaNextCli = "bun prisma-next";
|
|
481
631
|
return {
|
|
482
|
-
"
|
|
483
|
-
"db:
|
|
484
|
-
"db:
|
|
485
|
-
"db:seed":
|
|
632
|
+
"contract:emit": `${prismaNextCli} contract emit`,
|
|
633
|
+
"db:init": `${prismaNextCli} db init`,
|
|
634
|
+
"db:update": `${prismaNextCli} db update`,
|
|
635
|
+
"db:seed": "bun prisma/seed.ts",
|
|
636
|
+
"migration:plan": `${prismaNextCli} migration plan`,
|
|
637
|
+
"migration:apply": `${prismaNextCli} migration apply`
|
|
486
638
|
};
|
|
487
639
|
}
|
|
488
640
|
return {
|
|
489
|
-
"
|
|
490
|
-
"db:
|
|
491
|
-
"db:
|
|
492
|
-
"db:seed": "prisma
|
|
641
|
+
"contract:emit": "prisma-next contract emit",
|
|
642
|
+
"db:init": "prisma-next db init",
|
|
643
|
+
"db:update": "prisma-next db update",
|
|
644
|
+
"db:seed": "tsx prisma/seed.ts",
|
|
645
|
+
"migration:plan": "prisma-next migration plan",
|
|
646
|
+
"migration:apply": "prisma-next migration apply"
|
|
493
647
|
};
|
|
494
648
|
}
|
|
495
649
|
function getVersion(packageName) {
|
|
@@ -501,24 +655,35 @@ function unique(items) {
|
|
|
501
655
|
function sortRecord(record) {
|
|
502
656
|
return Object.fromEntries(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
|
|
503
657
|
}
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
658
|
+
function getGeneratedContractTypePackages(provider) {
|
|
659
|
+
if (provider === "mongo") return [
|
|
660
|
+
"@prisma-next/adapter-mongo",
|
|
661
|
+
"@prisma-next/contract",
|
|
662
|
+
"@prisma-next/mongo-contract"
|
|
663
|
+
];
|
|
664
|
+
return [
|
|
665
|
+
"@prisma-next/adapter-postgres",
|
|
666
|
+
"@prisma-next/contract",
|
|
667
|
+
"@prisma-next/sql-contract",
|
|
668
|
+
"@prisma-next/target-postgres"
|
|
669
|
+
];
|
|
670
|
+
}
|
|
671
|
+
function getTypeScriptContractPackages(provider) {
|
|
672
|
+
if (provider === "mongo") return [
|
|
673
|
+
...getGeneratedContractTypePackages(provider),
|
|
674
|
+
"@prisma-next/family-mongo",
|
|
675
|
+
"@prisma-next/mongo-contract-ts",
|
|
676
|
+
"@prisma-next/target-mongo"
|
|
677
|
+
];
|
|
678
|
+
return [
|
|
679
|
+
...getGeneratedContractTypePackages(provider),
|
|
680
|
+
"@prisma-next/family-sql",
|
|
681
|
+
"@prisma-next/sql-contract-ts"
|
|
682
|
+
];
|
|
683
|
+
}
|
|
684
|
+
function getMigrationPackages(provider) {
|
|
685
|
+
if (provider === "mongo") return ["@prisma-next/family-mongo", "@prisma-next/target-mongo"];
|
|
686
|
+
return ["@prisma-next/target-postgres"];
|
|
522
687
|
}
|
|
523
688
|
async function addPackageDependency(opts) {
|
|
524
689
|
const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir } = opts;
|
|
@@ -550,16 +715,20 @@ async function addPackageDependency(opts) {
|
|
|
550
715
|
pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
|
|
551
716
|
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
552
717
|
}
|
|
553
|
-
async function writePrismaDependencies(provider, packageManager, projectDir = process.cwd()) {
|
|
554
|
-
const dependencies = ["
|
|
555
|
-
const devDependencies = [
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
718
|
+
async function writePrismaDependencies(provider, packageManager, authoring, projectDir = process.cwd()) {
|
|
719
|
+
const dependencies = [getDbPackages(provider, packageManager), "dotenv"];
|
|
720
|
+
const devDependencies = [
|
|
721
|
+
"prisma-next",
|
|
722
|
+
"@prisma-next/cli",
|
|
723
|
+
"@types/node"
|
|
724
|
+
];
|
|
725
|
+
devDependencies.push(...getMigrationPackages(provider));
|
|
726
|
+
if (authoring === "typescript") devDependencies.push(...getTypeScriptContractPackages(provider));
|
|
727
|
+
else if (packageManager === "deno") devDependencies.push(...getGeneratedContractTypePackages(provider));
|
|
559
728
|
await addPackageDependency({
|
|
560
729
|
dependencies,
|
|
561
730
|
devDependencies,
|
|
562
|
-
scripts:
|
|
731
|
+
scripts: getPrismaNextScriptMap(packageManager),
|
|
563
732
|
scriptMode: "if-missing",
|
|
564
733
|
projectDir
|
|
565
734
|
});
|
|
@@ -580,8 +749,10 @@ async function writeCreateTemplateDependencies(opts) {
|
|
|
580
749
|
async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
|
|
581
750
|
const verbose = options.verbose === true;
|
|
582
751
|
const installCommand = getInstallArgs(packageManager);
|
|
752
|
+
const env = packageManager === "yarn" ? { YARN_ENABLE_IMMUTABLE_INSTALLS: "false" } : void 0;
|
|
583
753
|
await execa(installCommand.command, installCommand.args, {
|
|
584
754
|
cwd: projectDir,
|
|
755
|
+
env,
|
|
585
756
|
stdio: verbose ? "inherit" : "pipe"
|
|
586
757
|
});
|
|
587
758
|
}
|
|
@@ -643,15 +814,46 @@ function getCreateDbCommand(packageManager) {
|
|
|
643
814
|
|
|
644
815
|
//#endregion
|
|
645
816
|
//#region src/tasks/setup-prisma.ts
|
|
646
|
-
const DEFAULT_DATABASE_PROVIDER = "
|
|
647
|
-
const
|
|
648
|
-
const
|
|
817
|
+
const DEFAULT_DATABASE_PROVIDER = "postgres";
|
|
818
|
+
const DEFAULT_AUTHORING = "psl";
|
|
819
|
+
const DEFAULT_SCHEMA_PRESET$1 = "basic";
|
|
649
820
|
const DEFAULT_INSTALL = true;
|
|
650
|
-
const
|
|
821
|
+
const DEFAULT_EMIT = true;
|
|
822
|
+
const DEFAULT_PRISMA_POSTGRES = false;
|
|
823
|
+
const MONGO_DOCKER_COMPOSE = `services:
|
|
824
|
+
mongodb:
|
|
825
|
+
image: mongo:latest
|
|
826
|
+
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
|
827
|
+
ports:
|
|
828
|
+
- "27017:27017"
|
|
829
|
+
volumes:
|
|
830
|
+
- mongodb-data:/data/db
|
|
831
|
+
healthcheck:
|
|
832
|
+
test:
|
|
833
|
+
[
|
|
834
|
+
"CMD-SHELL",
|
|
835
|
+
"mongosh --quiet --eval 'try { rs.status().members.some((member) => member.stateStr === \\"PRIMARY\\") } catch (error) { rs.initiate({_id: \\"rs0\\", members: [{ _id: 0, host: \\"localhost:27017\\" }] }); false }' | grep true",
|
|
836
|
+
]
|
|
837
|
+
interval: 5s
|
|
838
|
+
timeout: 5s
|
|
839
|
+
retries: 30
|
|
840
|
+
start_period: 5s
|
|
841
|
+
|
|
842
|
+
volumes:
|
|
843
|
+
mongodb-data:
|
|
844
|
+
`;
|
|
845
|
+
const mongoDockerScripts = {
|
|
846
|
+
"db:up": "docker compose up -d --wait",
|
|
847
|
+
"db:down": "docker compose down"
|
|
848
|
+
};
|
|
651
849
|
const requiredPrismaFileGroups = [
|
|
652
|
-
[
|
|
653
|
-
|
|
654
|
-
|
|
850
|
+
[
|
|
851
|
+
"prisma/contract.prisma",
|
|
852
|
+
"prisma/contract.ts",
|
|
853
|
+
"packages/db/prisma/contract.prisma",
|
|
854
|
+
"packages/db/prisma/contract.ts"
|
|
855
|
+
],
|
|
856
|
+
["prisma-next.config.ts", "packages/db/prisma-next.config.ts"],
|
|
655
857
|
[
|
|
656
858
|
"src/lib/prisma.ts",
|
|
657
859
|
"src/lib/prisma.server.ts",
|
|
@@ -662,36 +864,22 @@ const requiredPrismaFileGroups = [
|
|
|
662
864
|
];
|
|
663
865
|
async function resolvePrismaProjectDir(projectDir) {
|
|
664
866
|
const monorepoDbDir = path.join(projectDir, "packages/db");
|
|
665
|
-
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/
|
|
867
|
+
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/contract.prisma")) || await fs.pathExists(path.join(monorepoDbDir, "prisma/contract.ts"))) return monorepoDbDir;
|
|
666
868
|
return projectDir;
|
|
667
869
|
}
|
|
668
870
|
async function promptForDatabaseProvider() {
|
|
669
871
|
const databaseProvider = await select({
|
|
670
872
|
message: "Select your database",
|
|
671
873
|
initialValue: DEFAULT_DATABASE_PROVIDER,
|
|
672
|
-
options: [
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
},
|
|
682
|
-
{
|
|
683
|
-
value: "sqlite",
|
|
684
|
-
label: "SQLite"
|
|
685
|
-
},
|
|
686
|
-
{
|
|
687
|
-
value: "sqlserver",
|
|
688
|
-
label: "SQL Server"
|
|
689
|
-
},
|
|
690
|
-
{
|
|
691
|
-
value: "cockroachdb",
|
|
692
|
-
label: "CockroachDB"
|
|
693
|
-
}
|
|
694
|
-
]
|
|
874
|
+
options: [{
|
|
875
|
+
value: "postgres",
|
|
876
|
+
label: "PostgreSQL",
|
|
877
|
+
hint: "Relational models with typed ORM, relations, indexes, raw SQL"
|
|
878
|
+
}, {
|
|
879
|
+
value: "mongo",
|
|
880
|
+
label: "MongoDB",
|
|
881
|
+
hint: "Document models with typed ORM, indexes, aggregations"
|
|
882
|
+
}]
|
|
695
883
|
});
|
|
696
884
|
if (isCancel(databaseProvider)) {
|
|
697
885
|
cancel("Operation cancelled.");
|
|
@@ -699,10 +887,48 @@ async function promptForDatabaseProvider() {
|
|
|
699
887
|
}
|
|
700
888
|
return DatabaseProviderSchema.parse(databaseProvider);
|
|
701
889
|
}
|
|
890
|
+
async function promptForAuthoringStyle() {
|
|
891
|
+
const authoring = await select({
|
|
892
|
+
message: "Choose contract authoring style",
|
|
893
|
+
initialValue: DEFAULT_AUTHORING,
|
|
894
|
+
options: [{
|
|
895
|
+
value: "psl",
|
|
896
|
+
label: "PSL",
|
|
897
|
+
hint: "Schema syntax emits contract.json + types"
|
|
898
|
+
}, {
|
|
899
|
+
value: "typescript",
|
|
900
|
+
label: "TypeScript",
|
|
901
|
+
hint: "Builder API emits the same contract artifacts"
|
|
902
|
+
}]
|
|
903
|
+
});
|
|
904
|
+
if (isCancel(authoring)) {
|
|
905
|
+
cancel("Operation cancelled.");
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
return AuthoringStyleSchema.parse(authoring);
|
|
909
|
+
}
|
|
910
|
+
async function promptForPrismaPostgres() {
|
|
911
|
+
const shouldUsePrismaPostgres = await confirm({
|
|
912
|
+
message: "Provision a Prisma Postgres database?",
|
|
913
|
+
active: "Provision Prisma Postgres",
|
|
914
|
+
inactive: "Use my own database",
|
|
915
|
+
initialValue: DEFAULT_PRISMA_POSTGRES
|
|
916
|
+
});
|
|
917
|
+
if (isCancel(shouldUsePrismaPostgres)) {
|
|
918
|
+
cancel("Operation cancelled.");
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
return Boolean(shouldUsePrismaPostgres);
|
|
922
|
+
}
|
|
702
923
|
function getPackageManagerHint(option, detected) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
924
|
+
const hint = {
|
|
925
|
+
npm: "Node.js default",
|
|
926
|
+
pnpm: "Fast, disk-efficient Node.js package manager",
|
|
927
|
+
yarn: "Yarn package manager",
|
|
928
|
+
bun: "Fast runtime + package manager",
|
|
929
|
+
deno: "Deno runtime + task runner"
|
|
930
|
+
}[option];
|
|
931
|
+
return option === detected ? `Detected; ${hint}` : hint;
|
|
706
932
|
}
|
|
707
933
|
async function promptForPackageManager(detectedPackageManager) {
|
|
708
934
|
const packageManager = await select({
|
|
@@ -744,7 +970,9 @@ async function promptForPackageManager(detectedPackageManager) {
|
|
|
744
970
|
}
|
|
745
971
|
async function promptForDependencyInstall(packageManager) {
|
|
746
972
|
const shouldInstall = await confirm({
|
|
747
|
-
message: `Install dependencies now with ${getInstallCommand(packageManager)}
|
|
973
|
+
message: `Install dependencies now with ${getInstallCommand(packageManager)}? You can run it later.`,
|
|
974
|
+
active: "Install now",
|
|
975
|
+
inactive: "Skip for now",
|
|
748
976
|
initialValue: true
|
|
749
977
|
});
|
|
750
978
|
if (isCancel(shouldInstall)) {
|
|
@@ -753,17 +981,6 @@ async function promptForDependencyInstall(packageManager) {
|
|
|
753
981
|
}
|
|
754
982
|
return Boolean(shouldInstall);
|
|
755
983
|
}
|
|
756
|
-
async function promptForPrismaPostgres() {
|
|
757
|
-
const shouldUsePrismaPostgres = await confirm({
|
|
758
|
-
message: "Use Prisma Postgres and auto-generate DATABASE_URL with create-db?",
|
|
759
|
-
initialValue: true
|
|
760
|
-
});
|
|
761
|
-
if (isCancel(shouldUsePrismaPostgres)) {
|
|
762
|
-
cancel("Operation cancelled.");
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
return Boolean(shouldUsePrismaPostgres);
|
|
766
|
-
}
|
|
767
984
|
function getCommandErrorMessage(error) {
|
|
768
985
|
if (error instanceof Error && "stderr" in error) {
|
|
769
986
|
const stderr = String(error.stderr ?? "").trim();
|
|
@@ -775,17 +992,23 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
775
992
|
const projectDir = path.resolve(options.projectDir ?? process.cwd());
|
|
776
993
|
const useDefaults = input.yes === true;
|
|
777
994
|
const verbose = input.verbose === true;
|
|
778
|
-
const
|
|
995
|
+
const shouldEmit = input.emit ?? DEFAULT_EMIT;
|
|
779
996
|
const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
|
|
780
997
|
if (!databaseProvider) return;
|
|
781
|
-
const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
|
|
782
998
|
const databaseUrl = input.databaseUrl;
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
999
|
+
const shouldUsePrismaPostgres = input.prismaPostgres ?? (databaseProvider === "postgres" && !databaseUrl && !useDefaults ? await promptForPrismaPostgres() : DEFAULT_PRISMA_POSTGRES);
|
|
1000
|
+
if (shouldUsePrismaPostgres === void 0) return;
|
|
1001
|
+
if (shouldUsePrismaPostgres && databaseProvider !== "postgres") {
|
|
1002
|
+
cancel("--prisma-postgres is only supported with --provider postgres.");
|
|
1003
|
+
return;
|
|
1004
|
+
}
|
|
1005
|
+
if (shouldUsePrismaPostgres && databaseUrl) {
|
|
1006
|
+
cancel("Use either --database-url or --prisma-postgres, not both.");
|
|
1007
|
+
return;
|
|
788
1008
|
}
|
|
1009
|
+
const authoring = input.authoring ?? (useDefaults ? DEFAULT_AUTHORING : await promptForAuthoringStyle());
|
|
1010
|
+
if (!authoring) return;
|
|
1011
|
+
const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
|
|
789
1012
|
const detectedPackageManager = await detectPackageManager(projectDir);
|
|
790
1013
|
const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
|
|
791
1014
|
if (!packageManager) return;
|
|
@@ -794,8 +1017,9 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
794
1017
|
return {
|
|
795
1018
|
projectDir,
|
|
796
1019
|
verbose,
|
|
797
|
-
|
|
1020
|
+
shouldEmit,
|
|
798
1021
|
databaseProvider,
|
|
1022
|
+
authoring,
|
|
799
1023
|
schemaPreset,
|
|
800
1024
|
databaseUrl,
|
|
801
1025
|
shouldUsePrismaPostgres,
|
|
@@ -805,14 +1029,11 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
805
1029
|
}
|
|
806
1030
|
function getDefaultDatabaseUrl(provider) {
|
|
807
1031
|
switch (provider) {
|
|
808
|
-
case "
|
|
809
|
-
case "
|
|
810
|
-
case "mysql": return "mysql://johndoe:randompassword@localhost:3306/mydb";
|
|
811
|
-
case "sqlite": return "file:./dev.db";
|
|
812
|
-
case "sqlserver": return "sqlserver://localhost:1433;database=mydb;user=SA;password=randompassword;";
|
|
1032
|
+
case "postgres": return "postgresql://user:password@localhost:5432/mydb";
|
|
1033
|
+
case "mongo": return "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
|
|
813
1034
|
default: {
|
|
814
1035
|
const exhaustiveCheck = provider;
|
|
815
|
-
throw new Error(`Unsupported
|
|
1036
|
+
throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
|
|
816
1037
|
}
|
|
817
1038
|
}
|
|
818
1039
|
}
|
|
@@ -882,6 +1103,34 @@ async function ensureGitignoreEntry(projectDir, entry) {
|
|
|
882
1103
|
const separator = existingContent.endsWith("\n") ? "" : "\n";
|
|
883
1104
|
await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
|
|
884
1105
|
}
|
|
1106
|
+
async function ensurePackageScripts(projectDir, scripts) {
|
|
1107
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
1108
|
+
if (!await fs.pathExists(packageJsonPath)) return;
|
|
1109
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1110
|
+
if (!packageJson.scripts) packageJson.scripts = {};
|
|
1111
|
+
let didChange = false;
|
|
1112
|
+
for (const [scriptName, command] of Object.entries(scripts)) if (typeof packageJson.scripts[scriptName] !== "string" || packageJson.scripts[scriptName].trim().length === 0) {
|
|
1113
|
+
packageJson.scripts[scriptName] = command;
|
|
1114
|
+
didChange = true;
|
|
1115
|
+
}
|
|
1116
|
+
if (didChange) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1117
|
+
}
|
|
1118
|
+
async function ensureMongoDockerCompose(projectDir) {
|
|
1119
|
+
const composePath = path.join(projectDir, "docker-compose.yml");
|
|
1120
|
+
if (await fs.pathExists(composePath)) return;
|
|
1121
|
+
await fs.writeFile(composePath, MONGO_DOCKER_COMPOSE, "utf8");
|
|
1122
|
+
}
|
|
1123
|
+
async function writeMongoDockerHelpersForContext(context, projectDir) {
|
|
1124
|
+
if (context.databaseProvider !== "mongo" || context.databaseUrl) return true;
|
|
1125
|
+
try {
|
|
1126
|
+
await ensureMongoDockerCompose(projectDir);
|
|
1127
|
+
await ensurePackageScripts(projectDir, mongoDockerScripts);
|
|
1128
|
+
return true;
|
|
1129
|
+
} catch (error) {
|
|
1130
|
+
cancel(getCommandErrorMessage(error));
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
885
1134
|
async function ensureRequiredPrismaFiles(projectDir) {
|
|
886
1135
|
const missingFiles = [];
|
|
887
1136
|
for (const candidates of requiredPrismaFileGroups) {
|
|
@@ -895,13 +1144,12 @@ async function ensureRequiredPrismaFiles(projectDir) {
|
|
|
895
1144
|
}
|
|
896
1145
|
if (!foundCandidate) missingFiles.push(candidates.join(" or "));
|
|
897
1146
|
}
|
|
898
|
-
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
|
|
1147
|
+
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma Next files: ${missingFiles.join(", ")}`);
|
|
899
1148
|
}
|
|
900
1149
|
async function finalizePrismaFiles(options) {
|
|
901
1150
|
const projectDir = options.projectDir ?? process.cwd();
|
|
902
1151
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
903
1152
|
await ensureRequiredPrismaFiles(projectDir);
|
|
904
|
-
const generatedDir = await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? "server/generated" : "src/generated";
|
|
905
1153
|
await ensureEnvVarInEnv(prismaProjectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
|
|
906
1154
|
mode: options.databaseUrl ? "upsert" : "keep-existing",
|
|
907
1155
|
comment: "Added by create-prisma"
|
|
@@ -913,7 +1161,7 @@ async function finalizePrismaFiles(options) {
|
|
|
913
1161
|
});
|
|
914
1162
|
await ensureEnvComment(prismaProjectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
|
|
915
1163
|
}
|
|
916
|
-
await ensureGitignoreEntry(prismaProjectDir,
|
|
1164
|
+
await ensureGitignoreEntry(prismaProjectDir, ".env");
|
|
917
1165
|
}
|
|
918
1166
|
async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
919
1167
|
if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
|
|
@@ -939,7 +1187,7 @@ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
|
939
1187
|
async function writeDependenciesForContext(context, projectDir) {
|
|
940
1188
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
941
1189
|
try {
|
|
942
|
-
await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
|
|
1190
|
+
await writePrismaDependencies(context.databaseProvider, context.packageManager, context.authoring, prismaProjectDir);
|
|
943
1191
|
return true;
|
|
944
1192
|
} catch (error) {
|
|
945
1193
|
cancel(getCommandErrorMessage(error));
|
|
@@ -974,7 +1222,7 @@ async function installDependenciesForContext(context, projectDir) {
|
|
|
974
1222
|
}
|
|
975
1223
|
async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
|
|
976
1224
|
const initSpinner = spinner();
|
|
977
|
-
initSpinner.start("Preparing Prisma files...");
|
|
1225
|
+
initSpinner.start("Preparing Prisma Next files...");
|
|
978
1226
|
try {
|
|
979
1227
|
await finalizePrismaFiles({
|
|
980
1228
|
provider: context.databaseProvider,
|
|
@@ -982,55 +1230,106 @@ async function finalizePrismaFilesForContext(context, projectDir, provisionResul
|
|
|
982
1230
|
claimUrl: provisionResult.claimUrl,
|
|
983
1231
|
projectDir
|
|
984
1232
|
});
|
|
985
|
-
initSpinner.stop("Prisma files ready.");
|
|
1233
|
+
initSpinner.stop("Prisma Next files ready.");
|
|
986
1234
|
return true;
|
|
987
1235
|
} catch (error) {
|
|
988
|
-
initSpinner.stop("Could not prepare Prisma files.");
|
|
1236
|
+
initSpinner.stop("Could not prepare Prisma Next files.");
|
|
989
1237
|
cancel(getCommandErrorMessage(error));
|
|
990
1238
|
return false;
|
|
991
1239
|
}
|
|
992
1240
|
}
|
|
993
|
-
|
|
1241
|
+
function getPrismaNextCliCommand(packageManager, prismaNextArgs) {
|
|
1242
|
+
if (packageManager === "deno") return `deno run -A --env-file=.env npm:prisma-next@${dependencyVersionMap["prisma-next"]} ${prismaNextArgs.join(" ")}`;
|
|
1243
|
+
return getLocalPackageBinaryCommand(packageManager, "prisma-next", prismaNextArgs);
|
|
1244
|
+
}
|
|
1245
|
+
function getPrismaNextCliArgs(packageManager, prismaNextArgs) {
|
|
1246
|
+
if (packageManager === "deno") return {
|
|
1247
|
+
command: "deno",
|
|
1248
|
+
args: [
|
|
1249
|
+
"run",
|
|
1250
|
+
"-A",
|
|
1251
|
+
"--env-file=.env",
|
|
1252
|
+
`npm:prisma-next@${dependencyVersionMap["prisma-next"]}`,
|
|
1253
|
+
...prismaNextArgs
|
|
1254
|
+
]
|
|
1255
|
+
};
|
|
1256
|
+
return getLocalPackageBinaryArgs(packageManager, "prisma-next", prismaNextArgs);
|
|
1257
|
+
}
|
|
1258
|
+
async function emitPrismaNextContractForContext(context, projectDir) {
|
|
994
1259
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
995
|
-
if (!context.
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1260
|
+
if (!context.shouldEmit) return { didEmitContract: false };
|
|
1261
|
+
if (!context.shouldInstall) return {
|
|
1262
|
+
didEmitContract: false,
|
|
1263
|
+
warning: "Skipped contract emit because dependencies were not installed."
|
|
1264
|
+
};
|
|
1265
|
+
const emitCommand = getPrismaNextCliCommand(context.packageManager, ["contract", "emit"]);
|
|
1266
|
+
if (context.verbose) log.step(`Running ${emitCommand}`);
|
|
1267
|
+
const emitSpinner = context.verbose ? void 0 : spinner();
|
|
1268
|
+
emitSpinner?.start("Emitting Prisma Next contract...");
|
|
1000
1269
|
try {
|
|
1001
|
-
const
|
|
1002
|
-
await execa(
|
|
1270
|
+
const emitArgs = getPrismaNextCliArgs(context.packageManager, ["contract", "emit"]);
|
|
1271
|
+
await execa(emitArgs.command, emitArgs.args, {
|
|
1003
1272
|
cwd: prismaProjectDir,
|
|
1004
1273
|
stdio: context.verbose ? "inherit" : "pipe"
|
|
1005
1274
|
});
|
|
1006
|
-
if (context.verbose) log.success("Prisma
|
|
1007
|
-
else
|
|
1008
|
-
return {
|
|
1275
|
+
if (context.verbose) log.success("Prisma Next contract emitted.");
|
|
1276
|
+
else emitSpinner?.stop("Prisma Next contract emitted.");
|
|
1277
|
+
return { didEmitContract: true };
|
|
1009
1278
|
} catch (error) {
|
|
1010
|
-
if (context.verbose) log.warn("Could not
|
|
1011
|
-
else
|
|
1279
|
+
if (context.verbose) log.warn("Could not emit Prisma Next contract.");
|
|
1280
|
+
else emitSpinner?.stop("Could not emit Prisma Next contract.");
|
|
1012
1281
|
return {
|
|
1013
|
-
|
|
1014
|
-
warning: `
|
|
1282
|
+
didEmitContract: false,
|
|
1283
|
+
warning: `Contract emit failed: ${getCommandErrorMessage(error)}`
|
|
1015
1284
|
};
|
|
1016
1285
|
}
|
|
1017
1286
|
}
|
|
1018
|
-
function buildWarningLines(provisionWarning,
|
|
1287
|
+
function buildWarningLines(provisionWarning, emitWarning) {
|
|
1019
1288
|
const warningLines = [];
|
|
1020
1289
|
if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
|
|
1021
|
-
if (
|
|
1290
|
+
if (emitWarning) warningLines.push(`- ${emitWarning}`);
|
|
1022
1291
|
return warningLines;
|
|
1023
1292
|
}
|
|
1024
1293
|
function buildNextStepsForContext(opts) {
|
|
1025
|
-
const { context, options,
|
|
1294
|
+
const { context, options, didEmitContract } = opts;
|
|
1026
1295
|
const nextSteps = [...options.prependNextSteps ?? []];
|
|
1027
|
-
if (!context.shouldInstall) nextSteps.push(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
if (
|
|
1296
|
+
if (!context.shouldInstall) nextSteps.push({
|
|
1297
|
+
command: getInstallCommand(context.packageManager),
|
|
1298
|
+
description: "Install the project dependencies."
|
|
1299
|
+
});
|
|
1300
|
+
if (!didEmitContract || !context.shouldEmit) nextSteps.push({
|
|
1301
|
+
command: getRunScriptCommand(context.packageManager, "contract:emit"),
|
|
1302
|
+
description: "Emit contract.json and TypeScript types from your Prisma Next contract."
|
|
1303
|
+
});
|
|
1304
|
+
if (context.databaseProvider === "postgres") nextSteps.push({
|
|
1305
|
+
command: getRunScriptCommand(context.packageManager, "db:init"),
|
|
1306
|
+
description: "Create the initial PostgreSQL database objects and sign the database."
|
|
1307
|
+
});
|
|
1308
|
+
if (context.databaseProvider === "mongo" && !context.databaseUrl) nextSteps.push({
|
|
1309
|
+
command: getRunScriptCommand(context.packageManager, "db:up"),
|
|
1310
|
+
description: "Start the local MongoDB replica set with Docker."
|
|
1311
|
+
});
|
|
1312
|
+
nextSteps.push({
|
|
1313
|
+
command: getRunScriptCommand(context.packageManager, "migration:plan"),
|
|
1314
|
+
description: "Compare the contract to the database and write a migration plan."
|
|
1315
|
+
});
|
|
1316
|
+
nextSteps.push({
|
|
1317
|
+
command: getRunScriptCommand(context.packageManager, "migration:apply"),
|
|
1318
|
+
description: "Apply the planned migration to the database."
|
|
1319
|
+
});
|
|
1320
|
+
if (context.schemaPreset === "basic") nextSteps.push({
|
|
1321
|
+
command: getRunScriptCommand(context.packageManager, "db:seed"),
|
|
1322
|
+
description: "Insert the sample user and post data from prisma/seed.ts."
|
|
1323
|
+
});
|
|
1324
|
+
if (options.includeDevNextStep) nextSteps.push({
|
|
1325
|
+
command: getRunScriptCommand(context.packageManager, "dev"),
|
|
1326
|
+
description: "Start the development server."
|
|
1327
|
+
});
|
|
1032
1328
|
return nextSteps;
|
|
1033
1329
|
}
|
|
1330
|
+
function formatNextSteps(nextSteps) {
|
|
1331
|
+
return nextSteps.map((step) => `${step.command}\n ${step.description}`).join("\n\n");
|
|
1332
|
+
}
|
|
1034
1333
|
async function executePrismaSetupContext(context, options = {}) {
|
|
1035
1334
|
const projectDir = path.resolve(options.projectDir ?? context.projectDir);
|
|
1036
1335
|
const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
|
|
@@ -1038,687 +1337,20 @@ async function executePrismaSetupContext(context, options = {}) {
|
|
|
1038
1337
|
if (!await writeDependenciesForContext(context, projectDir)) return false;
|
|
1039
1338
|
if (!await installDependenciesForContext(context, projectDir)) return false;
|
|
1040
1339
|
if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) return false;
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1340
|
+
if (!await writeMongoDockerHelpersForContext(context, projectDir)) return false;
|
|
1341
|
+
const emitResult = await emitPrismaNextContractForContext(context, projectDir);
|
|
1342
|
+
const warningLines = buildWarningLines(provisionResult.warning, emitResult.warning);
|
|
1043
1343
|
const nextSteps = buildNextStepsForContext({
|
|
1044
1344
|
context,
|
|
1045
1345
|
options,
|
|
1046
|
-
|
|
1346
|
+
didEmitContract: emitResult.didEmitContract
|
|
1047
1347
|
});
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
${nextSteps.join("\n")}`);
|
|
1348
|
+
if (warningLines.length > 0) note(warningLines.map((line) => line.replace(/^- /, "")).join("\n"), "Heads up");
|
|
1349
|
+
note(formatNextSteps(nextSteps), "Next steps");
|
|
1350
|
+
outro("Setup complete.");
|
|
1052
1351
|
return true;
|
|
1053
1352
|
}
|
|
1054
1353
|
|
|
1055
|
-
//#endregion
|
|
1056
|
-
//#region src/tasks/setup-addons.ts
|
|
1057
|
-
const DEFAULT_ADDON_SCOPE = "project";
|
|
1058
|
-
const DEFAULT_SKILLS_AGENTS = [
|
|
1059
|
-
"claude-code",
|
|
1060
|
-
"codex",
|
|
1061
|
-
"cursor"
|
|
1062
|
-
];
|
|
1063
|
-
const DEFAULT_MCP_AGENTS = [
|
|
1064
|
-
"claude-code",
|
|
1065
|
-
"codex",
|
|
1066
|
-
"cursor"
|
|
1067
|
-
];
|
|
1068
|
-
const DEFAULT_EXTENSION_TARGETS = ["vscode", "cursor"];
|
|
1069
|
-
const PRISMA_MCP_SERVER = "https://mcp.prisma.io/mcp";
|
|
1070
|
-
const ADDON_OPTIONS = [
|
|
1071
|
-
{
|
|
1072
|
-
value: "skills",
|
|
1073
|
-
label: "Skills",
|
|
1074
|
-
hint: "Install curated Prisma skills to your selected coding agents"
|
|
1075
|
-
},
|
|
1076
|
-
{
|
|
1077
|
-
value: "mcp",
|
|
1078
|
-
label: "MCP",
|
|
1079
|
-
hint: "Configure Prisma MCP server in agent MCP config files"
|
|
1080
|
-
},
|
|
1081
|
-
{
|
|
1082
|
-
value: "extension",
|
|
1083
|
-
label: "IDE Extension",
|
|
1084
|
-
hint: "Install Prisma extension in selected IDEs (VS Code, Cursor, Windsurf)"
|
|
1085
|
-
}
|
|
1086
|
-
];
|
|
1087
|
-
const SKILLS_AGENT_OPTIONS = [
|
|
1088
|
-
{
|
|
1089
|
-
value: "cursor",
|
|
1090
|
-
label: "Cursor"
|
|
1091
|
-
},
|
|
1092
|
-
{
|
|
1093
|
-
value: "claude-code",
|
|
1094
|
-
label: "Claude Code"
|
|
1095
|
-
},
|
|
1096
|
-
{
|
|
1097
|
-
value: "cline",
|
|
1098
|
-
label: "Cline"
|
|
1099
|
-
},
|
|
1100
|
-
{
|
|
1101
|
-
value: "github-copilot",
|
|
1102
|
-
label: "GitHub Copilot"
|
|
1103
|
-
},
|
|
1104
|
-
{
|
|
1105
|
-
value: "codex",
|
|
1106
|
-
label: "Codex"
|
|
1107
|
-
},
|
|
1108
|
-
{
|
|
1109
|
-
value: "opencode",
|
|
1110
|
-
label: "OpenCode"
|
|
1111
|
-
},
|
|
1112
|
-
{
|
|
1113
|
-
value: "windsurf",
|
|
1114
|
-
label: "Windsurf"
|
|
1115
|
-
},
|
|
1116
|
-
{
|
|
1117
|
-
value: "goose",
|
|
1118
|
-
label: "Goose"
|
|
1119
|
-
},
|
|
1120
|
-
{
|
|
1121
|
-
value: "roo",
|
|
1122
|
-
label: "Roo Code"
|
|
1123
|
-
},
|
|
1124
|
-
{
|
|
1125
|
-
value: "kilo",
|
|
1126
|
-
label: "Kilo Code"
|
|
1127
|
-
},
|
|
1128
|
-
{
|
|
1129
|
-
value: "gemini-cli",
|
|
1130
|
-
label: "Gemini CLI"
|
|
1131
|
-
},
|
|
1132
|
-
{
|
|
1133
|
-
value: "antigravity",
|
|
1134
|
-
label: "Antigravity"
|
|
1135
|
-
},
|
|
1136
|
-
{
|
|
1137
|
-
value: "openhands",
|
|
1138
|
-
label: "OpenHands"
|
|
1139
|
-
},
|
|
1140
|
-
{
|
|
1141
|
-
value: "trae",
|
|
1142
|
-
label: "Trae"
|
|
1143
|
-
},
|
|
1144
|
-
{
|
|
1145
|
-
value: "amp",
|
|
1146
|
-
label: "Amp"
|
|
1147
|
-
},
|
|
1148
|
-
{
|
|
1149
|
-
value: "pi",
|
|
1150
|
-
label: "Pi"
|
|
1151
|
-
},
|
|
1152
|
-
{
|
|
1153
|
-
value: "qoder",
|
|
1154
|
-
label: "Qoder"
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
value: "qwen-code",
|
|
1158
|
-
label: "Qwen Code"
|
|
1159
|
-
},
|
|
1160
|
-
{
|
|
1161
|
-
value: "kiro-cli",
|
|
1162
|
-
label: "Kiro CLI"
|
|
1163
|
-
},
|
|
1164
|
-
{
|
|
1165
|
-
value: "droid",
|
|
1166
|
-
label: "Droid"
|
|
1167
|
-
},
|
|
1168
|
-
{
|
|
1169
|
-
value: "command-code",
|
|
1170
|
-
label: "Command Code"
|
|
1171
|
-
},
|
|
1172
|
-
{
|
|
1173
|
-
value: "clawdbot",
|
|
1174
|
-
label: "Clawdbot"
|
|
1175
|
-
},
|
|
1176
|
-
{
|
|
1177
|
-
value: "zencoder",
|
|
1178
|
-
label: "Zencoder"
|
|
1179
|
-
},
|
|
1180
|
-
{
|
|
1181
|
-
value: "neovate",
|
|
1182
|
-
label: "Neovate"
|
|
1183
|
-
},
|
|
1184
|
-
{
|
|
1185
|
-
value: "mcpjam",
|
|
1186
|
-
label: "MCPJam"
|
|
1187
|
-
}
|
|
1188
|
-
];
|
|
1189
|
-
const MCP_AGENT_OPTIONS = [
|
|
1190
|
-
{
|
|
1191
|
-
value: "claude-code",
|
|
1192
|
-
label: "Claude Code"
|
|
1193
|
-
},
|
|
1194
|
-
{
|
|
1195
|
-
value: "codex",
|
|
1196
|
-
label: "Codex"
|
|
1197
|
-
},
|
|
1198
|
-
{
|
|
1199
|
-
value: "cursor",
|
|
1200
|
-
label: "Cursor"
|
|
1201
|
-
},
|
|
1202
|
-
{
|
|
1203
|
-
value: "vscode",
|
|
1204
|
-
label: "VS Code"
|
|
1205
|
-
},
|
|
1206
|
-
{
|
|
1207
|
-
value: "github-copilot-cli",
|
|
1208
|
-
label: "GitHub Copilot CLI"
|
|
1209
|
-
},
|
|
1210
|
-
{
|
|
1211
|
-
value: "opencode",
|
|
1212
|
-
label: "OpenCode"
|
|
1213
|
-
},
|
|
1214
|
-
{
|
|
1215
|
-
value: "gemini-cli",
|
|
1216
|
-
label: "Gemini CLI"
|
|
1217
|
-
},
|
|
1218
|
-
{
|
|
1219
|
-
value: "goose",
|
|
1220
|
-
label: "Goose"
|
|
1221
|
-
},
|
|
1222
|
-
{
|
|
1223
|
-
value: "zed",
|
|
1224
|
-
label: "Zed"
|
|
1225
|
-
},
|
|
1226
|
-
{
|
|
1227
|
-
value: "antigravity",
|
|
1228
|
-
label: "Antigravity"
|
|
1229
|
-
},
|
|
1230
|
-
{
|
|
1231
|
-
value: "cline",
|
|
1232
|
-
label: "Cline VS Code Extension"
|
|
1233
|
-
},
|
|
1234
|
-
{
|
|
1235
|
-
value: "cline-cli",
|
|
1236
|
-
label: "Cline CLI"
|
|
1237
|
-
},
|
|
1238
|
-
{
|
|
1239
|
-
value: "claude-desktop",
|
|
1240
|
-
label: "Claude Desktop"
|
|
1241
|
-
},
|
|
1242
|
-
{
|
|
1243
|
-
value: "mcporter",
|
|
1244
|
-
label: "MCPorter"
|
|
1245
|
-
}
|
|
1246
|
-
];
|
|
1247
|
-
const SHARED_PRISMA_SKILLS = [
|
|
1248
|
-
"prisma-cli",
|
|
1249
|
-
"prisma-client-api",
|
|
1250
|
-
"prisma-database-setup",
|
|
1251
|
-
"prisma-upgrade-v7"
|
|
1252
|
-
];
|
|
1253
|
-
function getAvailablePrismaSkills(provider) {
|
|
1254
|
-
if (provider === "postgresql") return [...SHARED_PRISMA_SKILLS, "prisma-postgres"];
|
|
1255
|
-
return [...SHARED_PRISMA_SKILLS];
|
|
1256
|
-
}
|
|
1257
|
-
function getSkillOptions(provider) {
|
|
1258
|
-
const available = getAvailablePrismaSkills(provider);
|
|
1259
|
-
const options = {
|
|
1260
|
-
"prisma-cli": {
|
|
1261
|
-
value: "prisma-cli",
|
|
1262
|
-
label: "prisma-cli",
|
|
1263
|
-
hint: "Prisma CLI reference"
|
|
1264
|
-
},
|
|
1265
|
-
"prisma-client-api": {
|
|
1266
|
-
value: "prisma-client-api",
|
|
1267
|
-
label: "prisma-client-api",
|
|
1268
|
-
hint: "Prisma Client query patterns"
|
|
1269
|
-
},
|
|
1270
|
-
"prisma-database-setup": {
|
|
1271
|
-
value: "prisma-database-setup",
|
|
1272
|
-
label: "prisma-database-setup",
|
|
1273
|
-
hint: "Database provider setup guides"
|
|
1274
|
-
},
|
|
1275
|
-
"prisma-upgrade-v7": {
|
|
1276
|
-
value: "prisma-upgrade-v7",
|
|
1277
|
-
label: "prisma-upgrade-v7",
|
|
1278
|
-
hint: "v6 to v7 migration guide"
|
|
1279
|
-
},
|
|
1280
|
-
"prisma-postgres": {
|
|
1281
|
-
value: "prisma-postgres",
|
|
1282
|
-
label: "prisma-postgres",
|
|
1283
|
-
hint: "Prisma Postgres workflows"
|
|
1284
|
-
}
|
|
1285
|
-
};
|
|
1286
|
-
return available.map((skill) => options[skill]);
|
|
1287
|
-
}
|
|
1288
|
-
function collectAddonsFromInput(input) {
|
|
1289
|
-
const addons = [];
|
|
1290
|
-
if (input.skills === true) addons.push("skills");
|
|
1291
|
-
if (input.mcp === true) addons.push("mcp");
|
|
1292
|
-
if (input.extension === true) addons.push("extension");
|
|
1293
|
-
return uniqueValues(addons);
|
|
1294
|
-
}
|
|
1295
|
-
const EXTENSION_TARGET_OPTIONS = [
|
|
1296
|
-
{
|
|
1297
|
-
value: "vscode",
|
|
1298
|
-
label: "VS Code",
|
|
1299
|
-
hint: "Uses the `code` CLI"
|
|
1300
|
-
},
|
|
1301
|
-
{
|
|
1302
|
-
value: "cursor",
|
|
1303
|
-
label: "Cursor",
|
|
1304
|
-
hint: "Uses the `cursor` CLI"
|
|
1305
|
-
},
|
|
1306
|
-
{
|
|
1307
|
-
value: "windsurf",
|
|
1308
|
-
label: "Windsurf",
|
|
1309
|
-
hint: "Uses the `windsurf` CLI"
|
|
1310
|
-
}
|
|
1311
|
-
];
|
|
1312
|
-
function uniqueValues(values) {
|
|
1313
|
-
return Array.from(new Set(values));
|
|
1314
|
-
}
|
|
1315
|
-
function getRecommendedPrismaSkills(provider, shouldUsePrismaPostgres) {
|
|
1316
|
-
const skills = [...getAvailablePrismaSkills(provider)];
|
|
1317
|
-
if (!shouldUsePrismaPostgres) return skills.filter((skill) => skill !== "prisma-postgres");
|
|
1318
|
-
return uniqueValues(skills);
|
|
1319
|
-
}
|
|
1320
|
-
async function promptForAddons() {
|
|
1321
|
-
const selectedAddons = await multiselect({
|
|
1322
|
-
message: "Select add-ons (optional)",
|
|
1323
|
-
options: ADDON_OPTIONS,
|
|
1324
|
-
required: false
|
|
1325
|
-
});
|
|
1326
|
-
if (isCancel(selectedAddons)) {
|
|
1327
|
-
cancel("Operation cancelled.");
|
|
1328
|
-
return;
|
|
1329
|
-
}
|
|
1330
|
-
return uniqueValues(selectedAddons);
|
|
1331
|
-
}
|
|
1332
|
-
async function promptForAddonScope() {
|
|
1333
|
-
const selectedScope = await select({
|
|
1334
|
-
message: "Where should add-ons write config?",
|
|
1335
|
-
initialValue: DEFAULT_ADDON_SCOPE,
|
|
1336
|
-
options: [{
|
|
1337
|
-
value: "project",
|
|
1338
|
-
label: "Project",
|
|
1339
|
-
hint: "Recommended for teams (checked into the project when applicable)"
|
|
1340
|
-
}, {
|
|
1341
|
-
value: "global",
|
|
1342
|
-
label: "Global",
|
|
1343
|
-
hint: "Personal machine-level setup"
|
|
1344
|
-
}]
|
|
1345
|
-
});
|
|
1346
|
-
if (isCancel(selectedScope)) {
|
|
1347
|
-
cancel("Operation cancelled.");
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
return selectedScope;
|
|
1351
|
-
}
|
|
1352
|
-
async function promptForPrismaSkills(provider, recommendedSkills) {
|
|
1353
|
-
const options = getSkillOptions(provider);
|
|
1354
|
-
const optionValues = new Set(options.map((option) => option.value));
|
|
1355
|
-
const selectedSkills = await multiselect({
|
|
1356
|
-
message: "Select Prisma skills",
|
|
1357
|
-
options,
|
|
1358
|
-
required: false,
|
|
1359
|
-
initialValues: recommendedSkills.filter((skill) => optionValues.has(skill))
|
|
1360
|
-
});
|
|
1361
|
-
if (isCancel(selectedSkills)) {
|
|
1362
|
-
cancel("Operation cancelled.");
|
|
1363
|
-
return;
|
|
1364
|
-
}
|
|
1365
|
-
return uniqueValues(selectedSkills);
|
|
1366
|
-
}
|
|
1367
|
-
async function promptForSkillsAgents() {
|
|
1368
|
-
const selectedAgents = await multiselect({
|
|
1369
|
-
message: "Select agents for skills",
|
|
1370
|
-
options: SKILLS_AGENT_OPTIONS,
|
|
1371
|
-
required: false,
|
|
1372
|
-
initialValues: [...DEFAULT_SKILLS_AGENTS]
|
|
1373
|
-
});
|
|
1374
|
-
if (isCancel(selectedAgents)) {
|
|
1375
|
-
cancel("Operation cancelled.");
|
|
1376
|
-
return;
|
|
1377
|
-
}
|
|
1378
|
-
return uniqueValues(selectedAgents);
|
|
1379
|
-
}
|
|
1380
|
-
async function promptForMcpAgents() {
|
|
1381
|
-
const selectedAgents = await multiselect({
|
|
1382
|
-
message: "Select agents for MCP",
|
|
1383
|
-
options: MCP_AGENT_OPTIONS,
|
|
1384
|
-
required: false,
|
|
1385
|
-
initialValues: [...DEFAULT_MCP_AGENTS]
|
|
1386
|
-
});
|
|
1387
|
-
if (isCancel(selectedAgents)) {
|
|
1388
|
-
cancel("Operation cancelled.");
|
|
1389
|
-
return;
|
|
1390
|
-
}
|
|
1391
|
-
return uniqueValues(selectedAgents);
|
|
1392
|
-
}
|
|
1393
|
-
async function promptForExtensionTargets() {
|
|
1394
|
-
const selectedTargets = await multiselect({
|
|
1395
|
-
message: "Select IDEs for extension install",
|
|
1396
|
-
options: EXTENSION_TARGET_OPTIONS,
|
|
1397
|
-
required: false,
|
|
1398
|
-
initialValues: DEFAULT_EXTENSION_TARGETS
|
|
1399
|
-
});
|
|
1400
|
-
if (isCancel(selectedTargets)) {
|
|
1401
|
-
cancel("Operation cancelled.");
|
|
1402
|
-
return;
|
|
1403
|
-
}
|
|
1404
|
-
return uniqueValues(selectedTargets);
|
|
1405
|
-
}
|
|
1406
|
-
async function collectCreateAddonSetupContext(input, options) {
|
|
1407
|
-
const hasExplicitAddonSelection = input.skills !== void 0 || input.mcp !== void 0 || input.extension !== void 0;
|
|
1408
|
-
const selectedFromInput = collectAddonsFromInput(input);
|
|
1409
|
-
const selectedAddons = selectedFromInput.length > 0 ? selectedFromInput : hasExplicitAddonSelection ? [] : options.useDefaults ? [] : await promptForAddons();
|
|
1410
|
-
if (!selectedAddons) return;
|
|
1411
|
-
const addons = uniqueValues(selectedAddons);
|
|
1412
|
-
if (addons.length === 0) return null;
|
|
1413
|
-
const scope = addons.includes("skills") || addons.includes("mcp") ? options.useDefaults ? DEFAULT_ADDON_SCOPE : await promptForAddonScope() : DEFAULT_ADDON_SCOPE;
|
|
1414
|
-
if (!scope) return;
|
|
1415
|
-
const recommendedSkills = getRecommendedPrismaSkills(options.provider, options.shouldUsePrismaPostgres);
|
|
1416
|
-
const skills = !addons.includes("skills") ? [] : options.useDefaults ? recommendedSkills : await promptForPrismaSkills(options.provider, recommendedSkills);
|
|
1417
|
-
if (!skills) return;
|
|
1418
|
-
const skillsAgents = !addons.includes("skills") ? [] : options.useDefaults ? [...DEFAULT_SKILLS_AGENTS] : await promptForSkillsAgents();
|
|
1419
|
-
if (!skillsAgents) return;
|
|
1420
|
-
const mcpAgents = !addons.includes("mcp") ? [] : options.useDefaults ? [...DEFAULT_MCP_AGENTS] : await promptForMcpAgents();
|
|
1421
|
-
if (!mcpAgents) return;
|
|
1422
|
-
const extensionTargets = !addons.includes("extension") ? [] : options.useDefaults ? [...DEFAULT_EXTENSION_TARGETS] : await promptForExtensionTargets();
|
|
1423
|
-
if (!extensionTargets) return;
|
|
1424
|
-
return {
|
|
1425
|
-
addons,
|
|
1426
|
-
scope,
|
|
1427
|
-
skills,
|
|
1428
|
-
skillsAgents: uniqueValues(skillsAgents),
|
|
1429
|
-
mcpAgents: uniqueValues(mcpAgents),
|
|
1430
|
-
extensionTargets: uniqueValues(extensionTargets)
|
|
1431
|
-
};
|
|
1432
|
-
}
|
|
1433
|
-
async function executeExternalCommand(params) {
|
|
1434
|
-
await execa(params.command, params.args, {
|
|
1435
|
-
cwd: params.cwd,
|
|
1436
|
-
stdio: params.verbose ? "inherit" : "pipe",
|
|
1437
|
-
env: {
|
|
1438
|
-
...process.env,
|
|
1439
|
-
CI: "true"
|
|
1440
|
-
}
|
|
1441
|
-
});
|
|
1442
|
-
}
|
|
1443
|
-
async function installSkillsAddon(params) {
|
|
1444
|
-
if (params.agents.length === 0 || params.skills.length === 0) return "Skipped skills addon because no skills or agents were selected.";
|
|
1445
|
-
const scopeArgs = params.scope === "global" ? ["-g"] : [];
|
|
1446
|
-
const skillArgs = params.skills.flatMap((skill) => ["-s", skill]);
|
|
1447
|
-
const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
|
|
1448
|
-
const commandArgs = [
|
|
1449
|
-
"skills@latest",
|
|
1450
|
-
"add",
|
|
1451
|
-
"prisma/skills",
|
|
1452
|
-
...scopeArgs,
|
|
1453
|
-
...skillArgs,
|
|
1454
|
-
...agentArgs,
|
|
1455
|
-
"-y"
|
|
1456
|
-
];
|
|
1457
|
-
const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
|
|
1458
|
-
try {
|
|
1459
|
-
await executeExternalCommand({
|
|
1460
|
-
command: execution.command,
|
|
1461
|
-
args: execution.args,
|
|
1462
|
-
cwd: params.projectDir,
|
|
1463
|
-
verbose: params.verbose
|
|
1464
|
-
});
|
|
1465
|
-
return;
|
|
1466
|
-
} catch (error) {
|
|
1467
|
-
return `Skills addon failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
async function installMcpAddon(params) {
|
|
1471
|
-
if (params.agents.length === 0) return "Skipped MCP addon because no agents were selected.";
|
|
1472
|
-
const scopeArgs = params.scope === "global" ? ["-g"] : [];
|
|
1473
|
-
const agentArgs = params.agents.flatMap((agent) => ["-a", agent]);
|
|
1474
|
-
const commandArgs = [
|
|
1475
|
-
"add-mcp@latest",
|
|
1476
|
-
PRISMA_MCP_SERVER,
|
|
1477
|
-
...scopeArgs,
|
|
1478
|
-
...agentArgs,
|
|
1479
|
-
"--name",
|
|
1480
|
-
"prisma",
|
|
1481
|
-
"--gitignore",
|
|
1482
|
-
"-y"
|
|
1483
|
-
];
|
|
1484
|
-
const execution = getPackageExecutionArgs(params.packageManager, commandArgs);
|
|
1485
|
-
try {
|
|
1486
|
-
await executeExternalCommand({
|
|
1487
|
-
command: execution.command,
|
|
1488
|
-
args: execution.args,
|
|
1489
|
-
cwd: params.projectDir,
|
|
1490
|
-
verbose: params.verbose
|
|
1491
|
-
});
|
|
1492
|
-
return;
|
|
1493
|
-
} catch (error) {
|
|
1494
|
-
return `MCP addon failed: ${error instanceof Error ? error.message : String(error)}`;
|
|
1495
|
-
}
|
|
1496
|
-
}
|
|
1497
|
-
function getExtensionInstallBinary(target) {
|
|
1498
|
-
switch (target) {
|
|
1499
|
-
case "vscode": return "code";
|
|
1500
|
-
case "cursor": return "cursor";
|
|
1501
|
-
case "windsurf": return "windsurf";
|
|
1502
|
-
default: {
|
|
1503
|
-
const exhaustiveCheck = target;
|
|
1504
|
-
throw new Error(`Unsupported extension target: ${String(exhaustiveCheck)}`);
|
|
1505
|
-
}
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
async function installExtensionAddon(params) {
|
|
1509
|
-
if (params.targets.length === 0) return ["Skipped extension addon because no IDE targets were selected."];
|
|
1510
|
-
const warnings = [];
|
|
1511
|
-
for (const target of params.targets) {
|
|
1512
|
-
const binary = getExtensionInstallBinary(target);
|
|
1513
|
-
try {
|
|
1514
|
-
await executeExternalCommand({
|
|
1515
|
-
command: binary,
|
|
1516
|
-
args: ["--version"],
|
|
1517
|
-
cwd: params.projectDir,
|
|
1518
|
-
verbose: false
|
|
1519
|
-
});
|
|
1520
|
-
} catch {
|
|
1521
|
-
warnings.push(`Skipped ${target} extension install because the \`${binary}\` CLI is not available.`);
|
|
1522
|
-
continue;
|
|
1523
|
-
}
|
|
1524
|
-
try {
|
|
1525
|
-
await executeExternalCommand({
|
|
1526
|
-
command: binary,
|
|
1527
|
-
args: [
|
|
1528
|
-
"--install-extension",
|
|
1529
|
-
"Prisma.prisma",
|
|
1530
|
-
"--force"
|
|
1531
|
-
],
|
|
1532
|
-
cwd: params.projectDir,
|
|
1533
|
-
verbose: params.verbose
|
|
1534
|
-
});
|
|
1535
|
-
} catch (error) {
|
|
1536
|
-
warnings.push(`${target} extension install failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return warnings;
|
|
1540
|
-
}
|
|
1541
|
-
async function executeCreateAddonSetupContext(params) {
|
|
1542
|
-
const { context, packageManager, projectDir, verbose } = params;
|
|
1543
|
-
const addonSpinner = spinner();
|
|
1544
|
-
addonSpinner.start("Applying selected add-ons...");
|
|
1545
|
-
const warnings = [];
|
|
1546
|
-
if (context.addons.includes("skills")) {
|
|
1547
|
-
const warning = await installSkillsAddon({
|
|
1548
|
-
packageManager,
|
|
1549
|
-
projectDir,
|
|
1550
|
-
scope: context.scope,
|
|
1551
|
-
skills: context.skills,
|
|
1552
|
-
agents: context.skillsAgents,
|
|
1553
|
-
verbose
|
|
1554
|
-
});
|
|
1555
|
-
if (warning) warnings.push(warning);
|
|
1556
|
-
}
|
|
1557
|
-
if (context.addons.includes("mcp")) {
|
|
1558
|
-
const warning = await installMcpAddon({
|
|
1559
|
-
packageManager,
|
|
1560
|
-
projectDir,
|
|
1561
|
-
scope: context.scope,
|
|
1562
|
-
agents: context.mcpAgents,
|
|
1563
|
-
verbose
|
|
1564
|
-
});
|
|
1565
|
-
if (warning) warnings.push(warning);
|
|
1566
|
-
}
|
|
1567
|
-
if (context.addons.includes("extension")) {
|
|
1568
|
-
const extensionWarnings = await installExtensionAddon({
|
|
1569
|
-
projectDir,
|
|
1570
|
-
verbose,
|
|
1571
|
-
targets: context.extensionTargets
|
|
1572
|
-
});
|
|
1573
|
-
warnings.push(...extensionWarnings);
|
|
1574
|
-
}
|
|
1575
|
-
if (warnings.length > 0) {
|
|
1576
|
-
addonSpinner.stop("Add-ons applied with warnings.");
|
|
1577
|
-
for (const warning of warnings) log.warn(warning);
|
|
1578
|
-
return;
|
|
1579
|
-
}
|
|
1580
|
-
addonSpinner.stop("Add-ons applied.");
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
//#endregion
|
|
1584
|
-
//#region src/telemetry/client.ts
|
|
1585
|
-
const TELEMETRY_API_KEY = "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
|
|
1586
|
-
const TELEMETRY_HOST = "https://us.i.posthog.com";
|
|
1587
|
-
const TELEMETRY_CONFIG_FILE = "telemetry.json";
|
|
1588
|
-
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;
|
|
1589
|
-
function isTruthyEnvValue(value) {
|
|
1590
|
-
return [
|
|
1591
|
-
"1",
|
|
1592
|
-
"true",
|
|
1593
|
-
"yes",
|
|
1594
|
-
"on"
|
|
1595
|
-
].includes(String(value ?? "").trim().toLowerCase());
|
|
1596
|
-
}
|
|
1597
|
-
function shouldDisableTelemetry() {
|
|
1598
|
-
if (isTruthyEnvValue(process.env.CI) || isTruthyEnvValue(process.env.GITHUB_ACTIONS)) return true;
|
|
1599
|
-
return process.env.CREATE_PRISMA_DISABLE_TELEMETRY !== void 0 || process.env.CREATE_PRISMA_TELEMETRY_DISABLED !== void 0 || process.env.DO_NOT_TRACK !== void 0;
|
|
1600
|
-
}
|
|
1601
|
-
function getTelemetryConfigDir() {
|
|
1602
|
-
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
|
|
1603
|
-
if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
|
|
1604
|
-
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
|
|
1605
|
-
}
|
|
1606
|
-
async function getAnonymousId() {
|
|
1607
|
-
const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
|
|
1608
|
-
try {
|
|
1609
|
-
const config = await fs.readJSON(telemetryConfigPath);
|
|
1610
|
-
if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
|
|
1611
|
-
} catch {}
|
|
1612
|
-
const anonymousId = randomUUID();
|
|
1613
|
-
try {
|
|
1614
|
-
await fs.ensureDir(path.dirname(telemetryConfigPath));
|
|
1615
|
-
await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
|
|
1616
|
-
} catch {}
|
|
1617
|
-
return anonymousId;
|
|
1618
|
-
}
|
|
1619
|
-
function getCommonProperties() {
|
|
1620
|
-
return {
|
|
1621
|
-
"cli-version": "0.4.1",
|
|
1622
|
-
"node-version": process.version,
|
|
1623
|
-
platform: process.platform,
|
|
1624
|
-
arch: process.arch
|
|
1625
|
-
};
|
|
1626
|
-
}
|
|
1627
|
-
function sanitizeProperties(properties) {
|
|
1628
|
-
return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
|
|
1629
|
-
}
|
|
1630
|
-
async function trackCliTelemetry(event, properties) {
|
|
1631
|
-
if (shouldDisableTelemetry()) return;
|
|
1632
|
-
let client;
|
|
1633
|
-
try {
|
|
1634
|
-
const distinctId = await getAnonymousId();
|
|
1635
|
-
const sanitizedProperties = sanitizeProperties({
|
|
1636
|
-
...getCommonProperties(),
|
|
1637
|
-
...properties,
|
|
1638
|
-
$process_person_profile: false
|
|
1639
|
-
});
|
|
1640
|
-
client = new PostHog(TELEMETRY_API_KEY, {
|
|
1641
|
-
host: TELEMETRY_HOST,
|
|
1642
|
-
disableGeoip: true,
|
|
1643
|
-
flushAt: 1,
|
|
1644
|
-
flushInterval: 0
|
|
1645
|
-
});
|
|
1646
|
-
await client.captureImmediate({
|
|
1647
|
-
distinctId,
|
|
1648
|
-
event,
|
|
1649
|
-
properties: sanitizedProperties,
|
|
1650
|
-
disableGeoip: true
|
|
1651
|
-
});
|
|
1652
|
-
} catch {} finally {
|
|
1653
|
-
if (client) await client.shutdown().catch(() => {});
|
|
1654
|
-
}
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
//#endregion
|
|
1658
|
-
//#region src/telemetry/create.ts
|
|
1659
|
-
function getRequestedAddons(input) {
|
|
1660
|
-
const addons = [];
|
|
1661
|
-
if (input.skills === true) addons.push("skills");
|
|
1662
|
-
if (input.mcp === true) addons.push("mcp");
|
|
1663
|
-
if (input.extension === true) addons.push("extension");
|
|
1664
|
-
return addons;
|
|
1665
|
-
}
|
|
1666
|
-
function getTargetDirectoryState(context) {
|
|
1667
|
-
if (!context.targetPathState.exists) return "new";
|
|
1668
|
-
if (context.targetPathState.isEmptyDirectory) return "empty_directory";
|
|
1669
|
-
return "non_empty_directory";
|
|
1670
|
-
}
|
|
1671
|
-
function getBaseCreateProperties(input, context) {
|
|
1672
|
-
const resolvedAddons = context?.addonSetupContext?.addons ?? getRequestedAddons(input);
|
|
1673
|
-
return {
|
|
1674
|
-
command: "create",
|
|
1675
|
-
"uses-defaults": input.yes === true,
|
|
1676
|
-
verbose: input.verbose === true,
|
|
1677
|
-
force: input.force === true,
|
|
1678
|
-
template: context?.template ?? input.template ?? null,
|
|
1679
|
-
"database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
|
|
1680
|
-
"package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
|
|
1681
|
-
"schema-preset": context?.prismaSetupContext.schemaPreset ?? input.schemaPreset ?? null,
|
|
1682
|
-
"should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
|
|
1683
|
-
"should-generate": context?.prismaSetupContext.shouldGenerate ?? input.generate ?? null,
|
|
1684
|
-
"uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
|
|
1685
|
-
addons: resolvedAddons,
|
|
1686
|
-
"addon-count": resolvedAddons.length,
|
|
1687
|
-
"addon-scope": context?.addonSetupContext?.scope ?? null,
|
|
1688
|
-
"skills-count": context?.addonSetupContext?.skills.length ?? null,
|
|
1689
|
-
"skills-agents-count": context?.addonSetupContext?.skillsAgents.length ?? null,
|
|
1690
|
-
"mcp-agents-count": context?.addonSetupContext?.mcpAgents.length ?? null,
|
|
1691
|
-
"extension-target-count": context?.addonSetupContext?.extensionTargets.length ?? null,
|
|
1692
|
-
"target-directory-state": context ? getTargetDirectoryState(context) : null
|
|
1693
|
-
};
|
|
1694
|
-
}
|
|
1695
|
-
function getErrorName(error) {
|
|
1696
|
-
if (error instanceof Error) return error.name;
|
|
1697
|
-
return error === void 0 ? null : "UnknownError";
|
|
1698
|
-
}
|
|
1699
|
-
function getErrorCode(error) {
|
|
1700
|
-
if (typeof error !== "object" || error === null) return null;
|
|
1701
|
-
const exitCode = Reflect.get(error, "exitCode");
|
|
1702
|
-
if (typeof exitCode === "number") return exitCode;
|
|
1703
|
-
const code = Reflect.get(error, "code");
|
|
1704
|
-
return typeof code === "number" || typeof code === "string" ? code : null;
|
|
1705
|
-
}
|
|
1706
|
-
async function trackCreateCompleted(params) {
|
|
1707
|
-
await trackCliTelemetry("cli:create_command_completed", {
|
|
1708
|
-
...getBaseCreateProperties(params.input, params.context),
|
|
1709
|
-
"duration-ms": params.durationMs
|
|
1710
|
-
});
|
|
1711
|
-
}
|
|
1712
|
-
async function trackCreateFailed(params) {
|
|
1713
|
-
await trackCliTelemetry("cli:create_command_failed", {
|
|
1714
|
-
...getBaseCreateProperties(params.input, params.context),
|
|
1715
|
-
"duration-ms": params.durationMs,
|
|
1716
|
-
"failure-stage": params.stage,
|
|
1717
|
-
"error-name": getErrorName(params.error),
|
|
1718
|
-
"error-code": getErrorCode(params.error)
|
|
1719
|
-
});
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
1354
|
//#endregion
|
|
1723
1355
|
//#region src/ui/branding.ts
|
|
1724
1356
|
const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
|
|
@@ -1764,7 +1396,7 @@ async function promptForCreateTemplate() {
|
|
|
1764
1396
|
{
|
|
1765
1397
|
value: "hono",
|
|
1766
1398
|
label: "Hono",
|
|
1767
|
-
hint: "TypeScript API starter"
|
|
1399
|
+
hint: "Default TypeScript API starter"
|
|
1768
1400
|
},
|
|
1769
1401
|
{
|
|
1770
1402
|
value: "elysia",
|
|
@@ -1872,8 +1504,8 @@ async function runCreateCommand(rawInput = {}) {
|
|
|
1872
1504
|
}
|
|
1873
1505
|
}
|
|
1874
1506
|
async function collectCreateContext(input) {
|
|
1875
|
-
const useDefaults = input.yes === true;
|
|
1876
1507
|
const force = input.force === true;
|
|
1508
|
+
const useDefaults = input.yes === true;
|
|
1877
1509
|
const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
|
|
1878
1510
|
if (projectNameInput === void 0) return;
|
|
1879
1511
|
const projectName = String(projectNameInput).trim();
|
|
@@ -1899,20 +1531,13 @@ async function collectCreateContext(input) {
|
|
|
1899
1531
|
defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
|
|
1900
1532
|
});
|
|
1901
1533
|
if (!prismaSetupContext) return;
|
|
1902
|
-
const addonSetupContext = await collectCreateAddonSetupContext(input, {
|
|
1903
|
-
useDefaults,
|
|
1904
|
-
provider: prismaSetupContext.databaseProvider,
|
|
1905
|
-
shouldUsePrismaPostgres: prismaSetupContext.shouldUsePrismaPostgres
|
|
1906
|
-
});
|
|
1907
|
-
if (addonSetupContext === void 0) return;
|
|
1908
1534
|
return {
|
|
1909
1535
|
targetDirectory,
|
|
1910
1536
|
targetPathState,
|
|
1911
1537
|
force,
|
|
1912
1538
|
template,
|
|
1913
1539
|
projectPackageName: toPackageName(path.basename(targetDirectory)),
|
|
1914
|
-
prismaSetupContext
|
|
1915
|
-
addonSetupContext: addonSetupContext ?? void 0
|
|
1540
|
+
prismaSetupContext
|
|
1916
1541
|
};
|
|
1917
1542
|
}
|
|
1918
1543
|
async function executeCreateContext(context) {
|
|
@@ -1925,6 +1550,7 @@ async function executeCreateContext(context) {
|
|
|
1925
1550
|
template: context.template,
|
|
1926
1551
|
schemaPreset: context.prismaSetupContext.schemaPreset,
|
|
1927
1552
|
provider: context.prismaSetupContext.databaseProvider,
|
|
1553
|
+
authoring: context.prismaSetupContext.authoring,
|
|
1928
1554
|
packageManager: context.prismaSetupContext.packageManager
|
|
1929
1555
|
});
|
|
1930
1556
|
scaffoldSpinner.stop("Project files scaffolded.");
|
|
@@ -1950,21 +1576,10 @@ async function executeCreateContext(context) {
|
|
|
1950
1576
|
};
|
|
1951
1577
|
}
|
|
1952
1578
|
if (context.targetPathState.exists && !context.targetPathState.isEmptyDirectory && context.force) log.warn(`Used --force in non-empty directory ${formatPathForDisplay(context.targetDirectory)}.`);
|
|
1953
|
-
const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
packageManager: context.prismaSetupContext.packageManager,
|
|
1958
|
-
projectDir: context.targetDirectory,
|
|
1959
|
-
verbose: context.prismaSetupContext.verbose
|
|
1960
|
-
});
|
|
1961
|
-
} catch (error) {
|
|
1962
|
-
return {
|
|
1963
|
-
ok: false,
|
|
1964
|
-
stage: "addons",
|
|
1965
|
-
error
|
|
1966
|
-
};
|
|
1967
|
-
}
|
|
1579
|
+
const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [{
|
|
1580
|
+
command: `cd ${formatPathForDisplay(context.targetDirectory)}`,
|
|
1581
|
+
description: "Enter your new project directory."
|
|
1582
|
+
}];
|
|
1968
1583
|
try {
|
|
1969
1584
|
if (!await executePrismaSetupContext(context.prismaSetupContext, {
|
|
1970
1585
|
prependNextSteps: nextSteps,
|
|
@@ -1985,4 +1600,4 @@ async function executeCreateContext(context) {
|
|
|
1985
1600
|
}
|
|
1986
1601
|
|
|
1987
1602
|
//#endregion
|
|
1988
|
-
export {
|
|
1603
|
+
export { DatabaseProviderSchema as a, SchemaPresetSchema as c, CreateTemplateSchema as i, AuthoringStyleSchema as n, DatabaseUrlSchema as o, CreateCommandInputSchema as r, PackageManagerSchema as s, runCreateCommand as t };
|