create-prisma 0.4.1 → 0.4.2-next.37.79.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-Btn7yJR3.mjs} +513 -913
- 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.79.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);
|
|
444
590
|
await renderTemplateTree({
|
|
445
|
-
templateRoot:
|
|
591
|
+
templateRoot: sharedTemplateRoot,
|
|
446
592
|
outputDir: projectDir,
|
|
447
|
-
context
|
|
593
|
+
context
|
|
594
|
+
});
|
|
595
|
+
await renderTemplateTree({
|
|
596
|
+
templateRoot,
|
|
597
|
+
outputDir: projectDir,
|
|
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,45 @@ 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 MONGO_DOCKER_COMPOSE = `services:
|
|
823
|
+
mongodb:
|
|
824
|
+
image: mongo:latest
|
|
825
|
+
command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
|
|
826
|
+
ports:
|
|
827
|
+
- "27017:27017"
|
|
828
|
+
volumes:
|
|
829
|
+
- mongodb-data:/data/db
|
|
830
|
+
healthcheck:
|
|
831
|
+
test:
|
|
832
|
+
[
|
|
833
|
+
"CMD-SHELL",
|
|
834
|
+
"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",
|
|
835
|
+
]
|
|
836
|
+
interval: 5s
|
|
837
|
+
timeout: 5s
|
|
838
|
+
retries: 30
|
|
839
|
+
start_period: 5s
|
|
840
|
+
|
|
841
|
+
volumes:
|
|
842
|
+
mongodb-data:
|
|
843
|
+
`;
|
|
844
|
+
const mongoDockerScripts = {
|
|
845
|
+
"db:up": "docker compose up -d --wait",
|
|
846
|
+
"db:down": "docker compose down"
|
|
847
|
+
};
|
|
651
848
|
const requiredPrismaFileGroups = [
|
|
652
|
-
[
|
|
653
|
-
|
|
654
|
-
|
|
849
|
+
[
|
|
850
|
+
"prisma/contract.prisma",
|
|
851
|
+
"prisma/contract.ts",
|
|
852
|
+
"packages/db/prisma/contract.prisma",
|
|
853
|
+
"packages/db/prisma/contract.ts"
|
|
854
|
+
],
|
|
855
|
+
["prisma-next.config.ts", "packages/db/prisma-next.config.ts"],
|
|
655
856
|
[
|
|
656
857
|
"src/lib/prisma.ts",
|
|
657
858
|
"src/lib/prisma.server.ts",
|
|
@@ -662,36 +863,22 @@ const requiredPrismaFileGroups = [
|
|
|
662
863
|
];
|
|
663
864
|
async function resolvePrismaProjectDir(projectDir) {
|
|
664
865
|
const monorepoDbDir = path.join(projectDir, "packages/db");
|
|
665
|
-
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/
|
|
866
|
+
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/contract.prisma")) || await fs.pathExists(path.join(monorepoDbDir, "prisma/contract.ts"))) return monorepoDbDir;
|
|
666
867
|
return projectDir;
|
|
667
868
|
}
|
|
668
869
|
async function promptForDatabaseProvider() {
|
|
669
870
|
const databaseProvider = await select({
|
|
670
871
|
message: "Select your database",
|
|
671
872
|
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
|
-
]
|
|
873
|
+
options: [{
|
|
874
|
+
value: "postgres",
|
|
875
|
+
label: "PostgreSQL",
|
|
876
|
+
hint: "Relational models with typed ORM, relations, indexes, raw SQL"
|
|
877
|
+
}, {
|
|
878
|
+
value: "mongo",
|
|
879
|
+
label: "MongoDB",
|
|
880
|
+
hint: "Document models with typed ORM, indexes, aggregations"
|
|
881
|
+
}]
|
|
695
882
|
});
|
|
696
883
|
if (isCancel(databaseProvider)) {
|
|
697
884
|
cancel("Operation cancelled.");
|
|
@@ -699,10 +886,35 @@ async function promptForDatabaseProvider() {
|
|
|
699
886
|
}
|
|
700
887
|
return DatabaseProviderSchema.parse(databaseProvider);
|
|
701
888
|
}
|
|
889
|
+
async function promptForAuthoringStyle() {
|
|
890
|
+
const authoring = await select({
|
|
891
|
+
message: "Choose contract authoring style",
|
|
892
|
+
initialValue: DEFAULT_AUTHORING,
|
|
893
|
+
options: [{
|
|
894
|
+
value: "psl",
|
|
895
|
+
label: "PSL",
|
|
896
|
+
hint: "Schema syntax emits contract.json + types"
|
|
897
|
+
}, {
|
|
898
|
+
value: "typescript",
|
|
899
|
+
label: "TypeScript",
|
|
900
|
+
hint: "Builder API emits the same contract artifacts"
|
|
901
|
+
}]
|
|
902
|
+
});
|
|
903
|
+
if (isCancel(authoring)) {
|
|
904
|
+
cancel("Operation cancelled.");
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
return AuthoringStyleSchema.parse(authoring);
|
|
908
|
+
}
|
|
702
909
|
function getPackageManagerHint(option, detected) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
910
|
+
const hint = {
|
|
911
|
+
npm: "Node.js default",
|
|
912
|
+
pnpm: "Fast, disk-efficient Node.js package manager",
|
|
913
|
+
yarn: "Yarn package manager",
|
|
914
|
+
bun: "Fast runtime + package manager",
|
|
915
|
+
deno: "Deno runtime + task runner"
|
|
916
|
+
}[option];
|
|
917
|
+
return option === detected ? `Detected; ${hint}` : hint;
|
|
706
918
|
}
|
|
707
919
|
async function promptForPackageManager(detectedPackageManager) {
|
|
708
920
|
const packageManager = await select({
|
|
@@ -744,7 +956,9 @@ async function promptForPackageManager(detectedPackageManager) {
|
|
|
744
956
|
}
|
|
745
957
|
async function promptForDependencyInstall(packageManager) {
|
|
746
958
|
const shouldInstall = await confirm({
|
|
747
|
-
message: `Install dependencies now with ${getInstallCommand(packageManager)}
|
|
959
|
+
message: `Install dependencies now with ${getInstallCommand(packageManager)}? You can run it later.`,
|
|
960
|
+
active: "Install now",
|
|
961
|
+
inactive: "Skip for now",
|
|
748
962
|
initialValue: true
|
|
749
963
|
});
|
|
750
964
|
if (isCancel(shouldInstall)) {
|
|
@@ -753,17 +967,6 @@ async function promptForDependencyInstall(packageManager) {
|
|
|
753
967
|
}
|
|
754
968
|
return Boolean(shouldInstall);
|
|
755
969
|
}
|
|
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
970
|
function getCommandErrorMessage(error) {
|
|
768
971
|
if (error instanceof Error && "stderr" in error) {
|
|
769
972
|
const stderr = String(error.stderr ?? "").trim();
|
|
@@ -775,16 +978,21 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
775
978
|
const projectDir = path.resolve(options.projectDir ?? process.cwd());
|
|
776
979
|
const useDefaults = input.yes === true;
|
|
777
980
|
const verbose = input.verbose === true;
|
|
778
|
-
const
|
|
981
|
+
const shouldEmit = input.emit ?? DEFAULT_EMIT;
|
|
779
982
|
const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
|
|
780
983
|
if (!databaseProvider) return;
|
|
984
|
+
const authoring = input.authoring ?? (useDefaults ? DEFAULT_AUTHORING : await promptForAuthoringStyle());
|
|
985
|
+
if (!authoring) return;
|
|
781
986
|
const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
|
|
782
987
|
const databaseUrl = input.databaseUrl;
|
|
783
|
-
|
|
784
|
-
if (databaseProvider
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
988
|
+
const shouldUsePrismaPostgres = input.prismaPostgres === true;
|
|
989
|
+
if (shouldUsePrismaPostgres && databaseProvider !== "postgres") {
|
|
990
|
+
cancel("--prisma-postgres is only supported with --provider postgres.");
|
|
991
|
+
return;
|
|
992
|
+
}
|
|
993
|
+
if (shouldUsePrismaPostgres && databaseUrl) {
|
|
994
|
+
cancel("Use either --database-url or --prisma-postgres, not both.");
|
|
995
|
+
return;
|
|
788
996
|
}
|
|
789
997
|
const detectedPackageManager = await detectPackageManager(projectDir);
|
|
790
998
|
const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
|
|
@@ -794,8 +1002,9 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
794
1002
|
return {
|
|
795
1003
|
projectDir,
|
|
796
1004
|
verbose,
|
|
797
|
-
|
|
1005
|
+
shouldEmit,
|
|
798
1006
|
databaseProvider,
|
|
1007
|
+
authoring,
|
|
799
1008
|
schemaPreset,
|
|
800
1009
|
databaseUrl,
|
|
801
1010
|
shouldUsePrismaPostgres,
|
|
@@ -805,14 +1014,11 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
805
1014
|
}
|
|
806
1015
|
function getDefaultDatabaseUrl(provider) {
|
|
807
1016
|
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;";
|
|
1017
|
+
case "postgres": return "postgresql://user:password@localhost:5432/mydb";
|
|
1018
|
+
case "mongo": return "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
|
|
813
1019
|
default: {
|
|
814
1020
|
const exhaustiveCheck = provider;
|
|
815
|
-
throw new Error(`Unsupported
|
|
1021
|
+
throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
|
|
816
1022
|
}
|
|
817
1023
|
}
|
|
818
1024
|
}
|
|
@@ -882,6 +1088,34 @@ async function ensureGitignoreEntry(projectDir, entry) {
|
|
|
882
1088
|
const separator = existingContent.endsWith("\n") ? "" : "\n";
|
|
883
1089
|
await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
|
|
884
1090
|
}
|
|
1091
|
+
async function ensurePackageScripts(projectDir, scripts) {
|
|
1092
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
1093
|
+
if (!await fs.pathExists(packageJsonPath)) return;
|
|
1094
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1095
|
+
if (!packageJson.scripts) packageJson.scripts = {};
|
|
1096
|
+
let didChange = false;
|
|
1097
|
+
for (const [scriptName, command] of Object.entries(scripts)) if (typeof packageJson.scripts[scriptName] !== "string" || packageJson.scripts[scriptName].trim().length === 0) {
|
|
1098
|
+
packageJson.scripts[scriptName] = command;
|
|
1099
|
+
didChange = true;
|
|
1100
|
+
}
|
|
1101
|
+
if (didChange) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1102
|
+
}
|
|
1103
|
+
async function ensureMongoDockerCompose(projectDir) {
|
|
1104
|
+
const composePath = path.join(projectDir, "docker-compose.yml");
|
|
1105
|
+
if (await fs.pathExists(composePath)) return;
|
|
1106
|
+
await fs.writeFile(composePath, MONGO_DOCKER_COMPOSE, "utf8");
|
|
1107
|
+
}
|
|
1108
|
+
async function writeMongoDockerHelpersForContext(context, projectDir) {
|
|
1109
|
+
if (context.databaseProvider !== "mongo" || context.databaseUrl) return true;
|
|
1110
|
+
try {
|
|
1111
|
+
await ensureMongoDockerCompose(projectDir);
|
|
1112
|
+
await ensurePackageScripts(projectDir, mongoDockerScripts);
|
|
1113
|
+
return true;
|
|
1114
|
+
} catch (error) {
|
|
1115
|
+
cancel(getCommandErrorMessage(error));
|
|
1116
|
+
return false;
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
885
1119
|
async function ensureRequiredPrismaFiles(projectDir) {
|
|
886
1120
|
const missingFiles = [];
|
|
887
1121
|
for (const candidates of requiredPrismaFileGroups) {
|
|
@@ -895,13 +1129,12 @@ async function ensureRequiredPrismaFiles(projectDir) {
|
|
|
895
1129
|
}
|
|
896
1130
|
if (!foundCandidate) missingFiles.push(candidates.join(" or "));
|
|
897
1131
|
}
|
|
898
|
-
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
|
|
1132
|
+
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma Next files: ${missingFiles.join(", ")}`);
|
|
899
1133
|
}
|
|
900
1134
|
async function finalizePrismaFiles(options) {
|
|
901
1135
|
const projectDir = options.projectDir ?? process.cwd();
|
|
902
1136
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
903
1137
|
await ensureRequiredPrismaFiles(projectDir);
|
|
904
|
-
const generatedDir = await fs.pathExists(path.join(prismaProjectDir, "server/utils/prisma.ts")) ? "server/generated" : "src/generated";
|
|
905
1138
|
await ensureEnvVarInEnv(prismaProjectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
|
|
906
1139
|
mode: options.databaseUrl ? "upsert" : "keep-existing",
|
|
907
1140
|
comment: "Added by create-prisma"
|
|
@@ -913,7 +1146,7 @@ async function finalizePrismaFiles(options) {
|
|
|
913
1146
|
});
|
|
914
1147
|
await ensureEnvComment(prismaProjectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
|
|
915
1148
|
}
|
|
916
|
-
await ensureGitignoreEntry(prismaProjectDir,
|
|
1149
|
+
await ensureGitignoreEntry(prismaProjectDir, ".env");
|
|
917
1150
|
}
|
|
918
1151
|
async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
919
1152
|
if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
|
|
@@ -939,7 +1172,7 @@ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
|
939
1172
|
async function writeDependenciesForContext(context, projectDir) {
|
|
940
1173
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
941
1174
|
try {
|
|
942
|
-
await writePrismaDependencies(context.databaseProvider, context.packageManager, prismaProjectDir);
|
|
1175
|
+
await writePrismaDependencies(context.databaseProvider, context.packageManager, context.authoring, prismaProjectDir);
|
|
943
1176
|
return true;
|
|
944
1177
|
} catch (error) {
|
|
945
1178
|
cancel(getCommandErrorMessage(error));
|
|
@@ -974,7 +1207,7 @@ async function installDependenciesForContext(context, projectDir) {
|
|
|
974
1207
|
}
|
|
975
1208
|
async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
|
|
976
1209
|
const initSpinner = spinner();
|
|
977
|
-
initSpinner.start("Preparing Prisma files...");
|
|
1210
|
+
initSpinner.start("Preparing Prisma Next files...");
|
|
978
1211
|
try {
|
|
979
1212
|
await finalizePrismaFiles({
|
|
980
1213
|
provider: context.databaseProvider,
|
|
@@ -982,55 +1215,106 @@ async function finalizePrismaFilesForContext(context, projectDir, provisionResul
|
|
|
982
1215
|
claimUrl: provisionResult.claimUrl,
|
|
983
1216
|
projectDir
|
|
984
1217
|
});
|
|
985
|
-
initSpinner.stop("Prisma files ready.");
|
|
1218
|
+
initSpinner.stop("Prisma Next files ready.");
|
|
986
1219
|
return true;
|
|
987
1220
|
} catch (error) {
|
|
988
|
-
initSpinner.stop("Could not prepare Prisma files.");
|
|
1221
|
+
initSpinner.stop("Could not prepare Prisma Next files.");
|
|
989
1222
|
cancel(getCommandErrorMessage(error));
|
|
990
1223
|
return false;
|
|
991
1224
|
}
|
|
992
1225
|
}
|
|
993
|
-
|
|
1226
|
+
function getPrismaNextCliCommand(packageManager, prismaNextArgs) {
|
|
1227
|
+
if (packageManager === "deno") return `deno run -A --env-file=.env npm:prisma-next@${dependencyVersionMap["prisma-next"]} ${prismaNextArgs.join(" ")}`;
|
|
1228
|
+
return getLocalPackageBinaryCommand(packageManager, "prisma-next", prismaNextArgs);
|
|
1229
|
+
}
|
|
1230
|
+
function getPrismaNextCliArgs(packageManager, prismaNextArgs) {
|
|
1231
|
+
if (packageManager === "deno") return {
|
|
1232
|
+
command: "deno",
|
|
1233
|
+
args: [
|
|
1234
|
+
"run",
|
|
1235
|
+
"-A",
|
|
1236
|
+
"--env-file=.env",
|
|
1237
|
+
`npm:prisma-next@${dependencyVersionMap["prisma-next"]}`,
|
|
1238
|
+
...prismaNextArgs
|
|
1239
|
+
]
|
|
1240
|
+
};
|
|
1241
|
+
return getLocalPackageBinaryArgs(packageManager, "prisma-next", prismaNextArgs);
|
|
1242
|
+
}
|
|
1243
|
+
async function emitPrismaNextContractForContext(context, projectDir) {
|
|
994
1244
|
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
995
|
-
if (!context.
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1245
|
+
if (!context.shouldEmit) return { didEmitContract: false };
|
|
1246
|
+
if (!context.shouldInstall) return {
|
|
1247
|
+
didEmitContract: false,
|
|
1248
|
+
warning: "Skipped contract emit because dependencies were not installed."
|
|
1249
|
+
};
|
|
1250
|
+
const emitCommand = getPrismaNextCliCommand(context.packageManager, ["contract", "emit"]);
|
|
1251
|
+
if (context.verbose) log.step(`Running ${emitCommand}`);
|
|
1252
|
+
const emitSpinner = context.verbose ? void 0 : spinner();
|
|
1253
|
+
emitSpinner?.start("Emitting Prisma Next contract...");
|
|
1000
1254
|
try {
|
|
1001
|
-
const
|
|
1002
|
-
await execa(
|
|
1255
|
+
const emitArgs = getPrismaNextCliArgs(context.packageManager, ["contract", "emit"]);
|
|
1256
|
+
await execa(emitArgs.command, emitArgs.args, {
|
|
1003
1257
|
cwd: prismaProjectDir,
|
|
1004
1258
|
stdio: context.verbose ? "inherit" : "pipe"
|
|
1005
1259
|
});
|
|
1006
|
-
if (context.verbose) log.success("Prisma
|
|
1007
|
-
else
|
|
1008
|
-
return {
|
|
1260
|
+
if (context.verbose) log.success("Prisma Next contract emitted.");
|
|
1261
|
+
else emitSpinner?.stop("Prisma Next contract emitted.");
|
|
1262
|
+
return { didEmitContract: true };
|
|
1009
1263
|
} catch (error) {
|
|
1010
|
-
if (context.verbose) log.warn("Could not
|
|
1011
|
-
else
|
|
1264
|
+
if (context.verbose) log.warn("Could not emit Prisma Next contract.");
|
|
1265
|
+
else emitSpinner?.stop("Could not emit Prisma Next contract.");
|
|
1012
1266
|
return {
|
|
1013
|
-
|
|
1014
|
-
warning: `
|
|
1267
|
+
didEmitContract: false,
|
|
1268
|
+
warning: `Contract emit failed: ${getCommandErrorMessage(error)}`
|
|
1015
1269
|
};
|
|
1016
1270
|
}
|
|
1017
1271
|
}
|
|
1018
|
-
function buildWarningLines(provisionWarning,
|
|
1272
|
+
function buildWarningLines(provisionWarning, emitWarning) {
|
|
1019
1273
|
const warningLines = [];
|
|
1020
1274
|
if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
|
|
1021
|
-
if (
|
|
1275
|
+
if (emitWarning) warningLines.push(`- ${emitWarning}`);
|
|
1022
1276
|
return warningLines;
|
|
1023
1277
|
}
|
|
1024
1278
|
function buildNextStepsForContext(opts) {
|
|
1025
|
-
const { context, options,
|
|
1279
|
+
const { context, options, didEmitContract } = opts;
|
|
1026
1280
|
const nextSteps = [...options.prependNextSteps ?? []];
|
|
1027
|
-
if (!context.shouldInstall) nextSteps.push(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
if (
|
|
1281
|
+
if (!context.shouldInstall) nextSteps.push({
|
|
1282
|
+
command: getInstallCommand(context.packageManager),
|
|
1283
|
+
description: "Install the project dependencies."
|
|
1284
|
+
});
|
|
1285
|
+
if (!didEmitContract || !context.shouldEmit) nextSteps.push({
|
|
1286
|
+
command: getRunScriptCommand(context.packageManager, "contract:emit"),
|
|
1287
|
+
description: "Emit contract.json and TypeScript types from your Prisma Next contract."
|
|
1288
|
+
});
|
|
1289
|
+
if (context.databaseProvider === "postgres") nextSteps.push({
|
|
1290
|
+
command: getRunScriptCommand(context.packageManager, "db:init"),
|
|
1291
|
+
description: "Create the initial PostgreSQL database objects and sign the database."
|
|
1292
|
+
});
|
|
1293
|
+
if (context.databaseProvider === "mongo" && !context.databaseUrl) nextSteps.push({
|
|
1294
|
+
command: getRunScriptCommand(context.packageManager, "db:up"),
|
|
1295
|
+
description: "Start the local MongoDB replica set with Docker."
|
|
1296
|
+
});
|
|
1297
|
+
nextSteps.push({
|
|
1298
|
+
command: getRunScriptCommand(context.packageManager, "migration:plan"),
|
|
1299
|
+
description: "Compare the contract to the database and write a migration plan."
|
|
1300
|
+
});
|
|
1301
|
+
nextSteps.push({
|
|
1302
|
+
command: getRunScriptCommand(context.packageManager, "migration:apply"),
|
|
1303
|
+
description: "Apply the planned migration to the database."
|
|
1304
|
+
});
|
|
1305
|
+
if (context.schemaPreset === "basic") nextSteps.push({
|
|
1306
|
+
command: getRunScriptCommand(context.packageManager, "db:seed"),
|
|
1307
|
+
description: "Insert the sample user and post data from prisma/seed.ts."
|
|
1308
|
+
});
|
|
1309
|
+
if (options.includeDevNextStep) nextSteps.push({
|
|
1310
|
+
command: getRunScriptCommand(context.packageManager, "dev"),
|
|
1311
|
+
description: "Start the development server."
|
|
1312
|
+
});
|
|
1032
1313
|
return nextSteps;
|
|
1033
1314
|
}
|
|
1315
|
+
function formatNextSteps(nextSteps) {
|
|
1316
|
+
return nextSteps.map((step) => `${step.command}\n ${step.description}`).join("\n\n");
|
|
1317
|
+
}
|
|
1034
1318
|
async function executePrismaSetupContext(context, options = {}) {
|
|
1035
1319
|
const projectDir = path.resolve(options.projectDir ?? context.projectDir);
|
|
1036
1320
|
const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
|
|
@@ -1038,687 +1322,20 @@ async function executePrismaSetupContext(context, options = {}) {
|
|
|
1038
1322
|
if (!await writeDependenciesForContext(context, projectDir)) return false;
|
|
1039
1323
|
if (!await installDependenciesForContext(context, projectDir)) return false;
|
|
1040
1324
|
if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) return false;
|
|
1041
|
-
|
|
1042
|
-
const
|
|
1325
|
+
if (!await writeMongoDockerHelpersForContext(context, projectDir)) return false;
|
|
1326
|
+
const emitResult = await emitPrismaNextContractForContext(context, projectDir);
|
|
1327
|
+
const warningLines = buildWarningLines(provisionResult.warning, emitResult.warning);
|
|
1043
1328
|
const nextSteps = buildNextStepsForContext({
|
|
1044
1329
|
context,
|
|
1045
1330
|
options,
|
|
1046
|
-
|
|
1331
|
+
didEmitContract: emitResult.didEmitContract
|
|
1047
1332
|
});
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
${nextSteps.join("\n")}`);
|
|
1333
|
+
if (warningLines.length > 0) note(warningLines.map((line) => line.replace(/^- /, "")).join("\n"), "Heads up");
|
|
1334
|
+
note(formatNextSteps(nextSteps), "Next steps");
|
|
1335
|
+
outro("Setup complete.");
|
|
1052
1336
|
return true;
|
|
1053
1337
|
}
|
|
1054
1338
|
|
|
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
1339
|
//#endregion
|
|
1723
1340
|
//#region src/ui/branding.ts
|
|
1724
1341
|
const prismaTitle = `${styleText(["bold", "cyan"], "Create")} ${styleText(["bold", "magenta"], "Prisma")}`;
|
|
@@ -1764,7 +1381,7 @@ async function promptForCreateTemplate() {
|
|
|
1764
1381
|
{
|
|
1765
1382
|
value: "hono",
|
|
1766
1383
|
label: "Hono",
|
|
1767
|
-
hint: "TypeScript API starter"
|
|
1384
|
+
hint: "Default TypeScript API starter"
|
|
1768
1385
|
},
|
|
1769
1386
|
{
|
|
1770
1387
|
value: "elysia",
|
|
@@ -1872,8 +1489,8 @@ async function runCreateCommand(rawInput = {}) {
|
|
|
1872
1489
|
}
|
|
1873
1490
|
}
|
|
1874
1491
|
async function collectCreateContext(input) {
|
|
1875
|
-
const useDefaults = input.yes === true;
|
|
1876
1492
|
const force = input.force === true;
|
|
1493
|
+
const useDefaults = input.yes === true;
|
|
1877
1494
|
const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
|
|
1878
1495
|
if (projectNameInput === void 0) return;
|
|
1879
1496
|
const projectName = String(projectNameInput).trim();
|
|
@@ -1899,20 +1516,13 @@ async function collectCreateContext(input) {
|
|
|
1899
1516
|
defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
|
|
1900
1517
|
});
|
|
1901
1518
|
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
1519
|
return {
|
|
1909
1520
|
targetDirectory,
|
|
1910
1521
|
targetPathState,
|
|
1911
1522
|
force,
|
|
1912
1523
|
template,
|
|
1913
1524
|
projectPackageName: toPackageName(path.basename(targetDirectory)),
|
|
1914
|
-
prismaSetupContext
|
|
1915
|
-
addonSetupContext: addonSetupContext ?? void 0
|
|
1525
|
+
prismaSetupContext
|
|
1916
1526
|
};
|
|
1917
1527
|
}
|
|
1918
1528
|
async function executeCreateContext(context) {
|
|
@@ -1925,6 +1535,7 @@ async function executeCreateContext(context) {
|
|
|
1925
1535
|
template: context.template,
|
|
1926
1536
|
schemaPreset: context.prismaSetupContext.schemaPreset,
|
|
1927
1537
|
provider: context.prismaSetupContext.databaseProvider,
|
|
1538
|
+
authoring: context.prismaSetupContext.authoring,
|
|
1928
1539
|
packageManager: context.prismaSetupContext.packageManager
|
|
1929
1540
|
});
|
|
1930
1541
|
scaffoldSpinner.stop("Project files scaffolded.");
|
|
@@ -1950,21 +1561,10 @@ async function executeCreateContext(context) {
|
|
|
1950
1561
|
};
|
|
1951
1562
|
}
|
|
1952
1563
|
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
|
-
}
|
|
1564
|
+
const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [{
|
|
1565
|
+
command: `cd ${formatPathForDisplay(context.targetDirectory)}`,
|
|
1566
|
+
description: "Enter your new project directory."
|
|
1567
|
+
}];
|
|
1968
1568
|
try {
|
|
1969
1569
|
if (!await executePrismaSetupContext(context.prismaSetupContext, {
|
|
1970
1570
|
prependNextSteps: nextSteps,
|
|
@@ -1985,4 +1585,4 @@ async function executeCreateContext(context) {
|
|
|
1985
1585
|
}
|
|
1986
1586
|
|
|
1987
1587
|
//#endregion
|
|
1988
|
-
export {
|
|
1588
|
+
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 };
|