Skip to content

Commit ed03287

Browse files
authored
feat(config): allow undefined urls in e.g. prisma generate (#28895)
This PR: - fixes [TML-1673](https://linear.app/prisma-company/issue/TML-1673/prismaconfigts-do-not-throw-validation-errors-if-env-variable-isnt) - adds a new e2e test, `prisma-config-generate-doesnt-need-valid-url` - updates the `import { env } from 'prisma/config'`'s error message - before: `"Missing required environment variable: UNDEFINED_VARIABLE"` - after: `"Cannot resolve environment variable: UNDEFINED_VARIABLE."` After this PR: - `prisma generate` works when the `datasource.url` value in the Prisma config file is undefined: ```ts // prisma.config.ts import { defineConfig } from 'prisma/config' export default defineConfig({ datasource: { url: process.env['UNDEFINED_VARIABLE'], }, }) ``` - `prisma generate` still fails when the `datasource.url` value in the Prisma config file uses the `env` utility with an undefined variable: ```ts // prisma.config.ts import { defineConfig, env } from 'prisma/config' export default defineConfig({ datasource: { url: env('UNDEFINED_VARIABLE'), }, }) ``` The error looks like: `Failed to load config file "[path]/prisma.config.ts" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: UNDEFINED_VARIABLE`. - `prisma db push` still fails when the `datasource.url` value in the Prisma config file is undefined. The error looks like: `Error: The datasource.url property is required in your Prisma config file when using prisma db push.`. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Datasource URL is now optional in Prisma configuration files, allowing prisma generate to succeed without a valid database URL. * **Bug Fixes** * Enhanced error messages to specify exactly which configuration property is required (datasource.url instead of datasource). * Improved environment variable resolution error messages for clarity when variables cannot be resolved. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: jkomyno <[email protected]>
1 parent f80a3d3 commit ed03287

File tree

27 files changed

+1293
-32
lines changed

27 files changed

+1293
-32
lines changed

packages/cli/src/__tests__/commands/Generate.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe('prisma.config.ts', () => {
2121

2222
const result = Generate.new().parse(['--sql'], await ctx.config())
2323
await expect(result).rejects.toThrowErrorMatchingInlineSnapshot(
24-
`"The datasource property is required in your Prisma config file when using prisma generate --sql."`,
24+
`"The datasource.url property is required in your Prisma config file when using prisma generate --sql."`,
2525
)
2626
})
2727
})

packages/cli/src/__tests__/incomplete-schemas.test.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ describe('[wasm] incomplete-schemas', () => {
109109
await Validate.new().parse([], await ctx.config())
110110
} catch (e) {
111111
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
112-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
112+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
113113
)
114114
}
115115
})
@@ -120,7 +120,7 @@ describe('[wasm] incomplete-schemas', () => {
120120
await DbPush.new().parse([], await ctx.config(), ctx.configDir())
121121
} catch (e) {
122122
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
123-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
123+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
124124
)
125125
}
126126
})
@@ -131,7 +131,7 @@ describe('[wasm] incomplete-schemas', () => {
131131
await DbPull.new().parse([], await ctx.config(), ctx.configDir())
132132
} catch (e) {
133133
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
134-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
134+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
135135
)
136136
}
137137
})
@@ -145,7 +145,7 @@ describe('[wasm] incomplete-schemas', () => {
145145
await DbExecute.new().parse(['--file=./script.sql'], config, ctx.configDir())
146146
} catch (e) {
147147
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
148-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
148+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
149149
)
150150
}
151151
})
@@ -156,7 +156,7 @@ describe('[wasm] incomplete-schemas', () => {
156156
await MigrateReset.new().parse([], await ctx.config(), ctx.configDir())
157157
} catch (e) {
158158
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
159-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
159+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
160160
)
161161
}
162162
})
@@ -167,7 +167,7 @@ describe('[wasm] incomplete-schemas', () => {
167167
await MigrateDev.new().parse([], await ctx.config(), ctx.configDir())
168168
} catch (e) {
169169
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
170-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
170+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
171171
)
172172
}
173173
})
@@ -178,7 +178,7 @@ describe('[wasm] incomplete-schemas', () => {
178178
await Validate.new().parse([], await ctx.config())
179179
} catch (e) {
180180
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
181-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
181+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
182182
)
183183
}
184184
})
@@ -190,7 +190,7 @@ describe('[wasm] incomplete-schemas', () => {
190190
await Format.new().parse([], await ctx.config())
191191
} catch (e) {
192192
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
193-
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Missing required environment variable: SOME_UNDEFINED_DB"`,
193+
`"Failed to load config file "/tmp/dir" as a TypeScript/JavaScript module. Error: PrismaConfigEnvError: Cannot resolve environment variable: SOME_UNDEFINED_DB."`,
194194
)
195195
}
196196
})
@@ -221,7 +221,7 @@ describe('[wasm] incomplete-schemas', () => {
221221
await DbPush.new().parse([], await ctx.config(), ctx.configDir())
222222
} catch (e) {
223223
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
224-
`"The datasource property is required in your Prisma config file when using prisma db push."`,
224+
`"The datasource.url property is required in your Prisma config file when using prisma db push."`,
225225
)
226226
}
227227
})
@@ -232,7 +232,7 @@ describe('[wasm] incomplete-schemas', () => {
232232
await DbPull.new().parse([], await ctx.config(), ctx.configDir())
233233
} catch (e) {
234234
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
235-
`"The datasource property is required in your Prisma config file when using prisma db pull."`,
235+
`"The datasource.url property is required in your Prisma config file when using prisma db pull."`,
236236
)
237237
}
238238
})
@@ -245,7 +245,7 @@ describe('[wasm] incomplete-schemas', () => {
245245
await DbExecute.new().parse(['--file=./script.sql'], await ctx.config(), ctx.configDir())
246246
} catch (e) {
247247
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
248-
`"The datasource property is required in your Prisma config file when using prisma db execute."`,
248+
`"The datasource.url property is required in your Prisma config file when using prisma db execute."`,
249249
)
250250
}
251251
})
@@ -256,7 +256,7 @@ describe('[wasm] incomplete-schemas', () => {
256256
await MigrateReset.new().parse([], await ctx.config(), ctx.configDir())
257257
} catch (e) {
258258
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
259-
`"The datasource property is required in your Prisma config file when using prisma migrate reset."`,
259+
`"The datasource.url property is required in your Prisma config file when using prisma migrate reset."`,
260260
)
261261
}
262262
})
@@ -267,7 +267,7 @@ describe('[wasm] incomplete-schemas', () => {
267267
await MigrateDev.new().parse([], await ctx.config(), ctx.configDir())
268268
} catch (e) {
269269
expect(stripVTControlCharacters(e.message)).toMatchInlineSnapshot(
270-
`"The datasource property is required in your Prisma config file when using prisma migrate dev."`,
270+
`"The datasource.url property is required in your Prisma config file when using prisma migrate dev."`,
271271
)
272272
}
273273
})

packages/cli/src/utils/loadConfig.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { type ConfigDiagnostic, loadConfigFromFile, type PrismaConfigInternal } from '@prisma/config'
1+
import {
2+
type ConfigDiagnostic,
3+
loadConfigFromFile,
4+
PrismaConfigEnvError,
5+
type PrismaConfigInternal,
6+
} from '@prisma/config'
27
import { Debug } from '@prisma/debug'
38
import { assertNever, HelpError } from '@prisma/internals'
49

@@ -17,14 +22,27 @@ export async function loadConfig(
1722
switch (error._tag) {
1823
case 'ConfigFileNotFound':
1924
return new HelpError(`Config file not found at "${resolvedPath}"`)
25+
2026
case 'ConfigLoadError':
27+
if (error.error instanceof PrismaConfigEnvError) {
28+
diagnostics.push({
29+
_tag: 'warn',
30+
value: (formatters) => () => {
31+
formatters.log(formatters.dim(`${error.error.message}`))
32+
},
33+
})
34+
}
35+
2136
return new HelpError(
2237
`Failed to load config file "${resolvedPath}" as a TypeScript/JavaScript module. Error: ${error.error}`,
2338
)
39+
2440
case 'ConfigFileSyntaxError':
2541
return new HelpError(`Failed to parse syntax of config file at "${resolvedPath}"`)
42+
2643
case 'UnknownError':
2744
return new HelpError(`Unknown error during config file loading: ${error.error}`)
45+
2846
default:
2947
assertNever(error, `Unhandled error '${JSON.stringify(error)}' in 'loadConfigFromFile'.`)
3048
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Readme
2+
3+
This is testing that `prisma generate` succeeds even when `datasource.url` is not provided in the Prisma config file.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { $ } from 'zx'
2+
3+
import { executeSteps } from '../_utils/executeSteps'
4+
5+
class ExpectedError extends Error {}
6+
7+
void executeSteps({
8+
setup: async () => {
9+
await $`pnpm install`
10+
},
11+
test: async () => {
12+
// using `process.env['UNDEFINED_VARIABLE']` in `config.datasource.url` + `prisma generate` should succeed
13+
await $`pnpm prisma generate --config ./src/prisma.config.process-env.ts`
14+
15+
// using `process.env['UNDEFINED_VARIABLE']` helper in `config.datasource.url` + `prisma db push` should fail
16+
try {
17+
await $`pnpm prisma db push --config ./src/prisma.config.process-env.ts`
18+
throw new ExpectedError('The command should have failed but it succeeded.')
19+
} catch (e: any) {
20+
console.error(e)
21+
if (e instanceof ExpectedError) {
22+
throw e
23+
}
24+
}
25+
26+
// using `env('UNDEFINED_VARIABLE')` helper in `config.datasource.url` should fail
27+
try {
28+
await $`pnpm prisma generate --config ./src/prisma.config.using-env-helper.ts`
29+
throw new ExpectedError('The command should have failed but it succeeded.')
30+
} catch (e: any) {
31+
console.error(e)
32+
if (e instanceof ExpectedError) {
33+
throw e
34+
}
35+
}
36+
},
37+
finish: async () => {
38+
await $`echo "done"`
39+
},
40+
// keep: true, // keep docker open to debug it
41+
})
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"private": true,
3+
"version": "0.0.0",
4+
"main": "index.js",
5+
"scripts": {},
6+
"dependencies": {
7+
"@prisma/client": "/tmp/prisma-client-0.0.0.tgz"
8+
},
9+
"devDependencies": {
10+
"@prisma/config": "/tmp/prisma-config-0.0.0.tgz",
11+
"@types/jest": "29.5.12",
12+
"@types/node": "~20.19.0",
13+
"prisma": "/tmp/prisma-0.0.0.tgz",
14+
"typescript": "5.7.3"
15+
}
16+
}

0 commit comments

Comments
 (0)