create-prisma 0.4.1 → 0.4.2-next.37.102.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 +50 -44
- package/dist/cli.mjs +1 -1
- package/dist/{create-H6Tk0JlE.mjs → create-DsDWZbPE.mjs} +875 -1010
- package/dist/index.d.mts +30 -39
- package/dist/index.mjs +3 -3
- package/package.json +7 -2
- package/templates/create/_shared/prisma/seed.ts.hbs +52 -0
- package/templates/create/astro/README.md.hbs +25 -15
- package/templates/create/astro/astro.config.mjs +6 -1
- package/templates/create/astro/deno.json.hbs +1 -1
- package/templates/create/astro/package.json.hbs +3 -2
- package/templates/create/astro/src/lib/prisma.ts.hbs +60 -40
- package/templates/create/astro/src/pages/api/users.ts.hbs +6 -22
- package/templates/create/astro/src/pages/index.astro.hbs +18 -55
- package/templates/create/elysia/README.md.hbs +27 -17
- package/templates/create/elysia/deno.json.hbs +1 -1
- package/templates/create/elysia/src/index.ts.hbs +15 -11
- package/templates/create/elysia/src/lib/prisma.ts.hbs +60 -43
- package/templates/create/elysia/tsconfig.json +1 -0
- package/templates/create/hono/README.md.hbs +27 -17
- package/templates/create/hono/deno.json.hbs +1 -1
- package/templates/create/hono/src/index.ts.hbs +9 -10
- package/templates/create/hono/src/lib/prisma.ts.hbs +60 -43
- package/templates/create/hono/tsconfig.json +1 -0
- package/templates/create/minimal/.yarnrc.yml.hbs +3 -0
- package/templates/create/minimal/README.md.hbs +39 -0
- package/templates/create/{turborepo → minimal}/deno.json.hbs +1 -1
- package/templates/create/minimal/package.json.hbs +13 -0
- package/templates/create/minimal/src/index.ts.hbs +43 -0
- package/templates/create/minimal/src/lib/prisma.ts.hbs +73 -0
- package/templates/create/{turborepo/apps/api → minimal}/tsconfig.json +3 -4
- package/templates/create/nest/README.md.hbs +28 -17
- package/templates/create/nest/deno.json.hbs +1 -1
- package/templates/create/nest/src/app.module.ts.hbs +5 -6
- package/templates/create/nest/src/lib/prisma.ts.hbs +60 -45
- package/templates/create/nest/src/prisma.service.ts.hbs +6 -5
- package/templates/create/nest/src/users.controller.ts.hbs +1 -2
- package/templates/create/nest/src/users.service.ts.hbs +2 -8
- package/templates/create/nest/tsconfig.json +1 -0
- package/templates/create/next/README.md.hbs +25 -13
- package/templates/create/next/deno.json.hbs +1 -1
- package/templates/create/next/src/app/page.tsx.hbs +16 -57
- package/templates/create/next/src/lib/prisma.ts.hbs +60 -40
- package/templates/create/next/tsconfig.json +1 -0
- package/templates/create/nuxt/README.md.hbs +26 -13
- package/templates/create/nuxt/app/pages/index.vue.hbs +12 -45
- package/templates/create/nuxt/deno.json.hbs +1 -1
- package/templates/create/nuxt/nuxt.config.ts +21 -0
- package/templates/create/nuxt/package.json.hbs +2 -1
- package/templates/create/nuxt/server/api/users.get.ts.hbs +4 -15
- package/templates/create/nuxt/server/utils/prisma.ts.hbs +60 -40
- package/templates/create/svelte/README.md.hbs +25 -15
- package/templates/create/svelte/deno.json.hbs +1 -1
- package/templates/create/svelte/package.json.hbs +1 -1
- package/templates/create/svelte/src/lib/server/prisma.ts.hbs +61 -41
- package/templates/create/svelte/src/routes/+page.server.ts.hbs +4 -15
- package/templates/create/svelte/src/routes/+page.svelte.hbs +13 -163
- package/templates/create/svelte/vite.config.ts +2 -1
- package/templates/create/tanstack-start/README.md.hbs +26 -13
- package/templates/create/tanstack-start/deno.json.hbs +1 -2
- package/templates/create/tanstack-start/package.json.hbs +1 -2
- package/templates/create/tanstack-start/src/lib/prisma.server.ts.hbs +60 -40
- package/templates/create/tanstack-start/src/routes/__root.tsx.hbs +2 -3
- package/templates/create/tanstack-start/src/routes/index.tsx.hbs +26 -77
- package/templates/create/tanstack-start/tsconfig.json +1 -0
- package/templates/create/tanstack-start/vite.config.ts +7 -1
- 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/README.md.hbs +0 -29
- package/templates/create/turborepo/apps/api/package.json.hbs +0 -19
- package/templates/create/turborepo/apps/api/src/index.ts.hbs +0 -51
- package/templates/create/turborepo/package.json.hbs +0 -24
- package/templates/create/turborepo/packages/db/package.json.hbs +0 -17
- 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
- package/templates/create/turborepo/packages/db/src/client.ts.hbs +0 -58
- package/templates/create/turborepo/packages/db/src/index.ts +0 -2
- package/templates/create/turborepo/packages/db/tsconfig.json +0 -15
- package/templates/create/turborepo/turbo.json +0 -28
- /package/templates/create/{turborepo → elysia}/.yarnrc.yml.hbs +0 -0
|
@@ -1,75 +1,149 @@
|
|
|
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/
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
//#region src/telemetry/client.ts
|
|
16
|
+
const TELEMETRY_API_KEY = "phc_cmc85avbWyuJ2JyKdGPdv7dxXli8xLdWDBPbvIXWJfs";
|
|
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 isTruthyEnvValue(value) {
|
|
21
|
+
return [
|
|
22
|
+
"1",
|
|
23
|
+
"true",
|
|
24
|
+
"yes",
|
|
25
|
+
"on"
|
|
26
|
+
].includes(String(value ?? "").trim().toLowerCase());
|
|
18
27
|
}
|
|
19
|
-
function
|
|
20
|
-
|
|
28
|
+
function shouldDisableTelemetry() {
|
|
29
|
+
if (isTruthyEnvValue(process.env.CI) || isTruthyEnvValue(process.env.GITHUB_ACTIONS)) return true;
|
|
30
|
+
return process.env.CREATE_PRISMA_DISABLE_TELEMETRY !== void 0 || process.env.CREATE_PRISMA_TELEMETRY_DISABLED !== void 0 || process.env.DO_NOT_TRACK !== void 0;
|
|
21
31
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"@elysiajs/node": "^1.4.5",
|
|
27
|
-
"@prisma/client": "^7.4.0",
|
|
28
|
-
"@prisma/adapter-pg": "^7.4.0",
|
|
29
|
-
"@prisma/adapter-mariadb": "^7.4.0",
|
|
30
|
-
"@prisma/adapter-better-sqlite3": "^7.4.0",
|
|
31
|
-
"@prisma/adapter-mssql": "^7.4.0",
|
|
32
|
-
"@types/node": "^24.3.0",
|
|
33
|
-
dotenv: "^17.2.3",
|
|
34
|
-
"node-gyp": "^11.5.0",
|
|
35
|
-
prisma: "^7.4.0",
|
|
36
|
-
tsx: "^4.21.0"
|
|
37
|
-
};
|
|
38
|
-
function getWorkspaceDependencyVersion(packageManager) {
|
|
39
|
-
return packageManager === "npm" ? "*" : "workspace:*";
|
|
32
|
+
function getTelemetryConfigDir() {
|
|
33
|
+
if (process.platform === "darwin") return path.join(os.homedir(), "Library", "Application Support", "create-prisma");
|
|
34
|
+
if (process.platform === "win32") return path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "create-prisma");
|
|
35
|
+
return path.join(process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"), "create-prisma");
|
|
40
36
|
}
|
|
41
|
-
function
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
async function getAnonymousId() {
|
|
38
|
+
const telemetryConfigPath = path.join(getTelemetryConfigDir(), TELEMETRY_CONFIG_FILE);
|
|
39
|
+
try {
|
|
40
|
+
const config = await fs.readJSON(telemetryConfigPath);
|
|
41
|
+
if (typeof config.anonymousId === "string" && UUID_V4_REGEX.test(config.anonymousId)) return config.anonymousId;
|
|
42
|
+
} catch {}
|
|
43
|
+
const anonymousId = randomUUID();
|
|
44
|
+
try {
|
|
45
|
+
await fs.ensureDir(path.dirname(telemetryConfigPath));
|
|
46
|
+
await fs.writeJSON(telemetryConfigPath, { anonymousId }, { spaces: 2 });
|
|
47
|
+
} catch {}
|
|
48
|
+
return anonymousId;
|
|
49
|
+
}
|
|
50
|
+
function getCommonProperties() {
|
|
51
|
+
return {
|
|
52
|
+
"cli-version": "0.4.2-next.37.102.1",
|
|
53
|
+
"node-version": process.version,
|
|
54
|
+
platform: process.platform,
|
|
55
|
+
arch: process.arch
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function sanitizeProperties(properties) {
|
|
59
|
+
return Object.fromEntries(Object.entries(properties).filter(([, value]) => value !== void 0));
|
|
60
|
+
}
|
|
61
|
+
async function trackCliTelemetry(event, properties) {
|
|
62
|
+
if (shouldDisableTelemetry()) return;
|
|
63
|
+
let client;
|
|
64
|
+
try {
|
|
65
|
+
const distinctId = await getAnonymousId();
|
|
66
|
+
const sanitizedProperties = sanitizeProperties({
|
|
67
|
+
...getCommonProperties(),
|
|
68
|
+
...properties,
|
|
69
|
+
$process_person_profile: false
|
|
49
70
|
});
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
71
|
+
client = new PostHog(TELEMETRY_API_KEY, {
|
|
72
|
+
host: TELEMETRY_HOST,
|
|
73
|
+
disableGeoip: true,
|
|
74
|
+
flushAt: 1,
|
|
75
|
+
flushInterval: 0
|
|
76
|
+
});
|
|
77
|
+
await client.captureImmediate({
|
|
78
|
+
distinctId,
|
|
79
|
+
event,
|
|
80
|
+
properties: sanitizedProperties,
|
|
81
|
+
disableGeoip: true
|
|
54
82
|
});
|
|
83
|
+
} catch {} finally {
|
|
84
|
+
if (client) await client.shutdown().catch(() => {});
|
|
55
85
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
//#endregion
|
|
89
|
+
//#region src/telemetry/create.ts
|
|
90
|
+
const CREATE_PRISMA_NEXT_COMPLETED_EVENT = "cli:create_prisma_next_command_completed";
|
|
91
|
+
const CREATE_PRISMA_NEXT_FAILED_EVENT = "cli:create_prisma_next_command_failed";
|
|
92
|
+
function getTargetDirectoryState(context) {
|
|
93
|
+
if (!context.targetPathState.exists) return "new";
|
|
94
|
+
if (context.targetPathState.isEmptyDirectory) return "empty_directory";
|
|
95
|
+
return "non_empty_directory";
|
|
96
|
+
}
|
|
97
|
+
function getBaseCreateProperties(input, context) {
|
|
98
|
+
return {
|
|
99
|
+
command: "create",
|
|
100
|
+
"uses-defaults": input.yes === true,
|
|
101
|
+
verbose: input.verbose === true,
|
|
102
|
+
force: input.force === true,
|
|
103
|
+
template: context?.template ?? input.template ?? null,
|
|
104
|
+
"database-provider": context?.prismaSetupContext.databaseProvider ?? input.provider ?? null,
|
|
105
|
+
"authoring-style": context?.prismaSetupContext.authoring ?? input.authoring ?? null,
|
|
106
|
+
"package-manager": context?.prismaSetupContext.packageManager ?? input.packageManager ?? null,
|
|
107
|
+
"should-install": context?.prismaSetupContext.shouldInstall ?? input.install ?? null,
|
|
108
|
+
"should-emit": context?.prismaSetupContext.shouldEmit ?? input.emit ?? null,
|
|
109
|
+
"uses-prisma-postgres": context?.prismaSetupContext.shouldUsePrismaPostgres ?? input.prismaPostgres ?? null,
|
|
110
|
+
"target-directory-state": context ? getTargetDirectoryState(context) : null
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function getErrorName(error) {
|
|
114
|
+
if (error instanceof Error) return error.name;
|
|
115
|
+
return error === void 0 ? null : "UnknownError";
|
|
116
|
+
}
|
|
117
|
+
function getErrorCode(error) {
|
|
118
|
+
if (typeof error !== "object" || error === null) return null;
|
|
119
|
+
const exitCode = Reflect.get(error, "exitCode");
|
|
120
|
+
if (typeof exitCode === "number") return exitCode;
|
|
121
|
+
const code = Reflect.get(error, "code");
|
|
122
|
+
return typeof code === "number" || typeof code === "string" ? code : null;
|
|
123
|
+
}
|
|
124
|
+
async function trackCreateCompleted(params) {
|
|
125
|
+
await trackCliTelemetry(CREATE_PRISMA_NEXT_COMPLETED_EVENT, {
|
|
126
|
+
...getBaseCreateProperties(params.input, params.context),
|
|
127
|
+
"duration-ms": params.durationMs
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async function trackCreateFailed(params) {
|
|
131
|
+
await trackCliTelemetry(CREATE_PRISMA_NEXT_FAILED_EVENT, {
|
|
132
|
+
...getBaseCreateProperties(params.input, params.context),
|
|
133
|
+
"duration-ms": params.durationMs,
|
|
134
|
+
"failure-stage": params.stage,
|
|
135
|
+
"error-name": getErrorName(params.error),
|
|
136
|
+
"error-code": getErrorCode(params.error)
|
|
61
137
|
});
|
|
62
|
-
return targets;
|
|
63
138
|
}
|
|
64
139
|
|
|
65
140
|
//#endregion
|
|
66
141
|
//#region src/types.ts
|
|
67
|
-
const
|
|
142
|
+
const databaseProviderInputs = [
|
|
143
|
+
"postgres",
|
|
68
144
|
"postgresql",
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"sqlserver",
|
|
72
|
-
"cockroachdb"
|
|
145
|
+
"mongo",
|
|
146
|
+
"mongodb"
|
|
73
147
|
];
|
|
74
148
|
const packageManagers = [
|
|
75
149
|
"npm",
|
|
@@ -78,8 +152,9 @@ const packageManagers = [
|
|
|
78
152
|
"bun",
|
|
79
153
|
"deno"
|
|
80
154
|
];
|
|
81
|
-
const
|
|
155
|
+
const authoringStyles = ["psl", "typescript"];
|
|
82
156
|
const createTemplates = [
|
|
157
|
+
"minimal",
|
|
83
158
|
"hono",
|
|
84
159
|
"elysia",
|
|
85
160
|
"nest",
|
|
@@ -87,56 +162,35 @@ const createTemplates = [
|
|
|
87
162
|
"svelte",
|
|
88
163
|
"astro",
|
|
89
164
|
"nuxt",
|
|
90
|
-
"tanstack-start"
|
|
91
|
-
"turborepo"
|
|
165
|
+
"tanstack-start"
|
|
92
166
|
];
|
|
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);
|
|
167
|
+
function normalizeDatabaseProvider(value) {
|
|
168
|
+
if (value === "postgresql") return "postgres";
|
|
169
|
+
if (value === "mongodb") return "mongo";
|
|
170
|
+
return value;
|
|
171
|
+
}
|
|
172
|
+
const DatabaseProviderSchema = z.enum(databaseProviderInputs).transform(normalizeDatabaseProvider);
|
|
112
173
|
const PackageManagerSchema = z.enum(packageManagers);
|
|
113
|
-
const
|
|
174
|
+
const AuthoringStyleSchema = z.enum(authoringStyles);
|
|
114
175
|
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
176
|
const DatabaseUrlSchema = z.string().trim().min(1, "Please enter a valid database URL");
|
|
120
177
|
const CommonCommandOptionsSchema = z.object({
|
|
121
178
|
yes: z.boolean().optional().describe("Skip prompts and accept default choices"),
|
|
122
179
|
verbose: z.boolean().optional().describe("Show verbose command output during setup")
|
|
123
180
|
});
|
|
124
181
|
const PrismaSetupOptionsSchema = z.object({
|
|
125
|
-
provider: DatabaseProviderSchema.optional().describe("
|
|
182
|
+
provider: DatabaseProviderSchema.optional().describe("Prisma Next database target: PostgreSQL relational models or MongoDB document models"),
|
|
183
|
+
authoring: AuthoringStyleSchema.optional().describe("Contract authoring style"),
|
|
126
184
|
packageManager: PackageManagerSchema.optional().describe("Package manager used for dependency installation"),
|
|
127
|
-
prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when
|
|
185
|
+
prismaPostgres: z.boolean().optional().describe("Provision Prisma Postgres with create-db when target is postgres"),
|
|
128
186
|
databaseUrl: DatabaseUrlSchema.optional().describe("DATABASE_URL value"),
|
|
129
187
|
install: z.boolean().optional().describe("Install dependencies with selected package manager"),
|
|
130
|
-
|
|
131
|
-
schemaPreset: SchemaPresetSchema.optional().describe("Schema preset to scaffold in prisma/schema.prisma")
|
|
188
|
+
emit: z.boolean().optional().describe("Emit Prisma Next contract artifacts after scaffolding")
|
|
132
189
|
});
|
|
133
190
|
const PrismaSetupCommandInputSchema = CommonCommandOptionsSchema.extend(PrismaSetupOptionsSchema.shape);
|
|
134
191
|
const CreateScaffoldOptionsSchema = z.object({
|
|
135
192
|
name: z.string().trim().min(1, "Please enter a valid project name").optional().describe("Project name / directory"),
|
|
136
193
|
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
194
|
force: z.boolean().optional().describe("Allow scaffolding into a non-empty target directory")
|
|
141
195
|
});
|
|
142
196
|
const CreateCommandInputSchema = PrismaSetupCommandInputSchema.extend(CreateScaffoldOptionsSchema.shape);
|
|
@@ -221,13 +275,17 @@ function getPackageManagerManifestValue(packageManager) {
|
|
|
221
275
|
return packageManagerManifestValues[packageManager];
|
|
222
276
|
}
|
|
223
277
|
function getDenoPrismaSpecifier() {
|
|
224
|
-
return
|
|
278
|
+
return "npm:prisma-next";
|
|
279
|
+
}
|
|
280
|
+
function getDenoNpmSpecifier(packageSpecifier) {
|
|
281
|
+
return `npm:${packageSpecifier.replace(/@latest$/, "")}`;
|
|
225
282
|
}
|
|
226
283
|
function getDenoAllowedScriptSpecifiers() {
|
|
227
284
|
return [
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
285
|
+
"npm:prisma-next",
|
|
286
|
+
"npm:@prisma-next/postgres",
|
|
287
|
+
"npm:@prisma-next/mongo",
|
|
288
|
+
"npm:mongodb-memory-server"
|
|
231
289
|
].join(",");
|
|
232
290
|
}
|
|
233
291
|
function getInstallCommand(packageManager) {
|
|
@@ -311,7 +369,7 @@ function getPackageExecutionArgs(packageManager, commandArgs) {
|
|
|
311
369
|
args: [
|
|
312
370
|
"run",
|
|
313
371
|
"-A",
|
|
314
|
-
|
|
372
|
+
getDenoNpmSpecifier(packageName),
|
|
315
373
|
...args
|
|
316
374
|
]
|
|
317
375
|
};
|
|
@@ -326,29 +384,58 @@ function getPackageExecutionCommand(packageManager, commandArgs) {
|
|
|
326
384
|
const execution = getPackageExecutionArgs(packageManager, commandArgs);
|
|
327
385
|
return [execution.command, ...execution.args].join(" ");
|
|
328
386
|
}
|
|
329
|
-
function
|
|
330
|
-
|
|
331
|
-
"
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
"
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
387
|
+
function getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs) {
|
|
388
|
+
switch (packageManager) {
|
|
389
|
+
case "pnpm": return {
|
|
390
|
+
command: "pnpm",
|
|
391
|
+
args: [
|
|
392
|
+
"exec",
|
|
393
|
+
binaryName,
|
|
394
|
+
...binaryArgs
|
|
395
|
+
]
|
|
396
|
+
};
|
|
397
|
+
case "yarn": return {
|
|
398
|
+
command: "yarn",
|
|
399
|
+
args: [binaryName, ...binaryArgs]
|
|
400
|
+
};
|
|
401
|
+
case "bun": return {
|
|
402
|
+
command: "bun",
|
|
403
|
+
args: [binaryName, ...binaryArgs]
|
|
404
|
+
};
|
|
405
|
+
case "deno": return {
|
|
406
|
+
command: "deno",
|
|
407
|
+
args: [
|
|
408
|
+
"run",
|
|
409
|
+
"-A",
|
|
410
|
+
`npm:${binaryName}`,
|
|
411
|
+
...binaryArgs
|
|
412
|
+
]
|
|
413
|
+
};
|
|
414
|
+
default: return {
|
|
415
|
+
command: "npm",
|
|
416
|
+
args: [
|
|
417
|
+
"exec",
|
|
418
|
+
binaryName,
|
|
419
|
+
"--",
|
|
420
|
+
...binaryArgs
|
|
421
|
+
]
|
|
422
|
+
};
|
|
423
|
+
}
|
|
346
424
|
}
|
|
347
|
-
function
|
|
348
|
-
const execution =
|
|
425
|
+
function getLocalPackageBinaryCommand(packageManager, binaryName, binaryArgs) {
|
|
426
|
+
const execution = getLocalPackageBinaryArgs(packageManager, binaryName, binaryArgs);
|
|
349
427
|
return [execution.command, ...execution.args].join(" ");
|
|
350
428
|
}
|
|
351
429
|
|
|
430
|
+
//#endregion
|
|
431
|
+
//#region src/utils/runtime.ts
|
|
432
|
+
function usesNodeStyleRuntime(packageManager) {
|
|
433
|
+
return packageManager !== void 0 && packageManager !== "bun" && packageManager !== "deno";
|
|
434
|
+
}
|
|
435
|
+
function requiresDotenvConfigImport(packageManager) {
|
|
436
|
+
return usesNodeStyleRuntime(packageManager);
|
|
437
|
+
}
|
|
438
|
+
|
|
352
439
|
//#endregion
|
|
353
440
|
//#region src/templates/shared.ts
|
|
354
441
|
function getOptionalHashString(hash, key) {
|
|
@@ -431,94 +518,176 @@ async function renderTemplateTree(opts) {
|
|
|
431
518
|
function getCreateTemplateDir(template) {
|
|
432
519
|
return resolveTemplatesDir(`templates/create/${template}`);
|
|
433
520
|
}
|
|
434
|
-
function
|
|
521
|
+
function getCreateSharedTemplateDir() {
|
|
522
|
+
return resolveTemplatesDir("templates/create/_shared");
|
|
523
|
+
}
|
|
524
|
+
function createTemplateContext(projectName, template, provider, authoring, packageManager) {
|
|
435
525
|
return {
|
|
436
526
|
projectName,
|
|
527
|
+
template,
|
|
437
528
|
provider,
|
|
438
|
-
|
|
529
|
+
authoring,
|
|
439
530
|
packageManager
|
|
440
531
|
};
|
|
441
532
|
}
|
|
442
533
|
async function scaffoldCreateTemplate(opts) {
|
|
443
|
-
const { projectDir, projectName, template, provider,
|
|
534
|
+
const { projectDir, projectName, template, provider, authoring, packageManager } = opts;
|
|
535
|
+
const templateRoot = getCreateTemplateDir(template);
|
|
536
|
+
const sharedTemplateRoot = getCreateSharedTemplateDir();
|
|
537
|
+
const context = createTemplateContext(projectName, template, provider, authoring, packageManager);
|
|
538
|
+
await renderTemplateTree({
|
|
539
|
+
templateRoot: sharedTemplateRoot,
|
|
540
|
+
outputDir: projectDir,
|
|
541
|
+
context
|
|
542
|
+
});
|
|
444
543
|
await renderTemplateTree({
|
|
445
|
-
templateRoot
|
|
544
|
+
templateRoot,
|
|
446
545
|
outputDir: projectDir,
|
|
447
|
-
context
|
|
546
|
+
context
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
//#endregion
|
|
551
|
+
//#region src/constants/dependencies.ts
|
|
552
|
+
const dependencyVersionMap = {
|
|
553
|
+
"@elysiajs/node": "^1.4.5",
|
|
554
|
+
"@types/node": "^25.6.2",
|
|
555
|
+
dotenv: "^17.4.2",
|
|
556
|
+
"mongodb-memory-server": "^11.1.0",
|
|
557
|
+
tsx: "^4.21.0"
|
|
558
|
+
};
|
|
559
|
+
const PRISMA_NEXT_PACKAGE_VERSION = "latest";
|
|
560
|
+
function isPrismaNextPackage(packageName) {
|
|
561
|
+
return packageName === "prisma-next" || packageName.startsWith("@prisma-next/");
|
|
562
|
+
}
|
|
563
|
+
function getPrismaNextPackageSpecifier(packageName) {
|
|
564
|
+
return `${packageName}@${PRISMA_NEXT_PACKAGE_VERSION}`;
|
|
565
|
+
}
|
|
566
|
+
function getDependencyVersion(packageName) {
|
|
567
|
+
if (isPrismaNextPackage(packageName)) return PRISMA_NEXT_PACKAGE_VERSION;
|
|
568
|
+
return dependencyVersionMap[packageName];
|
|
569
|
+
}
|
|
570
|
+
function usesViteDevServer(template) {
|
|
571
|
+
return template === "astro" || template === "nuxt" || template === "svelte" || template === "tanstack-start";
|
|
572
|
+
}
|
|
573
|
+
function getCreateTemplateDependencies(template, packageManager) {
|
|
574
|
+
const targets = [];
|
|
575
|
+
if (usesViteDevServer(template)) targets.push({
|
|
576
|
+
packageJsonPath: "package.json",
|
|
577
|
+
dependencies: [],
|
|
578
|
+
devDependencies: ["@prisma-next/vite-plugin-contract-emit"]
|
|
448
579
|
});
|
|
580
|
+
if (template === "minimal" || template === "hono" || template === "elysia" || template === "nest") {
|
|
581
|
+
const runtimeDevDependencies = usesNodeStyleRuntime(packageManager) ? ["tsx"] : [];
|
|
582
|
+
if (template === "elysia" && packageManager !== "deno") targets.push({
|
|
583
|
+
packageJsonPath: "package.json",
|
|
584
|
+
dependencies: ["@elysiajs/node"],
|
|
585
|
+
devDependencies: ["@types/node", ...runtimeDevDependencies]
|
|
586
|
+
});
|
|
587
|
+
else if (runtimeDevDependencies.length > 0) targets.push({
|
|
588
|
+
packageJsonPath: "package.json",
|
|
589
|
+
dependencies: [],
|
|
590
|
+
devDependencies: runtimeDevDependencies
|
|
591
|
+
});
|
|
592
|
+
}
|
|
593
|
+
return targets;
|
|
449
594
|
}
|
|
450
595
|
|
|
451
596
|
//#endregion
|
|
452
597
|
//#region src/constants/db-packages.ts
|
|
453
|
-
function getDbPackages(provider) {
|
|
598
|
+
function getDbPackages(provider, _packageManager) {
|
|
454
599
|
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";
|
|
600
|
+
case "postgres": return "@prisma-next/postgres";
|
|
601
|
+
case "mongo": return "@prisma-next/mongo";
|
|
460
602
|
default: {
|
|
461
603
|
const exhaustiveCheck = provider;
|
|
462
|
-
throw new Error(`Unsupported
|
|
604
|
+
throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
|
|
463
605
|
}
|
|
464
606
|
}
|
|
465
607
|
}
|
|
466
608
|
|
|
467
609
|
//#endregion
|
|
468
610
|
//#region src/tasks/install.ts
|
|
469
|
-
function
|
|
611
|
+
function getPrismaNextScriptMap(packageManager) {
|
|
470
612
|
if (packageManager === "deno") {
|
|
471
|
-
const
|
|
613
|
+
const prismaNextCli = `deno run -A --env-file=.env ${getDenoPrismaSpecifier()}`;
|
|
472
614
|
return {
|
|
473
|
-
"
|
|
474
|
-
"db:
|
|
475
|
-
"db:
|
|
476
|
-
"db:
|
|
615
|
+
"contract:emit": `${prismaNextCli} contract emit`,
|
|
616
|
+
"db:init": `${prismaNextCli} db init`,
|
|
617
|
+
"db:update": `${prismaNextCli} db update`,
|
|
618
|
+
"db:verify": `${prismaNextCli} db verify`,
|
|
619
|
+
"db:seed": "deno run -A --env-file=.env prisma/seed.ts",
|
|
620
|
+
"migration:plan": `${prismaNextCli} migration plan`,
|
|
621
|
+
migrate: `${prismaNextCli} migrate`,
|
|
622
|
+
"migration:status": `${prismaNextCli} migration status`,
|
|
623
|
+
"migration:show": `${prismaNextCli} migration show`
|
|
477
624
|
};
|
|
478
625
|
}
|
|
479
626
|
if (packageManager === "bun") {
|
|
480
|
-
const
|
|
627
|
+
const prismaNextCli = "bun prisma-next";
|
|
481
628
|
return {
|
|
482
|
-
"
|
|
483
|
-
"db:
|
|
484
|
-
"db:
|
|
485
|
-
"db:
|
|
629
|
+
"contract:emit": `${prismaNextCli} contract emit`,
|
|
630
|
+
"db:init": `${prismaNextCli} db init`,
|
|
631
|
+
"db:update": `${prismaNextCli} db update`,
|
|
632
|
+
"db:verify": `${prismaNextCli} db verify`,
|
|
633
|
+
"db:seed": "bun prisma/seed.ts",
|
|
634
|
+
"migration:plan": `${prismaNextCli} migration plan`,
|
|
635
|
+
migrate: `${prismaNextCli} migrate`,
|
|
636
|
+
"migration:status": `${prismaNextCli} migration status`,
|
|
637
|
+
"migration:show": `${prismaNextCli} migration show`
|
|
486
638
|
};
|
|
487
639
|
}
|
|
488
640
|
return {
|
|
489
|
-
"
|
|
490
|
-
"db:
|
|
491
|
-
"db:
|
|
492
|
-
"db:
|
|
641
|
+
"contract:emit": "prisma-next contract emit",
|
|
642
|
+
"db:init": "prisma-next db init",
|
|
643
|
+
"db:update": "prisma-next db update",
|
|
644
|
+
"db:verify": "prisma-next db verify",
|
|
645
|
+
"db:seed": "tsx prisma/seed.ts",
|
|
646
|
+
"migration:plan": "prisma-next migration plan",
|
|
647
|
+
migrate: "prisma-next migrate",
|
|
648
|
+
"migration:status": "prisma-next migration status",
|
|
649
|
+
"migration:show": "prisma-next migration show"
|
|
493
650
|
};
|
|
494
651
|
}
|
|
495
|
-
function getVersion(packageName) {
|
|
496
|
-
return dependencyVersionMap[packageName];
|
|
497
|
-
}
|
|
498
652
|
function unique(items) {
|
|
499
653
|
return [...new Set(items)];
|
|
500
654
|
}
|
|
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"];
|
|
687
|
+
}
|
|
688
|
+
function getOrmTypePackages(provider) {
|
|
689
|
+
if (provider === "mongo") return ["@prisma-next/mongo-orm"];
|
|
690
|
+
return ["@prisma-next/sql-orm-client"];
|
|
522
691
|
}
|
|
523
692
|
async function addPackageDependency(opts) {
|
|
524
693
|
const { dependencies = [], devDependencies = [], customDependencies = {}, scripts = {}, scriptMode, projectDir } = opts;
|
|
@@ -529,12 +698,12 @@ async function addPackageDependency(opts) {
|
|
|
529
698
|
if (!pkgJson.devDependencies) pkgJson.devDependencies = {};
|
|
530
699
|
if (!pkgJson.scripts) pkgJson.scripts = {};
|
|
531
700
|
for (const pkgName of unique(dependencies)) {
|
|
532
|
-
const version =
|
|
701
|
+
const version = getDependencyVersion(pkgName);
|
|
533
702
|
if (version) pkgJson.dependencies[pkgName] = version;
|
|
534
703
|
else console.warn(`Warning: Dependency ${pkgName} not found in version map.`);
|
|
535
704
|
}
|
|
536
705
|
for (const pkgName of unique(devDependencies)) {
|
|
537
|
-
const version =
|
|
706
|
+
const version = getDependencyVersion(pkgName);
|
|
538
707
|
if (version) pkgJson.devDependencies[pkgName] = version;
|
|
539
708
|
else console.warn(`Warning: Dev dependency ${pkgName} not found in version map.`);
|
|
540
709
|
}
|
|
@@ -550,17 +719,21 @@ async function addPackageDependency(opts) {
|
|
|
550
719
|
pkgJson.devDependencies = sortRecord(pkgJson.devDependencies);
|
|
551
720
|
await fs.writeJson(pkgJsonPath, pkgJson, { spaces: 2 });
|
|
552
721
|
}
|
|
553
|
-
async function writePrismaDependencies(provider, packageManager, projectDir = process.cwd()) {
|
|
554
|
-
const dependencies = ["
|
|
555
|
-
const devDependencies = [
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
722
|
+
async function writePrismaDependencies(provider, packageManager, authoring, projectDir = process.cwd()) {
|
|
723
|
+
const dependencies = [getDbPackages(provider, packageManager), "dotenv"];
|
|
724
|
+
const devDependencies = [
|
|
725
|
+
"prisma-next",
|
|
726
|
+
"@prisma-next/cli",
|
|
727
|
+
"@types/node"
|
|
728
|
+
];
|
|
729
|
+
devDependencies.push(...getGeneratedContractTypePackages(provider));
|
|
730
|
+
devDependencies.push(...getMigrationPackages(provider));
|
|
731
|
+
devDependencies.push(...getOrmTypePackages(provider));
|
|
732
|
+
if (authoring === "typescript") devDependencies.push(...getTypeScriptContractPackages(provider));
|
|
559
733
|
await addPackageDependency({
|
|
560
734
|
dependencies,
|
|
561
735
|
devDependencies,
|
|
562
|
-
scripts:
|
|
563
|
-
scriptMode: "if-missing",
|
|
736
|
+
scripts: getPrismaNextScriptMap(packageManager),
|
|
564
737
|
projectDir
|
|
565
738
|
});
|
|
566
739
|
}
|
|
@@ -580,8 +753,10 @@ async function writeCreateTemplateDependencies(opts) {
|
|
|
580
753
|
async function installProjectDependencies(packageManager, projectDir = process.cwd(), options = {}) {
|
|
581
754
|
const verbose = options.verbose === true;
|
|
582
755
|
const installCommand = getInstallArgs(packageManager);
|
|
756
|
+
const env = packageManager === "yarn" ? { YARN_ENABLE_IMMUTABLE_INSTALLS: "false" } : void 0;
|
|
583
757
|
await execa(installCommand.command, installCommand.args, {
|
|
584
758
|
cwd: projectDir,
|
|
759
|
+
env,
|
|
585
760
|
stdio: verbose ? "inherit" : "pipe"
|
|
586
761
|
});
|
|
587
762
|
}
|
|
@@ -643,55 +818,233 @@ function getCreateDbCommand(packageManager) {
|
|
|
643
818
|
|
|
644
819
|
//#endregion
|
|
645
820
|
//#region src/tasks/setup-prisma.ts
|
|
646
|
-
const DEFAULT_DATABASE_PROVIDER = "
|
|
647
|
-
const
|
|
648
|
-
const DEFAULT_PRISMA_POSTGRES = true;
|
|
821
|
+
const DEFAULT_DATABASE_PROVIDER = "postgres";
|
|
822
|
+
const DEFAULT_AUTHORING = "psl";
|
|
649
823
|
const DEFAULT_INSTALL = true;
|
|
650
|
-
const
|
|
824
|
+
const DEFAULT_EMIT = true;
|
|
825
|
+
const DEFAULT_INTERACTIVE_PRISMA_POSTGRES = true;
|
|
826
|
+
const DEFAULT_AUTOMATED_PRISMA_POSTGRES = false;
|
|
827
|
+
const MONGO_MEMORY_SERVER_SCRIPT = `import { spawn } from "node:child_process";
|
|
828
|
+
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
829
|
+
import path from "node:path";
|
|
830
|
+
|
|
831
|
+
const defaultDatabaseUrl = "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
|
|
832
|
+
const dataRoot = path.resolve(process.env.MONGO_DB_PATH ?? ".mongo-data");
|
|
833
|
+
const dbPath = path.join(dataRoot, "db");
|
|
834
|
+
const pidFile = path.join(dataRoot, "mongo.pid");
|
|
835
|
+
const logFile = path.join(dataRoot, "mongo.log");
|
|
836
|
+
const readyTimeoutMs = Number(process.env.MONGO_READY_TIMEOUT_MS ?? 60_000);
|
|
837
|
+
|
|
838
|
+
function getMongoConfig() {
|
|
839
|
+
const databaseUrl = process.env.DATABASE_URL ?? defaultDatabaseUrl;
|
|
840
|
+
const url = new URL(databaseUrl);
|
|
841
|
+
if (url.protocol !== "mongodb:") {
|
|
842
|
+
throw new Error("DATABASE_URL must use the mongodb:// protocol.");
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const port = Number(url.port || "27017");
|
|
846
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535) {
|
|
847
|
+
throw new Error(\`DATABASE_URL has an invalid MongoDB port: \${url.port}\`);
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
return {
|
|
851
|
+
databaseUrl,
|
|
852
|
+
port,
|
|
853
|
+
replSetName: url.searchParams.get("replicaSet") || "rs0",
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function readPid() {
|
|
858
|
+
if (!existsSync(pidFile)) return null;
|
|
859
|
+
const raw = readFileSync(pidFile, "utf8").trim();
|
|
860
|
+
const pid = Number(raw);
|
|
861
|
+
return Number.isFinite(pid) && pid > 0 ? pid : null;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function isAlive(pid) {
|
|
865
|
+
try {
|
|
866
|
+
process.kill(pid, 0);
|
|
867
|
+
return true;
|
|
868
|
+
} catch {
|
|
869
|
+
return false;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
function getChildCommand() {
|
|
874
|
+
const scriptPath = path.resolve(process.argv[1] ?? "");
|
|
875
|
+
const versions = process.versions;
|
|
876
|
+
if (versions.deno) return { command: process.execPath, args: ["run", "-A", scriptPath, "_run"] };
|
|
877
|
+
return { command: process.execPath, args: [scriptPath, "_run"] };
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
async function runServer() {
|
|
881
|
+
mkdirSync(dbPath, { recursive: true });
|
|
882
|
+
const config = getMongoConfig();
|
|
883
|
+
const memoryServer = await import("mongodb-memory-server");
|
|
884
|
+
const { MongoMemoryReplSet } = memoryServer.default ?? memoryServer;
|
|
885
|
+
const replSet = await MongoMemoryReplSet.create({
|
|
886
|
+
replSet: { name: config.replSetName, count: 1 },
|
|
887
|
+
instanceOpts: [{ port: config.port, storageEngine: "wiredTiger", dbPath }],
|
|
888
|
+
});
|
|
889
|
+
console.log(\`MongoDB server ready for \${config.databaseUrl}\`);
|
|
890
|
+
console.log(\`Data directory: \${dbPath}\`);
|
|
891
|
+
const shutdown = async () => {
|
|
892
|
+
await replSet.stop();
|
|
893
|
+
process.exit(0);
|
|
894
|
+
};
|
|
895
|
+
process.on("SIGINT", shutdown);
|
|
896
|
+
process.on("SIGTERM", shutdown);
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
async function up() {
|
|
900
|
+
mkdirSync(dataRoot, { recursive: true });
|
|
901
|
+
const existing = readPid();
|
|
902
|
+
if (existing !== null && isAlive(existing)) {
|
|
903
|
+
console.log(\`MongoDB is already running (PID \${existing}). Use \\\`db:down\\\` to stop.\`);
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
if (existing !== null) rmSync(pidFile, { force: true });
|
|
907
|
+
|
|
908
|
+
writeFileSync(logFile, "");
|
|
909
|
+
const logFd = openSync(logFile, "a");
|
|
910
|
+
const { command, args } = getChildCommand();
|
|
911
|
+
const child = spawn(
|
|
912
|
+
command,
|
|
913
|
+
args,
|
|
914
|
+
{
|
|
915
|
+
detached: true,
|
|
916
|
+
stdio: ["ignore", logFd, logFd],
|
|
917
|
+
env: process.env,
|
|
918
|
+
},
|
|
919
|
+
);
|
|
920
|
+
closeSync(logFd);
|
|
921
|
+
if (typeof child.pid !== "number") throw new Error("Failed to spawn MongoDB child process.");
|
|
922
|
+
writeFileSync(pidFile, String(child.pid));
|
|
923
|
+
child.unref();
|
|
924
|
+
|
|
925
|
+
const start = Date.now();
|
|
926
|
+
while (Date.now() - start < readyTimeoutMs) {
|
|
927
|
+
if (!isAlive(child.pid)) {
|
|
928
|
+
console.error("MongoDB failed to start:");
|
|
929
|
+
console.error(readFileSync(logFile, "utf8"));
|
|
930
|
+
rmSync(pidFile, { force: true });
|
|
931
|
+
process.exit(1);
|
|
932
|
+
}
|
|
933
|
+
const log = readFileSync(logFile, "utf8");
|
|
934
|
+
if (log.includes("MongoDB server ready")) {
|
|
935
|
+
for (const line of log.split("\\n")) {
|
|
936
|
+
if (line.trim().length > 0) console.log(line);
|
|
937
|
+
}
|
|
938
|
+
console.log(\`Detached (PID \${child.pid}). Logs: \${logFile}\`);
|
|
939
|
+
console.log("Stop with \`db:down\` or wipe with \`db:reset\`.");
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
943
|
+
}
|
|
944
|
+
console.error(\`Timed out waiting for MongoDB after \${readyTimeoutMs}ms.\`);
|
|
945
|
+
console.error(readFileSync(logFile, "utf8"));
|
|
946
|
+
try {
|
|
947
|
+
process.kill(child.pid, "SIGTERM");
|
|
948
|
+
} catch {
|
|
949
|
+
// ignore
|
|
950
|
+
}
|
|
951
|
+
rmSync(pidFile, { force: true });
|
|
952
|
+
process.exit(1);
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
async function down(wipe) {
|
|
956
|
+
const pid = readPid();
|
|
957
|
+
if (pid !== null && isAlive(pid)) {
|
|
958
|
+
process.kill(pid, "SIGTERM");
|
|
959
|
+
const deadline = Date.now() + 10_000;
|
|
960
|
+
while (Date.now() < deadline && isAlive(pid)) {
|
|
961
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
962
|
+
}
|
|
963
|
+
if (isAlive(pid)) {
|
|
964
|
+
console.warn(\`MongoDB (PID \${pid}) did not exit within 10s; sending SIGKILL.\`);
|
|
965
|
+
try {
|
|
966
|
+
process.kill(pid, "SIGKILL");
|
|
967
|
+
} catch {
|
|
968
|
+
// ignore
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
console.log(\`Stopped MongoDB (PID \${pid}).\`);
|
|
972
|
+
} else if (pid !== null) {
|
|
973
|
+
console.log("MongoDB was not running (stale PID file).");
|
|
974
|
+
} else {
|
|
975
|
+
console.log("MongoDB is not running.");
|
|
976
|
+
}
|
|
977
|
+
rmSync(pidFile, { force: true });
|
|
978
|
+
if (wipe) {
|
|
979
|
+
rmSync(dataRoot, { recursive: true, force: true });
|
|
980
|
+
console.log(\`Removed \${dataRoot}.\`);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const cmd = process.argv[2] ?? "up";
|
|
985
|
+
switch (cmd) {
|
|
986
|
+
case "up":
|
|
987
|
+
await up();
|
|
988
|
+
break;
|
|
989
|
+
case "down":
|
|
990
|
+
await down(false);
|
|
991
|
+
break;
|
|
992
|
+
case "reset":
|
|
993
|
+
await down(true);
|
|
994
|
+
break;
|
|
995
|
+
case "_run":
|
|
996
|
+
await runServer();
|
|
997
|
+
break;
|
|
998
|
+
default:
|
|
999
|
+
console.error(\`Unknown command: \${cmd}. Use: up | down | reset\`);
|
|
1000
|
+
process.exit(2);
|
|
1001
|
+
}
|
|
1002
|
+
`;
|
|
1003
|
+
function getMongoMemoryScripts(packageManager) {
|
|
1004
|
+
switch (packageManager) {
|
|
1005
|
+
case "bun": return {
|
|
1006
|
+
"db:up": "bun --env-file=.env scripts/mongo.mjs up",
|
|
1007
|
+
"db:down": "bun --env-file=.env scripts/mongo.mjs down",
|
|
1008
|
+
"db:reset": "bun --env-file=.env scripts/mongo.mjs reset"
|
|
1009
|
+
};
|
|
1010
|
+
case "deno": return {
|
|
1011
|
+
"db:up": "deno run -A --env-file=.env scripts/mongo.mjs up",
|
|
1012
|
+
"db:down": "deno run -A --env-file=.env scripts/mongo.mjs down",
|
|
1013
|
+
"db:reset": "deno run -A --env-file=.env scripts/mongo.mjs reset"
|
|
1014
|
+
};
|
|
1015
|
+
default: return {
|
|
1016
|
+
"db:up": "node --env-file=.env scripts/mongo.mjs up",
|
|
1017
|
+
"db:down": "node --env-file=.env scripts/mongo.mjs down",
|
|
1018
|
+
"db:reset": "node --env-file=.env scripts/mongo.mjs reset"
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
651
1022
|
const requiredPrismaFileGroups = [
|
|
652
|
-
["prisma/
|
|
653
|
-
["prisma
|
|
654
|
-
["prisma.config.ts", "packages/db/prisma.config.ts"],
|
|
1023
|
+
["prisma/contract.prisma", "prisma/contract.ts"],
|
|
1024
|
+
["prisma-next.config.ts"],
|
|
655
1025
|
[
|
|
656
1026
|
"src/lib/prisma.ts",
|
|
657
1027
|
"src/lib/prisma.server.ts",
|
|
658
1028
|
"src/lib/server/prisma.ts",
|
|
659
|
-
"server/utils/prisma.ts"
|
|
660
|
-
"packages/db/src/client.ts"
|
|
1029
|
+
"server/utils/prisma.ts"
|
|
661
1030
|
]
|
|
662
1031
|
];
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
if (await fs.pathExists(path.join(monorepoDbDir, "prisma/schema.prisma"))) return monorepoDbDir;
|
|
666
|
-
return projectDir;
|
|
1032
|
+
function getContractPath(authoring) {
|
|
1033
|
+
return `prisma/contract${authoring === "typescript" ? ".ts" : ".prisma"}`;
|
|
667
1034
|
}
|
|
668
1035
|
async function promptForDatabaseProvider() {
|
|
669
1036
|
const databaseProvider = await select({
|
|
670
1037
|
message: "Select your database",
|
|
671
1038
|
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
|
-
]
|
|
1039
|
+
options: [{
|
|
1040
|
+
value: "postgres",
|
|
1041
|
+
label: "PostgreSQL",
|
|
1042
|
+
hint: "Relational models with typed ORM, relations, indexes, raw SQL"
|
|
1043
|
+
}, {
|
|
1044
|
+
value: "mongo",
|
|
1045
|
+
label: "MongoDB",
|
|
1046
|
+
hint: "Document models with typed ORM, indexes, aggregations"
|
|
1047
|
+
}]
|
|
695
1048
|
});
|
|
696
1049
|
if (isCancel(databaseProvider)) {
|
|
697
1050
|
cancel("Operation cancelled.");
|
|
@@ -699,10 +1052,48 @@ async function promptForDatabaseProvider() {
|
|
|
699
1052
|
}
|
|
700
1053
|
return DatabaseProviderSchema.parse(databaseProvider);
|
|
701
1054
|
}
|
|
1055
|
+
async function promptForAuthoringStyle() {
|
|
1056
|
+
const authoring = await select({
|
|
1057
|
+
message: "Choose contract authoring style",
|
|
1058
|
+
initialValue: DEFAULT_AUTHORING,
|
|
1059
|
+
options: [{
|
|
1060
|
+
value: "psl",
|
|
1061
|
+
label: "PSL",
|
|
1062
|
+
hint: "Schema syntax emits contract.json + types"
|
|
1063
|
+
}, {
|
|
1064
|
+
value: "typescript",
|
|
1065
|
+
label: "TypeScript",
|
|
1066
|
+
hint: "Builder API emits the same contract artifacts"
|
|
1067
|
+
}]
|
|
1068
|
+
});
|
|
1069
|
+
if (isCancel(authoring)) {
|
|
1070
|
+
cancel("Operation cancelled.");
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
return AuthoringStyleSchema.parse(authoring);
|
|
1074
|
+
}
|
|
1075
|
+
async function promptForPrismaPostgres() {
|
|
1076
|
+
const shouldUsePrismaPostgres = await confirm({
|
|
1077
|
+
message: "Provision a Prisma Postgres database?",
|
|
1078
|
+
active: "Provision Prisma Postgres",
|
|
1079
|
+
inactive: "Use my own database",
|
|
1080
|
+
initialValue: DEFAULT_INTERACTIVE_PRISMA_POSTGRES
|
|
1081
|
+
});
|
|
1082
|
+
if (isCancel(shouldUsePrismaPostgres)) {
|
|
1083
|
+
cancel("Operation cancelled.");
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
return Boolean(shouldUsePrismaPostgres);
|
|
1087
|
+
}
|
|
702
1088
|
function getPackageManagerHint(option, detected) {
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
1089
|
+
const hint = {
|
|
1090
|
+
npm: "Node.js default",
|
|
1091
|
+
pnpm: "Fast, disk-efficient Node.js package manager",
|
|
1092
|
+
yarn: "Yarn package manager",
|
|
1093
|
+
bun: "Fast runtime + package manager",
|
|
1094
|
+
deno: "Deno runtime + task runner"
|
|
1095
|
+
}[option];
|
|
1096
|
+
return option === detected ? `Detected; ${hint}` : hint;
|
|
706
1097
|
}
|
|
707
1098
|
async function promptForPackageManager(detectedPackageManager) {
|
|
708
1099
|
const packageManager = await select({
|
|
@@ -744,7 +1135,9 @@ async function promptForPackageManager(detectedPackageManager) {
|
|
|
744
1135
|
}
|
|
745
1136
|
async function promptForDependencyInstall(packageManager) {
|
|
746
1137
|
const shouldInstall = await confirm({
|
|
747
|
-
message: `Install dependencies now with ${getInstallCommand(packageManager)}
|
|
1138
|
+
message: `Install dependencies now with ${getInstallCommand(packageManager)}? You can run it later.`,
|
|
1139
|
+
active: "Install now",
|
|
1140
|
+
inactive: "Skip for now",
|
|
748
1141
|
initialValue: true
|
|
749
1142
|
});
|
|
750
1143
|
if (isCancel(shouldInstall)) {
|
|
@@ -753,17 +1146,6 @@ async function promptForDependencyInstall(packageManager) {
|
|
|
753
1146
|
}
|
|
754
1147
|
return Boolean(shouldInstall);
|
|
755
1148
|
}
|
|
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
1149
|
function getCommandErrorMessage(error) {
|
|
768
1150
|
if (error instanceof Error && "stderr" in error) {
|
|
769
1151
|
const stderr = String(error.stderr ?? "").trim();
|
|
@@ -775,17 +1157,22 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
775
1157
|
const projectDir = path.resolve(options.projectDir ?? process.cwd());
|
|
776
1158
|
const useDefaults = input.yes === true;
|
|
777
1159
|
const verbose = input.verbose === true;
|
|
778
|
-
const
|
|
1160
|
+
const shouldEmit = input.emit ?? DEFAULT_EMIT;
|
|
779
1161
|
const databaseProvider = input.provider ?? (useDefaults ? DEFAULT_DATABASE_PROVIDER : await promptForDatabaseProvider());
|
|
780
1162
|
if (!databaseProvider) return;
|
|
781
|
-
const schemaPreset = input.schemaPreset ?? options.defaultSchemaPreset ?? DEFAULT_SCHEMA_PRESET$1;
|
|
782
1163
|
const databaseUrl = input.databaseUrl;
|
|
783
|
-
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
1164
|
+
const shouldUsePrismaPostgres = input.prismaPostgres ?? (databaseProvider === "postgres" && !databaseUrl && !useDefaults ? await promptForPrismaPostgres() : DEFAULT_AUTOMATED_PRISMA_POSTGRES);
|
|
1165
|
+
if (shouldUsePrismaPostgres === void 0) return;
|
|
1166
|
+
if (shouldUsePrismaPostgres && databaseProvider !== "postgres") {
|
|
1167
|
+
cancel("--prisma-postgres is only supported with --provider postgres.");
|
|
1168
|
+
return;
|
|
788
1169
|
}
|
|
1170
|
+
if (shouldUsePrismaPostgres && databaseUrl) {
|
|
1171
|
+
cancel("Use either --database-url or --prisma-postgres, not both.");
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const authoring = input.authoring ?? (useDefaults ? DEFAULT_AUTHORING : await promptForAuthoringStyle());
|
|
1175
|
+
if (!authoring) return;
|
|
789
1176
|
const detectedPackageManager = await detectPackageManager(projectDir);
|
|
790
1177
|
const packageManager = input.packageManager ?? (useDefaults ? detectedPackageManager : await promptForPackageManager(detectedPackageManager));
|
|
791
1178
|
if (!packageManager) return;
|
|
@@ -794,9 +1181,9 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
794
1181
|
return {
|
|
795
1182
|
projectDir,
|
|
796
1183
|
verbose,
|
|
797
|
-
|
|
1184
|
+
shouldEmit,
|
|
798
1185
|
databaseProvider,
|
|
799
|
-
|
|
1186
|
+
authoring,
|
|
800
1187
|
databaseUrl,
|
|
801
1188
|
shouldUsePrismaPostgres,
|
|
802
1189
|
packageManager,
|
|
@@ -805,14 +1192,11 @@ async function collectPrismaSetupContext(input, options = {}) {
|
|
|
805
1192
|
}
|
|
806
1193
|
function getDefaultDatabaseUrl(provider) {
|
|
807
1194
|
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;";
|
|
1195
|
+
case "postgres": return "postgresql://user:password@localhost:5432/mydb";
|
|
1196
|
+
case "mongo": return "mongodb://localhost:27017/mydb?replicaSet=rs0&directConnection=true";
|
|
813
1197
|
default: {
|
|
814
1198
|
const exhaustiveCheck = provider;
|
|
815
|
-
throw new Error(`Unsupported
|
|
1199
|
+
throw new Error(`Unsupported Prisma Next target: ${String(exhaustiveCheck)}`);
|
|
816
1200
|
}
|
|
817
1201
|
}
|
|
818
1202
|
}
|
|
@@ -882,6 +1266,48 @@ async function ensureGitignoreEntry(projectDir, entry) {
|
|
|
882
1266
|
const separator = existingContent.endsWith("\n") ? "" : "\n";
|
|
883
1267
|
await fs.appendFile(gitignorePath, `${separator}${entry}\n`, "utf8");
|
|
884
1268
|
}
|
|
1269
|
+
async function ensurePackageScripts(projectDir, scripts) {
|
|
1270
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
1271
|
+
if (!await fs.pathExists(packageJsonPath)) return;
|
|
1272
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1273
|
+
if (!packageJson.scripts) packageJson.scripts = {};
|
|
1274
|
+
let didChange = false;
|
|
1275
|
+
for (const [scriptName, command] of Object.entries(scripts)) if (typeof packageJson.scripts[scriptName] !== "string" || packageJson.scripts[scriptName].trim().length === 0) {
|
|
1276
|
+
packageJson.scripts[scriptName] = command;
|
|
1277
|
+
didChange = true;
|
|
1278
|
+
}
|
|
1279
|
+
if (didChange) await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1280
|
+
}
|
|
1281
|
+
async function ensureMongoMemoryServerScript(projectDir) {
|
|
1282
|
+
const scriptPath = path.join(projectDir, "scripts", "mongo.mjs");
|
|
1283
|
+
if (await fs.pathExists(scriptPath)) return;
|
|
1284
|
+
await fs.ensureDir(path.dirname(scriptPath));
|
|
1285
|
+
await fs.writeFile(scriptPath, MONGO_MEMORY_SERVER_SCRIPT, "utf8");
|
|
1286
|
+
}
|
|
1287
|
+
async function ensureMongoMemoryServerDevDependency(projectDir) {
|
|
1288
|
+
const packageJsonPath = path.join(projectDir, "package.json");
|
|
1289
|
+
if (!await fs.pathExists(packageJsonPath)) return;
|
|
1290
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
1291
|
+
if (!packageJson.devDependencies) packageJson.devDependencies = {};
|
|
1292
|
+
const memoryServerVersion = getDependencyVersion("mongodb-memory-server");
|
|
1293
|
+
if (packageJson.devDependencies["mongodb-memory-server"] === memoryServerVersion) return;
|
|
1294
|
+
packageJson.devDependencies["mongodb-memory-server"] = memoryServerVersion;
|
|
1295
|
+
packageJson.devDependencies = Object.fromEntries(Object.entries(packageJson.devDependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
1296
|
+
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
1297
|
+
}
|
|
1298
|
+
async function writeMongoLocalHelpersForContext(context, projectDir) {
|
|
1299
|
+
if (context.databaseProvider !== "mongo" || context.databaseUrl) return true;
|
|
1300
|
+
try {
|
|
1301
|
+
await ensureMongoMemoryServerScript(projectDir);
|
|
1302
|
+
await ensureMongoMemoryServerDevDependency(projectDir);
|
|
1303
|
+
await ensurePackageScripts(projectDir, getMongoMemoryScripts(context.packageManager));
|
|
1304
|
+
await ensureGitignoreEntry(projectDir, ".mongo-data");
|
|
1305
|
+
return true;
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
cancel(getCommandErrorMessage(error));
|
|
1308
|
+
return false;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
885
1311
|
async function ensureRequiredPrismaFiles(projectDir) {
|
|
886
1312
|
const missingFiles = [];
|
|
887
1313
|
for (const candidates of requiredPrismaFileGroups) {
|
|
@@ -895,41 +1321,37 @@ async function ensureRequiredPrismaFiles(projectDir) {
|
|
|
895
1321
|
}
|
|
896
1322
|
if (!foundCandidate) missingFiles.push(candidates.join(" or "));
|
|
897
1323
|
}
|
|
898
|
-
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma files: ${missingFiles.join(", ")}`);
|
|
1324
|
+
if (missingFiles.length > 0) throw new Error(`Template is missing required Prisma Next files: ${missingFiles.join(", ")}`);
|
|
899
1325
|
}
|
|
900
1326
|
async function finalizePrismaFiles(options) {
|
|
901
1327
|
const projectDir = options.projectDir ?? process.cwd();
|
|
902
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
903
1328
|
await ensureRequiredPrismaFiles(projectDir);
|
|
904
|
-
|
|
905
|
-
await ensureEnvVarInEnv(prismaProjectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
|
|
1329
|
+
await ensureEnvVarInEnv(projectDir, "DATABASE_URL", options.databaseUrl ?? getDefaultDatabaseUrl(options.provider), {
|
|
906
1330
|
mode: options.databaseUrl ? "upsert" : "keep-existing",
|
|
907
1331
|
comment: "Added by create-prisma"
|
|
908
1332
|
});
|
|
909
1333
|
if (options.claimUrl) {
|
|
910
|
-
await ensureEnvVarInEnv(
|
|
1334
|
+
await ensureEnvVarInEnv(projectDir, "CLAIM_URL", options.claimUrl, {
|
|
911
1335
|
mode: "upsert",
|
|
912
1336
|
comment: PRISMA_POSTGRES_TEMPORARY_NOTICE
|
|
913
1337
|
});
|
|
914
|
-
await ensureEnvComment(
|
|
1338
|
+
await ensureEnvComment(projectDir, PRISMA_POSTGRES_TEMPORARY_NOTICE);
|
|
915
1339
|
}
|
|
916
|
-
await ensureGitignoreEntry(
|
|
1340
|
+
await ensureGitignoreEntry(projectDir, ".env");
|
|
917
1341
|
}
|
|
918
1342
|
async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
919
1343
|
if (!context.shouldUsePrismaPostgres) return { databaseUrl: context.databaseUrl };
|
|
920
1344
|
const createDbCommand = getCreateDbCommand(context.packageManager);
|
|
921
|
-
|
|
922
|
-
prismaPostgresSpinner.start(`Provisioning Prisma Postgres with ${createDbCommand}...`);
|
|
1345
|
+
if (context.verbose) log.step(`Running ${createDbCommand}`);
|
|
923
1346
|
try {
|
|
924
1347
|
const prismaPostgresResult = await provisionPrismaPostgres(context.packageManager, projectDir);
|
|
925
|
-
|
|
1348
|
+
if (context.verbose) log.success("Prisma Postgres database provisioned.");
|
|
926
1349
|
return {
|
|
927
1350
|
databaseUrl: prismaPostgresResult.databaseUrl,
|
|
928
1351
|
claimUrl: prismaPostgresResult.claimUrl
|
|
929
1352
|
};
|
|
930
1353
|
} catch (error) {
|
|
931
1354
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
932
|
-
prismaPostgresSpinner.stop("Could not provision Prisma Postgres.");
|
|
933
1355
|
return {
|
|
934
1356
|
databaseUrl: context.databaseUrl,
|
|
935
1357
|
warning: `Prisma Postgres provisioning failed: ${errorMessage}`
|
|
@@ -937,44 +1359,86 @@ async function provisionPrismaPostgresIfNeeded(context, projectDir) {
|
|
|
937
1359
|
}
|
|
938
1360
|
}
|
|
939
1361
|
async function writeDependenciesForContext(context, projectDir) {
|
|
940
|
-
const prismaProjectDir = await resolvePrismaProjectDir(projectDir);
|
|
941
1362
|
try {
|
|
942
|
-
await writePrismaDependencies(context.databaseProvider, context.packageManager,
|
|
1363
|
+
await writePrismaDependencies(context.databaseProvider, context.packageManager, context.authoring, projectDir);
|
|
943
1364
|
return true;
|
|
944
1365
|
} catch (error) {
|
|
945
1366
|
cancel(getCommandErrorMessage(error));
|
|
946
1367
|
return false;
|
|
947
1368
|
}
|
|
948
1369
|
}
|
|
1370
|
+
function getPrismaNextCliPackageSpecifier() {
|
|
1371
|
+
return getPrismaNextPackageSpecifier("prisma-next");
|
|
1372
|
+
}
|
|
1373
|
+
function getPrismaNextInitTarget(provider) {
|
|
1374
|
+
return provider === "mongo" ? "mongodb" : "postgres";
|
|
1375
|
+
}
|
|
1376
|
+
function getPrismaNextInitCliArgs(packageManager, prismaNextArgs) {
|
|
1377
|
+
if (packageManager === "npm") return {
|
|
1378
|
+
command: "npx",
|
|
1379
|
+
args: [
|
|
1380
|
+
"--yes",
|
|
1381
|
+
getPrismaNextCliPackageSpecifier(),
|
|
1382
|
+
"init",
|
|
1383
|
+
...prismaNextArgs
|
|
1384
|
+
]
|
|
1385
|
+
};
|
|
1386
|
+
return getPackageExecutionArgs(packageManager, [
|
|
1387
|
+
getPrismaNextCliPackageSpecifier(),
|
|
1388
|
+
"init",
|
|
1389
|
+
...prismaNextArgs
|
|
1390
|
+
]);
|
|
1391
|
+
}
|
|
1392
|
+
function getPrismaNextInitCliCommand(packageManager, prismaNextArgs) {
|
|
1393
|
+
const execution = getPrismaNextInitCliArgs(packageManager, prismaNextArgs);
|
|
1394
|
+
return [execution.command, ...execution.args].join(" ");
|
|
1395
|
+
}
|
|
1396
|
+
async function runPrismaNextInitForContext(context, projectDir) {
|
|
1397
|
+
const initArgs = [
|
|
1398
|
+
"--yes",
|
|
1399
|
+
"--force",
|
|
1400
|
+
"--target",
|
|
1401
|
+
getPrismaNextInitTarget(context.databaseProvider),
|
|
1402
|
+
"--authoring",
|
|
1403
|
+
context.authoring,
|
|
1404
|
+
"--schema-path",
|
|
1405
|
+
getContractPath(context.authoring),
|
|
1406
|
+
"--no-install"
|
|
1407
|
+
];
|
|
1408
|
+
const initCommand = getPrismaNextInitCliCommand(context.packageManager, initArgs);
|
|
1409
|
+
if (context.verbose) log.step(`Running ${initCommand}`);
|
|
1410
|
+
try {
|
|
1411
|
+
const initExecution = getPrismaNextInitCliArgs(context.packageManager, initArgs);
|
|
1412
|
+
await execa(initExecution.command, initExecution.args, {
|
|
1413
|
+
cwd: projectDir,
|
|
1414
|
+
stdio: context.verbose ? "inherit" : "pipe",
|
|
1415
|
+
env: {
|
|
1416
|
+
...process.env,
|
|
1417
|
+
CI: "1"
|
|
1418
|
+
}
|
|
1419
|
+
});
|
|
1420
|
+
if (context.verbose) log.success("Prisma Next project files ready.");
|
|
1421
|
+
return true;
|
|
1422
|
+
} catch (error) {
|
|
1423
|
+
if (context.verbose) log.warn("Could not run Prisma Next init.");
|
|
1424
|
+
cancel(`Failed to run ${initCommand}: ${getCommandErrorMessage(error)}`);
|
|
1425
|
+
return false;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
949
1428
|
async function installDependenciesForContext(context, projectDir) {
|
|
950
1429
|
if (!context.shouldInstall) return true;
|
|
951
1430
|
const installCommand = getInstallCommand(context.packageManager);
|
|
952
|
-
if (context.verbose) {
|
|
953
|
-
log.step(`Running ${installCommand}`);
|
|
954
|
-
try {
|
|
955
|
-
await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
|
|
956
|
-
log.success("Dependencies installed.");
|
|
957
|
-
return true;
|
|
958
|
-
} catch (error) {
|
|
959
|
-
cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
|
|
960
|
-
return false;
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
const installSpinner = spinner();
|
|
964
|
-
installSpinner.start(`Running ${installCommand}...`);
|
|
1431
|
+
if (context.verbose) log.step(`Running ${installCommand}`);
|
|
965
1432
|
try {
|
|
966
1433
|
await installProjectDependencies(context.packageManager, projectDir, { verbose: context.verbose });
|
|
967
|
-
|
|
1434
|
+
if (context.verbose) log.success("Dependencies installed.");
|
|
968
1435
|
return true;
|
|
969
1436
|
} catch (error) {
|
|
970
|
-
installSpinner.stop("Could not install dependencies.");
|
|
971
1437
|
cancel(`Failed to run ${installCommand}: ${getCommandErrorMessage(error)}`);
|
|
972
1438
|
return false;
|
|
973
1439
|
}
|
|
974
1440
|
}
|
|
975
1441
|
async function finalizePrismaFilesForContext(context, projectDir, provisionResult) {
|
|
976
|
-
const initSpinner = spinner();
|
|
977
|
-
initSpinner.start("Preparing Prisma files...");
|
|
978
1442
|
try {
|
|
979
1443
|
await finalizePrismaFiles({
|
|
980
1444
|
provider: context.databaseProvider,
|
|
@@ -982,746 +1446,165 @@ async function finalizePrismaFilesForContext(context, projectDir, provisionResul
|
|
|
982
1446
|
claimUrl: provisionResult.claimUrl,
|
|
983
1447
|
projectDir
|
|
984
1448
|
});
|
|
985
|
-
|
|
1449
|
+
if (context.verbose) log.success("Prisma Next environment configured.");
|
|
986
1450
|
return true;
|
|
987
1451
|
} catch (error) {
|
|
988
|
-
initSpinner.stop("Could not prepare Prisma files.");
|
|
989
1452
|
cancel(getCommandErrorMessage(error));
|
|
990
1453
|
return false;
|
|
991
1454
|
}
|
|
992
1455
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1456
|
+
function getPrismaNextCliCommand(packageManager, prismaNextArgs) {
|
|
1457
|
+
if (packageManager === "deno") return `deno run -A --env-file=.env ${getDenoPrismaSpecifier()} ${prismaNextArgs.join(" ")}`;
|
|
1458
|
+
return getLocalPackageBinaryCommand(packageManager, "prisma-next", prismaNextArgs);
|
|
1459
|
+
}
|
|
1460
|
+
function getPrismaNextCliArgs(packageManager, prismaNextArgs) {
|
|
1461
|
+
if (packageManager === "deno") return {
|
|
1462
|
+
command: "deno",
|
|
1463
|
+
args: [
|
|
1464
|
+
"run",
|
|
1465
|
+
"-A",
|
|
1466
|
+
"--env-file=.env",
|
|
1467
|
+
getDenoPrismaSpecifier(),
|
|
1468
|
+
...prismaNextArgs
|
|
1469
|
+
]
|
|
1470
|
+
};
|
|
1471
|
+
return getLocalPackageBinaryArgs(packageManager, "prisma-next", prismaNextArgs);
|
|
1472
|
+
}
|
|
1473
|
+
async function emitPrismaNextContractForContext(context, projectDir) {
|
|
1474
|
+
if (!context.shouldEmit) return { didEmitContract: false };
|
|
1475
|
+
if (!context.shouldInstall) return {
|
|
1476
|
+
didEmitContract: false,
|
|
1477
|
+
warning: "Skipped contract emit because dependencies were not installed."
|
|
1478
|
+
};
|
|
1479
|
+
const emitCommand = getPrismaNextCliCommand(context.packageManager, ["contract", "emit"]);
|
|
1480
|
+
if (context.verbose) log.step(`Running ${emitCommand}`);
|
|
1000
1481
|
try {
|
|
1001
|
-
const
|
|
1002
|
-
await execa(
|
|
1003
|
-
cwd:
|
|
1482
|
+
const emitArgs = getPrismaNextCliArgs(context.packageManager, ["contract", "emit"]);
|
|
1483
|
+
await execa(emitArgs.command, emitArgs.args, {
|
|
1484
|
+
cwd: projectDir,
|
|
1004
1485
|
stdio: context.verbose ? "inherit" : "pipe"
|
|
1005
1486
|
});
|
|
1006
|
-
if (context.verbose) log.success("Prisma
|
|
1007
|
-
|
|
1008
|
-
return { didGenerateClient: true };
|
|
1487
|
+
if (context.verbose) log.success("Prisma Next contract artifacts emitted.");
|
|
1488
|
+
return { didEmitContract: true };
|
|
1009
1489
|
} catch (error) {
|
|
1010
|
-
if (context.verbose) log.warn("Could not
|
|
1011
|
-
else generateSpinner?.stop("Could not generate Prisma Client.");
|
|
1490
|
+
if (context.verbose) log.warn("Could not emit Prisma Next contract.");
|
|
1012
1491
|
return {
|
|
1013
|
-
|
|
1014
|
-
warning: `
|
|
1492
|
+
didEmitContract: false,
|
|
1493
|
+
warning: `Contract emit failed: ${getCommandErrorMessage(error)}`
|
|
1015
1494
|
};
|
|
1016
1495
|
}
|
|
1017
1496
|
}
|
|
1018
|
-
function buildWarningLines(provisionWarning,
|
|
1497
|
+
function buildWarningLines(provisionWarning, emitWarning) {
|
|
1019
1498
|
const warningLines = [];
|
|
1020
1499
|
if (provisionWarning) warningLines.push(`- ${provisionWarning}`);
|
|
1021
|
-
if (
|
|
1500
|
+
if (emitWarning) warningLines.push(`- ${emitWarning}`);
|
|
1022
1501
|
return warningLines;
|
|
1023
1502
|
}
|
|
1024
1503
|
function buildNextStepsForContext(opts) {
|
|
1025
|
-
const { context, options,
|
|
1504
|
+
const { context, options, didEmitContract } = opts;
|
|
1026
1505
|
const nextSteps = [...options.prependNextSteps ?? []];
|
|
1027
|
-
if (!context.shouldInstall) nextSteps.push(
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "db:seed")}`);
|
|
1031
|
-
if (options.includeDevNextStep) nextSteps.push(`- ${getRunScriptCommand(context.packageManager, "dev")}`);
|
|
1032
|
-
return nextSteps;
|
|
1033
|
-
}
|
|
1034
|
-
async function executePrismaSetupContext(context, options = {}) {
|
|
1035
|
-
const projectDir = path.resolve(options.projectDir ?? context.projectDir);
|
|
1036
|
-
const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
|
|
1037
|
-
if (!provisionResult) return false;
|
|
1038
|
-
if (!await writeDependenciesForContext(context, projectDir)) return false;
|
|
1039
|
-
if (!await installDependenciesForContext(context, projectDir)) return false;
|
|
1040
|
-
if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) return false;
|
|
1041
|
-
const generateResult = await generatePrismaClientForContext(context, projectDir);
|
|
1042
|
-
const warningLines = buildWarningLines(provisionResult.warning, generateResult.warning);
|
|
1043
|
-
const nextSteps = buildNextStepsForContext({
|
|
1044
|
-
context,
|
|
1045
|
-
options,
|
|
1046
|
-
didGenerateClient: generateResult.didGenerateClient
|
|
1506
|
+
if (!context.shouldInstall) nextSteps.push({
|
|
1507
|
+
command: getInstallCommand(context.packageManager),
|
|
1508
|
+
description: "Install the project dependencies."
|
|
1047
1509
|
});
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
Next
|
|
1051
|
-
${nextSteps.join("\n")}`);
|
|
1052
|
-
return true;
|
|
1053
|
-
}
|
|
1054
|
-
|
|
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
|
|
1510
|
+
if (!didEmitContract || !context.shouldEmit) nextSteps.push({
|
|
1511
|
+
command: getRunScriptCommand(context.packageManager, "contract:emit"),
|
|
1512
|
+
description: "Emit contract.json and TypeScript types from your Prisma Next contract."
|
|
1325
1513
|
});
|
|
1326
|
-
if (
|
|
1327
|
-
|
|
1328
|
-
|
|
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
|
-
}]
|
|
1514
|
+
if (context.databaseProvider === "postgres") nextSteps.push({
|
|
1515
|
+
command: getRunScriptCommand(context.packageManager, "db:init"),
|
|
1516
|
+
description: "Create the initial PostgreSQL database objects and sign the database."
|
|
1345
1517
|
});
|
|
1346
|
-
if (
|
|
1347
|
-
|
|
1348
|
-
|
|
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))
|
|
1518
|
+
if (context.databaseProvider === "mongo" && !context.databaseUrl) nextSteps.push({
|
|
1519
|
+
command: getRunScriptCommand(context.packageManager, "db:up"),
|
|
1520
|
+
description: "Start the local MongoDB replica set with mongodb-memory-server. Stop with `db:down`, wipe with `db:reset`."
|
|
1360
1521
|
});
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
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]
|
|
1522
|
+
nextSteps.push({
|
|
1523
|
+
command: getRunScriptCommand(context.packageManager, "migration:plan"),
|
|
1524
|
+
description: "Compare the contract to the database and write a migration plan."
|
|
1373
1525
|
});
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
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]
|
|
1526
|
+
nextSteps.push({
|
|
1527
|
+
command: getRunScriptCommand(context.packageManager, "migrate"),
|
|
1528
|
+
description: "Apply the planned migration to the database."
|
|
1386
1529
|
});
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
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
|
|
1530
|
+
nextSteps.push({
|
|
1531
|
+
command: getRunScriptCommand(context.packageManager, "db:seed"),
|
|
1532
|
+
description: "Insert the sample users from prisma/seed.ts."
|
|
1399
1533
|
});
|
|
1400
|
-
if (
|
|
1401
|
-
|
|
1402
|
-
|
|
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
|
-
}
|
|
1534
|
+
if (options.includeDevNextStep) nextSteps.push({
|
|
1535
|
+
command: getRunScriptCommand(context.packageManager, "dev"),
|
|
1536
|
+
description: "Start the development server."
|
|
1441
1537
|
});
|
|
1538
|
+
return nextSteps;
|
|
1442
1539
|
}
|
|
1443
|
-
|
|
1444
|
-
|
|
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
|
-
}
|
|
1540
|
+
function formatNextSteps(nextSteps) {
|
|
1541
|
+
return nextSteps.map((step) => `${step.command}\n ${step.description}`).join("\n\n");
|
|
1496
1542
|
}
|
|
1497
|
-
function
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
}
|
|
1543
|
+
function formatAgentPrompt() {
|
|
1544
|
+
return [
|
|
1545
|
+
"Ask your agent:",
|
|
1546
|
+
"What can I do with Prisma Next?",
|
|
1547
|
+
"",
|
|
1548
|
+
"Learn more:",
|
|
1549
|
+
`Docs: prisma-next.md`,
|
|
1550
|
+
"Skills: https://github.com/prisma/prisma-next/tree/main/skills"
|
|
1551
|
+
].join("\n");
|
|
1507
1552
|
}
|
|
1508
|
-
async function
|
|
1509
|
-
|
|
1510
|
-
const
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
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
|
-
}
|
|
1553
|
+
async function executePrismaSetupContext(context, options = {}) {
|
|
1554
|
+
const projectDir = path.resolve(options.projectDir ?? context.projectDir);
|
|
1555
|
+
const progressSpinner = context.verbose ? void 0 : options.progressSpinner ?? spinner();
|
|
1556
|
+
if (progressSpinner !== void 0 && !options.progressSpinner) progressSpinner.start("Creating Prisma Next project...");
|
|
1557
|
+
const stopProgressOnFailure = () => {
|
|
1558
|
+
progressSpinner?.stop("Could not create Prisma Next project.");
|
|
1559
|
+
};
|
|
1560
|
+
if (context.shouldUsePrismaPostgres) progressSpinner?.message("Provisioning Prisma Postgres...");
|
|
1561
|
+
const provisionResult = await provisionPrismaPostgresIfNeeded(context, projectDir);
|
|
1562
|
+
if (!provisionResult) {
|
|
1563
|
+
stopProgressOnFailure();
|
|
1564
|
+
return false;
|
|
1538
1565
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
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);
|
|
1566
|
+
progressSpinner?.message("Preparing Prisma Next project files...");
|
|
1567
|
+
if (!await runPrismaNextInitForContext(context, projectDir)) {
|
|
1568
|
+
stopProgressOnFailure();
|
|
1569
|
+
return false;
|
|
1556
1570
|
}
|
|
1557
|
-
if (context
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
projectDir,
|
|
1561
|
-
scope: context.scope,
|
|
1562
|
-
agents: context.mcpAgents,
|
|
1563
|
-
verbose
|
|
1564
|
-
});
|
|
1565
|
-
if (warning) warnings.push(warning);
|
|
1571
|
+
if (!await writeDependenciesForContext(context, projectDir)) {
|
|
1572
|
+
stopProgressOnFailure();
|
|
1573
|
+
return false;
|
|
1566
1574
|
}
|
|
1567
|
-
if (context
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
verbose,
|
|
1571
|
-
targets: context.extensionTargets
|
|
1572
|
-
});
|
|
1573
|
-
warnings.push(...extensionWarnings);
|
|
1575
|
+
if (!await writeMongoLocalHelpersForContext(context, projectDir)) {
|
|
1576
|
+
stopProgressOnFailure();
|
|
1577
|
+
return false;
|
|
1574
1578
|
}
|
|
1575
|
-
if (
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
return;
|
|
1579
|
+
if (context.shouldInstall) progressSpinner?.message("Installing dependencies...");
|
|
1580
|
+
if (!await installDependenciesForContext(context, projectDir)) {
|
|
1581
|
+
stopProgressOnFailure();
|
|
1582
|
+
return false;
|
|
1579
1583
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
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(() => {});
|
|
1584
|
+
progressSpinner?.message("Configuring Prisma Next...");
|
|
1585
|
+
if (!await finalizePrismaFilesForContext(context, projectDir, provisionResult)) {
|
|
1586
|
+
stopProgressOnFailure();
|
|
1587
|
+
return false;
|
|
1654
1588
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
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)
|
|
1589
|
+
if (context.shouldEmit && context.shouldInstall) progressSpinner?.message("Emitting Prisma Next contract artifacts...");
|
|
1590
|
+
const emitResult = await emitPrismaNextContractForContext(context, projectDir);
|
|
1591
|
+
const warningLines = buildWarningLines(provisionResult.warning, emitResult.warning);
|
|
1592
|
+
const nextSteps = buildNextStepsForContext({
|
|
1593
|
+
context,
|
|
1594
|
+
options,
|
|
1595
|
+
didEmitContract: emitResult.didEmitContract
|
|
1719
1596
|
});
|
|
1597
|
+
progressSpinner?.stop("Prisma Next project ready.");
|
|
1598
|
+
if (warningLines.length > 0) note(warningLines.map((line) => line.replace(/^- /, "")).join("\n"), "Heads up");
|
|
1599
|
+
note(formatAgentPrompt(), "Agent prompt");
|
|
1600
|
+
if (context.verbose) note(formatNextSteps(nextSteps), "Next steps for Prisma Next");
|
|
1601
|
+
outro("Prisma Next setup complete.");
|
|
1602
|
+
return true;
|
|
1720
1603
|
}
|
|
1721
1604
|
|
|
1722
1605
|
//#endregion
|
|
1723
1606
|
//#region src/ui/branding.ts
|
|
1724
|
-
const prismaTitle = `${styleText(["bold", "
|
|
1607
|
+
const prismaTitle = `${styleText(["bold", "cyanBright"], "◭")} ${styleText(["bold", "cyanBright"], "Create")} ${styleText(["bold", "magentaBright"], "Prisma")} ${styleText(["bold", "blueBright"], "Next")}`;
|
|
1725
1608
|
function getCreatePrismaIntro() {
|
|
1726
1609
|
return prismaTitle;
|
|
1727
1610
|
}
|
|
@@ -1729,8 +1612,7 @@ function getCreatePrismaIntro() {
|
|
|
1729
1612
|
//#endregion
|
|
1730
1613
|
//#region src/commands/create.ts
|
|
1731
1614
|
const DEFAULT_PROJECT_NAME = "my-app";
|
|
1732
|
-
const DEFAULT_TEMPLATE = "
|
|
1733
|
-
const DEFAULT_SCHEMA_PRESET = "basic";
|
|
1615
|
+
const DEFAULT_TEMPLATE = "minimal";
|
|
1734
1616
|
function toPackageName(projectName) {
|
|
1735
1617
|
return projectName.toLowerCase().replace(/[^a-z0-9._-]/g, "-").replace(/^-+/, "").replace(/-+$/, "") || "app";
|
|
1736
1618
|
}
|
|
@@ -1761,50 +1643,50 @@ async function promptForCreateTemplate() {
|
|
|
1761
1643
|
message: "Select template",
|
|
1762
1644
|
initialValue: DEFAULT_TEMPLATE,
|
|
1763
1645
|
options: [
|
|
1646
|
+
{
|
|
1647
|
+
value: "minimal",
|
|
1648
|
+
label: "Minimal",
|
|
1649
|
+
hint: "Script-first Prisma Next starter with no web framework"
|
|
1650
|
+
},
|
|
1764
1651
|
{
|
|
1765
1652
|
value: "hono",
|
|
1766
1653
|
label: "Hono",
|
|
1767
|
-
hint: "TypeScript API
|
|
1654
|
+
hint: "Lightweight TypeScript API server"
|
|
1768
1655
|
},
|
|
1769
1656
|
{
|
|
1770
1657
|
value: "elysia",
|
|
1771
1658
|
label: "Elysia",
|
|
1772
|
-
hint: "TypeScript API
|
|
1659
|
+
hint: "Bun-friendly TypeScript API server"
|
|
1773
1660
|
},
|
|
1774
1661
|
{
|
|
1775
1662
|
value: "nest",
|
|
1776
1663
|
label: "NestJS",
|
|
1777
|
-
hint: "
|
|
1664
|
+
hint: "Structured Node API with controllers and services"
|
|
1778
1665
|
},
|
|
1779
1666
|
{
|
|
1780
1667
|
value: "next",
|
|
1781
1668
|
label: "Next.js",
|
|
1782
|
-
hint: "
|
|
1669
|
+
hint: "Full-stack React app with App Router"
|
|
1783
1670
|
},
|
|
1784
1671
|
{
|
|
1785
1672
|
value: "svelte",
|
|
1786
1673
|
label: "SvelteKit",
|
|
1787
|
-
hint: "
|
|
1674
|
+
hint: "Full-stack Svelte 5 app with Vite"
|
|
1788
1675
|
},
|
|
1789
1676
|
{
|
|
1790
1677
|
value: "astro",
|
|
1791
1678
|
label: "Astro",
|
|
1792
|
-
hint: "
|
|
1679
|
+
hint: "Content-oriented web app with server routes"
|
|
1793
1680
|
},
|
|
1794
1681
|
{
|
|
1795
1682
|
value: "nuxt",
|
|
1796
1683
|
label: "Nuxt",
|
|
1797
|
-
hint: "
|
|
1684
|
+
hint: "Full-stack Vue app with Nitro server routes"
|
|
1798
1685
|
},
|
|
1799
1686
|
{
|
|
1800
1687
|
value: "tanstack-start",
|
|
1801
1688
|
label: "TanStack Start",
|
|
1802
|
-
hint: "
|
|
1803
|
-
},
|
|
1804
|
-
{
|
|
1805
|
-
value: "turborepo",
|
|
1806
|
-
label: "Turborepo",
|
|
1807
|
-
hint: "Monorepo starter with apps + packages/db Prisma package"
|
|
1689
|
+
hint: "React app with file routes and server functions"
|
|
1808
1690
|
}
|
|
1809
1691
|
]
|
|
1810
1692
|
});
|
|
@@ -1872,8 +1754,8 @@ async function runCreateCommand(rawInput = {}) {
|
|
|
1872
1754
|
}
|
|
1873
1755
|
}
|
|
1874
1756
|
async function collectCreateContext(input) {
|
|
1875
|
-
const useDefaults = input.yes === true;
|
|
1876
1757
|
const force = input.force === true;
|
|
1758
|
+
const useDefaults = input.yes === true;
|
|
1877
1759
|
const projectNameInput = input.name ?? (useDefaults ? DEFAULT_PROJECT_NAME : await promptForProjectName());
|
|
1878
1760
|
if (projectNameInput === void 0) return;
|
|
1879
1761
|
const projectName = String(projectNameInput).trim();
|
|
@@ -1894,42 +1776,33 @@ async function collectCreateContext(input) {
|
|
|
1894
1776
|
cancel(`Target directory ${formatPathForDisplay(targetDirectory)} is not empty. Use --force to continue.`);
|
|
1895
1777
|
return;
|
|
1896
1778
|
}
|
|
1897
|
-
const prismaSetupContext = await collectPrismaSetupContext(input, {
|
|
1898
|
-
projectDir: targetDirectory,
|
|
1899
|
-
defaultSchemaPreset: DEFAULT_SCHEMA_PRESET
|
|
1900
|
-
});
|
|
1779
|
+
const prismaSetupContext = await collectPrismaSetupContext(input, { projectDir: targetDirectory });
|
|
1901
1780
|
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
1781
|
return {
|
|
1909
1782
|
targetDirectory,
|
|
1910
1783
|
targetPathState,
|
|
1911
1784
|
force,
|
|
1912
1785
|
template,
|
|
1913
1786
|
projectPackageName: toPackageName(path.basename(targetDirectory)),
|
|
1914
|
-
prismaSetupContext
|
|
1915
|
-
addonSetupContext: addonSetupContext ?? void 0
|
|
1787
|
+
prismaSetupContext
|
|
1916
1788
|
};
|
|
1917
1789
|
}
|
|
1918
1790
|
async function executeCreateContext(context) {
|
|
1919
|
-
const
|
|
1920
|
-
|
|
1791
|
+
const createSpinner = context.prismaSetupContext.verbose ? void 0 : spinner();
|
|
1792
|
+
createSpinner?.start("Creating Prisma Next project...");
|
|
1921
1793
|
try {
|
|
1794
|
+
if (context.prismaSetupContext.verbose) log.step(`Scaffolding ${context.template} starter.`);
|
|
1922
1795
|
await scaffoldCreateTemplate({
|
|
1923
1796
|
projectDir: context.targetDirectory,
|
|
1924
1797
|
projectName: context.projectPackageName,
|
|
1925
1798
|
template: context.template,
|
|
1926
|
-
schemaPreset: context.prismaSetupContext.schemaPreset,
|
|
1927
1799
|
provider: context.prismaSetupContext.databaseProvider,
|
|
1800
|
+
authoring: context.prismaSetupContext.authoring,
|
|
1928
1801
|
packageManager: context.prismaSetupContext.packageManager
|
|
1929
1802
|
});
|
|
1930
|
-
|
|
1803
|
+
if (context.prismaSetupContext.verbose) log.success("Starter files scaffolded.");
|
|
1931
1804
|
} catch (error) {
|
|
1932
|
-
|
|
1805
|
+
createSpinner?.stop("Could not create Prisma Next project.");
|
|
1933
1806
|
return {
|
|
1934
1807
|
ok: false,
|
|
1935
1808
|
stage: "scaffold_template",
|
|
@@ -1943,6 +1816,7 @@ async function executeCreateContext(context) {
|
|
|
1943
1816
|
projectDir: context.targetDirectory
|
|
1944
1817
|
});
|
|
1945
1818
|
} catch (error) {
|
|
1819
|
+
createSpinner?.stop("Could not create Prisma Next project.");
|
|
1946
1820
|
return {
|
|
1947
1821
|
ok: false,
|
|
1948
1822
|
stage: "scaffold_template",
|
|
@@ -1950,31 +1824,22 @@ async function executeCreateContext(context) {
|
|
|
1950
1824
|
};
|
|
1951
1825
|
}
|
|
1952
1826
|
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
|
-
}
|
|
1827
|
+
const nextSteps = formatPathForDisplay(context.targetDirectory) === "." ? [] : [{
|
|
1828
|
+
command: `cd ${formatPathForDisplay(context.targetDirectory)}`,
|
|
1829
|
+
description: "Enter your new project directory."
|
|
1830
|
+
}];
|
|
1968
1831
|
try {
|
|
1969
1832
|
if (!await executePrismaSetupContext(context.prismaSetupContext, {
|
|
1970
1833
|
prependNextSteps: nextSteps,
|
|
1971
1834
|
projectDir: context.targetDirectory,
|
|
1972
|
-
includeDevNextStep: true
|
|
1835
|
+
includeDevNextStep: true,
|
|
1836
|
+
progressSpinner: createSpinner
|
|
1973
1837
|
})) return {
|
|
1974
1838
|
ok: false,
|
|
1975
1839
|
stage: "prisma_setup"
|
|
1976
1840
|
};
|
|
1977
1841
|
} catch (error) {
|
|
1842
|
+
createSpinner?.stop("Could not create Prisma Next project.");
|
|
1978
1843
|
return {
|
|
1979
1844
|
ok: false,
|
|
1980
1845
|
stage: "prisma_setup",
|
|
@@ -1985,4 +1850,4 @@ async function executeCreateContext(context) {
|
|
|
1985
1850
|
}
|
|
1986
1851
|
|
|
1987
1852
|
//#endregion
|
|
1988
|
-
export {
|
|
1853
|
+
export { DatabaseProviderSchema as a, CreateTemplateSchema as i, AuthoringStyleSchema as n, DatabaseUrlSchema as o, CreateCommandInputSchema as r, PackageManagerSchema as s, runCreateCommand as t };
|