@prisma/cli 3.0.0-dev.60.1 → 3.0.0-dev.61.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.
@@ -32,6 +32,10 @@ async function maybeSetupBranchDatabase(context, provider, projectId, branch, op
32
32
  warnings: warning ? [warning] : []
33
33
  };
34
34
  }
35
+ if (localSignal.unsupportedSchema) {
36
+ if (options.db === true) throw unsupportedBranchDatabaseSchemaError(localSignal.unsupportedSchema, branch.name, context);
37
+ return emptyBranchDatabaseSetupOutcome();
38
+ }
35
39
  const hasSignal = hasBranchDatabaseSignal(localSignal) || Boolean(envState.previewDatabaseUrl);
36
40
  if (options.db !== true) {
37
41
  if (!hasSignal) return emptyBranchDatabaseSetupOutcome();
@@ -76,10 +80,10 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
76
80
  databaseUrl: database.databaseUrl,
77
81
  directUrl: database.directUrl
78
82
  }).catch((error) => {
79
- throw schemaSetupFailedError(error, signal.schema, branch.name);
83
+ throw schemaSetupFailedError(error, signal.schema, branch.name, context.runtime.cwd);
80
84
  });
81
85
  emitBranchDatabaseProgress(context, "success", "Applied database schema");
82
- } else skippedSchemaWarning = "No schema.prisma file was found. Branch database env vars were created, but schema setup was skipped.";
86
+ } else skippedSchemaWarning = "No supported Prisma schema source was found. Branch database env vars were created, but schema setup was skipped.";
83
87
  const envVars = await upsertBranchDatabaseEnvVars(context, provider, projectId, branch, database, envState);
84
88
  emitBranchDatabaseProgress(context, "success", `Added branch env override${envVars.length === 1 ? "" : "s"} ${envVars.join(", ")}`);
85
89
  if (skippedSchemaWarning) {
@@ -96,6 +100,7 @@ async function setupBranchDatabase(context, provider, projectId, branch, signal,
96
100
  envVars,
97
101
  schema: schemaSetup ? {
98
102
  command: schemaSetup.command,
103
+ source: schemaSetup.source,
99
104
  path: schemaSetup.schemaPath
100
105
  } : null
101
106
  },
@@ -185,7 +190,7 @@ function hasInlineDatabaseEnvVars(envVars) {
185
190
  function maybeRenderBranchDatabaseSignal(context, branchName, signal, envState) {
186
191
  if (context.flags.json || context.flags.quiet) return;
187
192
  const rows = [
188
- signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) || "schema.prisma"}` : null,
193
+ signal.schema ? ` Schema ${path.relative(context.runtime.cwd, signal.schema.path) || defaultSchemaSourcePath(signal.schema)}` : null,
189
194
  signal.databaseUrlReferences.length > 0 ? ` Code ${signal.databaseUrlReferences.slice(0, 3).join(", ")}` : null,
190
195
  envState.previewDatabaseUrl ? " Env preview DATABASE_URL is inherited by this branch" : null
191
196
  ].filter((row) => Boolean(row));
@@ -207,7 +212,11 @@ function emptyBranchDatabaseSetupOutcome() {
207
212
  };
208
213
  }
209
214
  function formatSchemaSetupCommand(command) {
210
- return command === "migrate-deploy" ? "prisma migrate deploy" : "prisma db push";
215
+ switch (command) {
216
+ case "migrate-deploy": return "prisma migrate deploy";
217
+ case "db-push": return "prisma db push";
218
+ case "prisma-next-db-init": return "prisma-next db init";
219
+ }
211
220
  }
212
221
  function branchDatabaseSetupFailedError(summary, error, branchName) {
213
222
  return new CliError({
@@ -257,7 +266,7 @@ function branchDatabaseCleanupFailedError(setupError, cleanupError, database, br
257
266
  nextSteps: []
258
267
  });
259
268
  }
260
- function schemaSetupFailedError(error, schema, branchName) {
269
+ function schemaSetupFailedError(error, schema, branchName, cwd) {
261
270
  return new CliError({
262
271
  code: "SCHEMA_SETUP_FAILED",
263
272
  domain: "app",
@@ -268,12 +277,39 @@ function schemaSetupFailedError(error, schema, branchName) {
268
277
  meta: {
269
278
  branch: branchName,
270
279
  schemaPath: schema.path,
280
+ source: schema.kind,
271
281
  command: schema.command
272
282
  },
273
283
  exitCode: 1,
274
- nextSteps: [schema.command === "migrate-deploy" ? "npx --no-install prisma migrate deploy" : "npx --no-install prisma db push --skip-generate", `prisma-cli app deploy --branch ${formatCommandArgument(branchName)} --db`]
284
+ nextSteps: [...formatSchemaSetupNextSteps(schema, cwd), `prisma-cli app deploy --branch ${formatCommandArgument(branchName)} --db`]
275
285
  });
276
286
  }
287
+ function unsupportedBranchDatabaseSchemaError(schema, branchName, context) {
288
+ return usageError("Branch database setup is not available for this Prisma schema", `${path.relative(context.runtime.cwd, schema.path) || defaultUnsupportedSchemaSourcePath(schema)} targets ${formatUnsupportedSchemaTarget(schema.target)}, but --db creates Prisma Postgres databases.`, "Use project env commands to provide a database URL for this branch, or switch the Prisma schema source to PostgreSQL before using --db.", [`prisma-cli project env add DATABASE_URL=<value> --branch ${formatCommandArgument(branchName)}`, `prisma-cli app deploy --branch ${formatCommandArgument(branchName)}`], "app");
289
+ }
290
+ function formatSchemaSetupNextSteps(schema, cwd) {
291
+ const sourcePath = path.relative(cwd, schema.path) || defaultSchemaSourcePath(schema);
292
+ switch (schema.command) {
293
+ case "migrate-deploy": return [`npx --no-install prisma migrate deploy --schema ${formatCommandArgument(sourcePath)}`];
294
+ case "db-push": return [`npx --no-install prisma db push --schema ${formatCommandArgument(sourcePath)}`];
295
+ case "prisma-next-db-init": return [`npx --no-install prisma-next contract emit --config ${formatCommandArgument(sourcePath)}`, `npx --no-install prisma-next db init --config ${formatCommandArgument(sourcePath)} --db <DATABASE_URL>`];
296
+ }
297
+ }
298
+ function defaultSchemaSourcePath(schema) {
299
+ return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
300
+ }
301
+ function defaultUnsupportedSchemaSourcePath(schema) {
302
+ return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
303
+ }
304
+ function formatUnsupportedSchemaTarget(target) {
305
+ switch (target) {
306
+ case "cockroachdb": return "CockroachDB";
307
+ case "mongodb": return "MongoDB";
308
+ case "mysql": return "MySQL";
309
+ case "sqlite": return "SQLite";
310
+ case "sqlserver": return "SQL Server";
311
+ }
312
+ }
277
313
  function formatDebugDetails(error) {
278
314
  if (error instanceof Error) return error.stack ?? error.message;
279
315
  return typeof error === "string" ? error : null;
@@ -10,6 +10,7 @@ const SKIPPED_DIRECTORIES = new Set([
10
10
  ".prisma",
11
11
  ".turbo",
12
12
  ".vercel",
13
+ ".wrangler",
13
14
  "build",
14
15
  "coverage",
15
16
  "dist",
@@ -37,29 +38,41 @@ async function inspectBranchDatabaseSignal(cwd, signal) {
37
38
  const state = {
38
39
  filesVisited: 0,
39
40
  schemaCandidates: [],
41
+ prismaNextConfigCandidates: [],
40
42
  databaseUrlReferences: []
41
43
  };
42
44
  await scanDirectory(cwd, cwd, 0, state, signal);
43
- const schemaPath = selectSchemaPath(cwd, state.schemaCandidates);
44
- const hasMigrations = schemaPath ? await hasMigrationsDirectory(path.dirname(schemaPath), signal) : false;
45
+ const prismaNextConfigs = await Promise.all(state.prismaNextConfigCandidates.map((configPath) => classifyPrismaNextConfig(configPath, signal)));
46
+ const supportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "supported");
47
+ const unsupportedPrismaNextConfig = selectPrismaNextConfig(cwd, prismaNextConfigs, "unsupported");
48
+ const selectedPrismaOrmSchema = await selectPrismaOrmSchema(cwd, state.schemaCandidates, signal);
49
+ const schema = supportedPrismaNextConfig ? {
50
+ kind: "prisma-next",
51
+ path: supportedPrismaNextConfig.path,
52
+ hasMigrations: false,
53
+ command: "prisma-next-db-init",
54
+ target: supportedPrismaNextConfig.target
55
+ } : selectedPrismaOrmSchema.schema;
45
56
  return {
46
- schema: schemaPath ? {
47
- path: schemaPath,
48
- hasMigrations,
49
- command: hasMigrations ? "migrate-deploy" : "db-push"
50
- } : null,
57
+ schema,
58
+ unsupportedSchema: schema ? null : unsupportedPrismaNextConfig ? {
59
+ kind: "prisma-next",
60
+ path: unsupportedPrismaNextConfig.path,
61
+ target: unsupportedPrismaNextConfig.target
62
+ } : selectedPrismaOrmSchema.unsupportedSchema,
51
63
  databaseUrlReferences: state.databaseUrlReferences
52
64
  };
53
65
  }
54
66
  function hasBranchDatabaseSignal(signal) {
67
+ if (signal.unsupportedSchema) return false;
55
68
  return Boolean(signal.schema || signal.databaseUrlReferences.length > 0);
56
69
  }
57
70
  async function runBranchDatabaseSchemaSetup(options) {
58
- const schemaPath = path.relative(options.context.runtime.cwd, options.schema.path) || "schema.prisma";
59
- const args = buildPrismaSchemaCommandArgs(options.schema.command, schemaPath);
60
- await runPrismaCommand({
71
+ const schemaPath = path.relative(options.context.runtime.cwd, options.schema.path) || defaultSchemaSourcePath(options.schema);
72
+ const commands = buildSchemaSetupCommands(options.schema, schemaPath, options.databaseUrl);
73
+ for (const command of commands) await runPrismaCommand({
61
74
  context: options.context,
62
- args,
75
+ ...command,
63
76
  env: {
64
77
  DATABASE_URL: options.databaseUrl,
65
78
  ...options.directUrl ? { DIRECT_URL: options.directUrl } : {}
@@ -67,6 +80,7 @@ async function runBranchDatabaseSchemaSetup(options) {
67
80
  });
68
81
  return {
69
82
  command: options.schema.command,
83
+ source: options.schema.kind,
70
84
  schemaPath
71
85
  };
72
86
  }
@@ -92,18 +106,57 @@ async function scanDirectory(cwd, directory, depth, state, signal) {
92
106
  if (!entry.isFile()) continue;
93
107
  state.filesVisited += 1;
94
108
  if (entry.name === "schema.prisma") state.schemaCandidates.push(entryPath);
109
+ if (isPrismaNextConfigFile(entry.name)) state.prismaNextConfigCandidates.push(entryPath);
95
110
  if (state.databaseUrlReferences.length < MAX_DATABASE_URL_REFERENCE_FILES && shouldScanForDatabaseUrl(entry.name) && await fileContainsDatabaseUrl(entryPath, signal)) state.databaseUrlReferences.push(path.relative(cwd, entryPath) || entry.name);
96
111
  }
97
112
  }
98
- function selectSchemaPath(cwd, candidates) {
113
+ async function selectPrismaOrmSchema(cwd, candidates, signal) {
114
+ const sorted = sortByPreferredRelativePath(cwd, candidates, "schema.prisma");
115
+ for (const schemaPath of sorted) {
116
+ const target = await classifyPrismaOrmSchemaTarget(schemaPath, signal);
117
+ if (target === "postgresql" || target === "unknown") {
118
+ const hasMigrations = await hasMigrationsDirectory(path.dirname(schemaPath), signal);
119
+ return {
120
+ schema: {
121
+ kind: "prisma-orm",
122
+ path: schemaPath,
123
+ hasMigrations,
124
+ command: hasMigrations ? "migrate-deploy" : "db-push",
125
+ target
126
+ },
127
+ unsupportedSchema: null
128
+ };
129
+ }
130
+ return {
131
+ schema: null,
132
+ unsupportedSchema: {
133
+ kind: "prisma-orm",
134
+ path: schemaPath,
135
+ target
136
+ }
137
+ };
138
+ }
139
+ return {
140
+ schema: null,
141
+ unsupportedSchema: null
142
+ };
143
+ }
144
+ function selectPrismaNextConfig(cwd, candidates, mode) {
145
+ const matches = candidates.filter((candidate) => {
146
+ const isSupported = candidate.target === "postgresql" || candidate.target === "unknown";
147
+ return mode === "supported" ? isSupported : !isSupported;
148
+ });
149
+ return sortByPreferredRelativePath(cwd, matches.map((candidate) => candidate.path), "prisma-next.config.ts").map((candidatePath) => matches.find((candidate) => candidate.path === candidatePath)).find((candidate) => Boolean(candidate)) ?? null;
150
+ }
151
+ function sortByPreferredRelativePath(cwd, candidates, preferredRootFile) {
99
152
  return candidates.map((candidate) => ({
100
153
  absolute: candidate,
101
- relative: path.relative(cwd, candidate) || "schema.prisma"
154
+ relative: path.relative(cwd, candidate) || preferredRootFile
102
155
  })).sort((left, right) => {
103
- if (left.relative === "schema.prisma") return -1;
104
- if (right.relative === "schema.prisma") return 1;
156
+ if (left.relative === preferredRootFile) return -1;
157
+ if (right.relative === preferredRootFile) return 1;
105
158
  return left.relative.length - right.relative.length || left.relative.localeCompare(right.relative);
106
- })[0]?.absolute ?? null;
159
+ }).map((candidate) => candidate.absolute);
107
160
  }
108
161
  async function hasMigrationsDirectory(schemaDirectory, signal) {
109
162
  signal.throwIfAborted();
@@ -116,36 +169,115 @@ async function hasMigrationsDirectory(schemaDirectory, signal) {
116
169
  throw error;
117
170
  }
118
171
  }
172
+ async function classifyPrismaNextConfig(configPath, signal) {
173
+ const content = await readTextFileIfSmall(configPath, signal);
174
+ if (!content) return {
175
+ path: configPath,
176
+ target: "unknown"
177
+ };
178
+ if (content.includes("@prisma-next/postgres/config")) return {
179
+ path: configPath,
180
+ target: "postgresql"
181
+ };
182
+ if (content.includes("@prisma-next/mongo/config")) return {
183
+ path: configPath,
184
+ target: "mongodb"
185
+ };
186
+ if (content.includes("@prisma-next/sqlite/config")) return {
187
+ path: configPath,
188
+ target: "sqlite"
189
+ };
190
+ return {
191
+ path: configPath,
192
+ target: "unknown"
193
+ };
194
+ }
195
+ async function classifyPrismaOrmSchemaTarget(schemaPath, signal) {
196
+ switch ((await readTextFileIfSmall(schemaPath, signal))?.match(/\bprovider\s*=\s*"([^"]+)"/)?.[1] ?? null) {
197
+ case "postgresql": return "postgresql";
198
+ case "mongodb": return "mongodb";
199
+ case "mysql": return "mysql";
200
+ case "sqlite": return "sqlite";
201
+ case "sqlserver": return "sqlserver";
202
+ case "cockroachdb": return "cockroachdb";
203
+ default: return "unknown";
204
+ }
205
+ }
119
206
  function shouldScanForDatabaseUrl(fileName) {
120
207
  if (fileName === ".env" || fileName.startsWith(".env.")) return true;
121
208
  return DATABASE_URL_SCAN_EXTENSIONS.has(path.extname(fileName));
122
209
  }
210
+ function isPrismaNextConfigFile(fileName) {
211
+ if (!fileName.startsWith("prisma-next.config.")) return false;
212
+ return [
213
+ ".cjs",
214
+ ".cts",
215
+ ".js",
216
+ ".mjs",
217
+ ".mts",
218
+ ".ts"
219
+ ].some((extension) => fileName.endsWith(extension));
220
+ }
123
221
  async function fileContainsDatabaseUrl(filePath, signal) {
222
+ return (await readTextFileIfSmall(filePath, signal))?.includes("DATABASE_URL") ?? false;
223
+ }
224
+ async function readTextFileIfSmall(filePath, signal) {
124
225
  signal.throwIfAborted();
125
- if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return false;
126
- return (await readFile(filePath, {
226
+ if ((await stat(filePath)).size > MAX_TEXT_FILE_BYTES) return null;
227
+ return readFile(filePath, {
127
228
  encoding: "utf8",
128
229
  signal
129
- })).includes("DATABASE_URL");
130
- }
131
- function buildPrismaSchemaCommandArgs(command, schemaPath) {
132
- if (command === "migrate-deploy") return [
133
- "--no-install",
134
- "prisma",
135
- "migrate",
136
- "deploy",
137
- "--schema",
138
- schemaPath
139
- ];
140
- return [
141
- "--no-install",
142
- "prisma",
143
- "db",
144
- "push",
145
- "--skip-generate",
146
- "--schema",
147
- schemaPath
148
- ];
230
+ });
231
+ }
232
+ function buildSchemaSetupCommands(schema, schemaPath, databaseUrl) {
233
+ if (schema.command === "migrate-deploy") return [{
234
+ args: [
235
+ "--no-install",
236
+ "prisma",
237
+ "migrate",
238
+ "deploy",
239
+ "--schema",
240
+ schemaPath
241
+ ],
242
+ displayCommand: "npx --no-install prisma migrate deploy"
243
+ }];
244
+ if (schema.command === "db-push") return [{
245
+ args: [
246
+ "--no-install",
247
+ "prisma",
248
+ "db",
249
+ "push",
250
+ "--schema",
251
+ schemaPath
252
+ ],
253
+ displayCommand: "npx --no-install prisma db push"
254
+ }];
255
+ return [{
256
+ args: [
257
+ "--no-install",
258
+ "prisma-next",
259
+ "contract",
260
+ "emit",
261
+ "--config",
262
+ schemaPath
263
+ ],
264
+ displayCommand: "npx --no-install prisma-next contract emit"
265
+ }, {
266
+ args: [
267
+ "--no-install",
268
+ "prisma-next",
269
+ "db",
270
+ "init",
271
+ "--config",
272
+ schemaPath,
273
+ "--db",
274
+ databaseUrl
275
+ ],
276
+ displayCommand: "npx --no-install prisma-next db init"
277
+ }];
278
+ }
279
+ function defaultSchemaSourcePath(schema) {
280
+ return schema.kind === "prisma-next" ? "prisma-next.config.ts" : "schema.prisma";
149
281
  }
150
282
  async function runPrismaCommand(options) {
151
283
  const shouldPipeOutput = !options.context.flags.json && !options.context.flags.quiet;
@@ -177,8 +309,8 @@ async function runPrismaCommand(options) {
177
309
  signal
178
310
  }));
179
311
  });
180
- if (exit.signal) throw new Error(`npx prisma was terminated by ${exit.signal}.`);
181
- if (exit.code !== 0) throw new Error(`npx prisma exited with code ${exit.code ?? 1}.`);
312
+ if (exit.signal) throw new Error(`${options.displayCommand} was terminated by ${exit.signal}.`);
313
+ if (exit.code !== 0) throw new Error(`${options.displayCommand} exited with code ${exit.code ?? 1}.`);
182
314
  }
183
315
  //#endregion
184
316
  export { hasBranchDatabaseSignal, inspectBranchDatabaseSignal, runBranchDatabaseSchemaSetup };
@@ -55,10 +55,17 @@ function renderBranchDatabaseDeploySummary(context, result) {
55
55
  },
56
56
  ...result.branchDatabase.schema ? [{
57
57
  label: "Schema",
58
- value: result.branchDatabase.schema.command === "migrate-deploy" ? "prisma migrate deploy" : "prisma db push"
58
+ value: formatBranchDatabaseSchemaCommand(result.branchDatabase.schema.command)
59
59
  }] : []
60
60
  ])];
61
61
  }
62
+ function formatBranchDatabaseSchemaCommand(command) {
63
+ switch (command) {
64
+ case "migrate-deploy": return "prisma migrate deploy";
65
+ case "db-push": return "prisma db push";
66
+ case "prisma-next-db-init": return "prisma-next db init";
67
+ }
68
+ }
62
69
  function formatDuration(durationMs) {
63
70
  if (durationMs < 1e3) return `${durationMs}ms`;
64
71
  return `${(durationMs / 1e3).toFixed(1)}s`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisma/cli",
3
- "version": "3.0.0-dev.60.1",
3
+ "version": "3.0.0-dev.61.1",
4
4
  "description": "Command-line interface for the Prisma Developer Platform.",
5
5
  "type": "module",
6
6
  "bin": {