From 424228845751a519bedef93347a246913d9f85dc Mon Sep 17 00:00:00 2001 From: Jack Allen Date: Sun, 1 Jun 2025 23:14:52 +0100 Subject: [PATCH] Support Prisma's official multi-file schema structure --- .changeset/unlucky-ghosts-do.md | 5 + docs/config/extensions/prismaExtension.mdx | 36 +++++-- packages/build/src/extensions/prisma.ts | 53 +++++---- pnpm-lock.yaml | 101 ++++++++++-------- references/v3-catalog/package.json | 4 +- .../prisma/{schema => models}/post.prisma | 0 .../prisma/{schema => models}/user.prisma | 0 .../prisma/{schema => }/schema.prisma | 2 +- references/v3-catalog/trigger.config.ts | 2 +- 9 files changed, 115 insertions(+), 88 deletions(-) create mode 100644 .changeset/unlucky-ghosts-do.md rename references/v3-catalog/prisma/{schema => models}/post.prisma (100%) rename references/v3-catalog/prisma/{schema => models}/user.prisma (100%) rename references/v3-catalog/prisma/{schema => }/schema.prisma (91%) diff --git a/.changeset/unlucky-ghosts-do.md b/.changeset/unlucky-ghosts-do.md new file mode 100644 index 0000000000..38982f7e1e --- /dev/null +++ b/.changeset/unlucky-ghosts-do.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/build": minor +--- + +Support Prisma's official multi-file schema structure diff --git a/docs/config/extensions/prismaExtension.mdx b/docs/config/extensions/prismaExtension.mdx index 2d057cc264..eebc0e285c 100644 --- a/docs/config/extensions/prismaExtension.mdx +++ b/docs/config/extensions/prismaExtension.mdx @@ -10,8 +10,7 @@ If you are using Prisma, you should use the prisma build extension. - Generates the Prisma client during the deploy process - Optionally will migrate the database during the deploy process - Support for TypedSQL and multiple schema files -- You can use `prismaSchemaFolder` to specify just the directory containing your schema file, instead of the full path -- You can add the extension twice if you have multiple separate schemas in the same project (example below) +- You can set the schema to be a folder to use Prisma's multi-file schema feature (example [below](#multiple-schemas)) You can use it for a simple Prisma setup like this: @@ -141,17 +140,32 @@ These environment variables are only used during the build process and are not e ### Multiple schemas -If you have multiple separate schemas in the same project you can add the extension multiple times: +Prisma supports splitting your schema into multiple files. To use this, set `schema` to be the folder that contains your root schema (e.g. `schema.prisma`). -```ts -prismaExtension({ - schema: 'prisma/schema/main.prisma', - version: '6.2.0', - migrate: false, -}), +For example, if your root schema is located at `./prisma/schema.prisma`, you would set the `schema` option to `prisma`: + + + +```ts trigger.config.ts prismaExtension({ - schema: 'prisma/schema/secondary.prisma', - version: '6.2.0', + schema: 'prisma', + version: '6.7.0', migrate: false, }), ``` + +```shell Example structure +./prisma +├── migrations +├── models +│ ├── posts.prisma +│ ├── users.prisma +│ └── ... other `.prisma` files +└── schema.prisma +``` + + + + + To use this feature you must be using `prisma@6.7.0` or higher. Their official documentation can be found [here](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema). + diff --git a/packages/build/src/extensions/prisma.ts b/packages/build/src/extensions/prisma.ts index b67adc7efc..967d1301b2 100644 --- a/packages/build/src/extensions/prisma.ts +++ b/packages/build/src/extensions/prisma.ts @@ -1,6 +1,7 @@ import { BuildManifest, BuildTarget } from "@trigger.dev/core/v3"; import { binaryForRuntime, BuildContext, BuildExtension } from "@trigger.dev/core/v3/build"; import assert from "node:assert"; +import { glob } from 'tinyglobby'; import { existsSync } from "node:fs"; import { cp, readdir } from "node:fs/promises"; import { dirname, join, resolve } from "node:path"; @@ -112,11 +113,18 @@ export class PrismaExtension implements BuildExtension { context.logger.debug(`PrismaExtension is generating the Prisma client for version ${version}`); - const usingSchemaFolder = dirname(this._resolvedSchemaPath).endsWith("schema"); + // Multi-file schemas can be used by specifying a directory instead of a file. https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema + const usingSchemaFolder = !this._resolvedSchemaPath.endsWith(".prisma"); + + context.logger.debug(`Using schema folder: ${usingSchemaFolder}`); const commands: string[] = []; - let prismaDir: string | undefined; + const prismaSourceDir = usingSchemaFolder + ? this._resolvedSchemaPath + : dirname(this._resolvedSchemaPath); + + const prismaDestinationDir = join(manifest.outputPath, "prisma"); const generatorFlags: string[] = []; @@ -127,14 +135,10 @@ export class PrismaExtension implements BuildExtension { if (this.options.typedSql) { generatorFlags.push(`--sql`); - const prismaDir = usingSchemaFolder - ? dirname(dirname(this._resolvedSchemaPath)) - : dirname(this._resolvedSchemaPath); - context.logger.debug(`Using typedSql`); // Find all the files prisma/sql/*.sql - const sqlFiles = await readdir(join(prismaDir, "sql")).then((files) => + const sqlFiles = await readdir(join(prismaSourceDir, "sql")).then((files) => files.filter((file) => file.endsWith(".sql")) ); @@ -142,11 +146,11 @@ export class PrismaExtension implements BuildExtension { sqlFiles, }); - const sqlDestinationPath = join(manifest.outputPath, "prisma", "sql"); + const sqlDestinationPath = join(prismaDestinationDir, "sql"); for (const file of sqlFiles) { const destination = join(sqlDestinationPath, file); - const source = join(prismaDir, "sql", file); + const source = join(prismaSourceDir, "sql", file); context.logger.debug(`Copying the sql from ${source} to ${destination}`); @@ -155,28 +159,20 @@ export class PrismaExtension implements BuildExtension { } if (usingSchemaFolder) { - const schemaDir = dirname(this._resolvedSchemaPath); - - prismaDir = dirname(schemaDir); - - context.logger.debug(`Using the schema folder: ${schemaDir}`); + context.logger.debug(`Using the schema folder: ${this._resolvedSchemaPath}`); // Find all the files in schemaDir that end with .prisma (excluding the schema.prisma file) - const prismaFiles = await readdir(schemaDir).then((files) => - files.filter((file) => file.endsWith(".prisma")) - ); + const prismaFiles = await glob(["**/*.prisma"], { + cwd: this._resolvedSchemaPath + }) context.logger.debug(`Found prisma files in the schema folder`, { prismaFiles, }); - const schemaDestinationPath = join(manifest.outputPath, "prisma", "schema"); - - const allPrismaFiles = [...prismaFiles]; - - for (const file of allPrismaFiles) { - const destination = join(schemaDestinationPath, file); - const source = join(schemaDir, file); + for (const file of prismaFiles) { + const destination = join(prismaDestinationDir, file); + const source = join(this._resolvedSchemaPath, file); context.logger.debug(`Copying the prisma schema from ${source} to ${destination}`); @@ -186,15 +182,14 @@ export class PrismaExtension implements BuildExtension { commands.push( `${binaryForRuntime( manifest.runtime - )} node_modules/prisma/build/index.js generate ${generatorFlags.join(" ")}` // Don't add the --schema flag or this will fail + )} node_modules/prisma/build/index.js generate --schema ./prisma ${generatorFlags.join(" ")}` ); } else { - prismaDir = dirname(this._resolvedSchemaPath); // Now we need to add a layer that: // Copies the prisma schema to the build outputPath // Adds the `prisma` CLI dependency to the dependencies // Adds the `prisma generate` command, which generates the Prisma client - const schemaDestinationPath = join(manifest.outputPath, "prisma", "schema.prisma"); + const schemaDestinationPath = join(prismaDestinationDir, "schema.prisma"); // Copy the prisma schema to the build output path context.logger.debug( `Copying the prisma schema from ${this._resolvedSchemaPath} to ${schemaDestinationPath}` @@ -215,8 +210,8 @@ export class PrismaExtension implements BuildExtension { if (this.options.migrate) { // Copy the migrations directory to the build output path - const migrationsDir = join(prismaDir, "migrations"); - const migrationsDestinationPath = join(manifest.outputPath, "prisma", "migrations"); + const migrationsDir = join(prismaSourceDir, "migrations"); + const migrationsDestinationPath = join(prismaDestinationDir, "migrations"); context.logger.debug( `Copying the prisma migrations from ${migrationsDir} to ${migrationsDestinationPath}` diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6decddb309..c4d728df8d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2375,8 +2375,8 @@ importers: specifier: 1.4.1 version: 1.4.1 '@prisma/client': - specifier: 5.19.0 - version: 5.19.0(prisma@5.19.0) + specifier: 6.8.2 + version: 6.8.2(prisma@6.8.2)(typescript@5.5.4) '@react-email/components': specifier: 0.0.24 version: 0.0.24(react-dom@18.3.1)(react@19.0.0-rc.0) @@ -2568,8 +2568,8 @@ importers: specifier: ^0.19.11 version: 0.19.11 prisma: - specifier: 5.19.0 - version: 5.19.0 + specifier: 6.8.2 + version: 6.8.2(typescript@5.5.4) prisma-kysely: specifier: ^1.8.0 version: 1.8.0 @@ -11431,8 +11431,8 @@ packages: resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false - /@prisma/client@5.19.0(prisma@5.19.0): - resolution: {integrity: sha512-CzOpau+q1kEWQyoQMvlnXIHqPvwmWbh48xZ4n8KWbAql0p8PC0BIgSTYW5ncxXa4JSEff0tcoxSZB874wDstdg==} + /@prisma/client@5.4.1(prisma@5.4.1): + resolution: {integrity: sha512-xyD0DJ3gRNfLbPsC+YfMBBuLJtZKQfy1OD2qU/PZg+HKrr7SO+09174LMeTlWP0YF2wca9LxtVd4HnAiB5ketQ==} engines: {node: '>=16.13'} requiresBuild: true peerDependencies: @@ -11441,25 +11441,31 @@ packages: prisma: optional: true dependencies: - prisma: 5.19.0 + '@prisma/engines-version': 5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f + prisma: 5.4.1 dev: false - /@prisma/client@5.4.1(prisma@5.4.1): - resolution: {integrity: sha512-xyD0DJ3gRNfLbPsC+YfMBBuLJtZKQfy1OD2qU/PZg+HKrr7SO+09174LMeTlWP0YF2wca9LxtVd4HnAiB5ketQ==} - engines: {node: '>=16.13'} + /@prisma/client@6.8.2(prisma@6.8.2)(typescript@5.5.4): + resolution: {integrity: sha512-5II+vbyzv4si6Yunwgkj0qT/iY0zyspttoDrL3R4BYgLdp42/d2C8xdi9vqkrYtKt9H32oFIukvyw3Koz5JoDg==} + engines: {node: '>=18.18'} requiresBuild: true peerDependencies: prisma: '*' + typescript: '>=5.1.0' peerDependenciesMeta: prisma: optional: true + typescript: + optional: true dependencies: - '@prisma/engines-version': 5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f - prisma: 5.4.1 + prisma: 6.8.2(typescript@5.5.4) + typescript: 5.5.4 dev: false - /@prisma/debug@5.19.0: - resolution: {integrity: sha512-+b/G0ubAZlrS+JSiDhXnYV5DF/aTJ3pinktkiV/L4TtLRLZO6SVGyFELgxBsicCTWJ2ZMu5vEV/jTtYCdjFTRA==} + /@prisma/config@6.8.2: + resolution: {integrity: sha512-ZJY1fF4qRBPdLQ/60wxNtX+eu89c3AkYEcP7L3jkp0IPXCNphCYxikTg55kPJLDOG6P0X+QG5tCv6CmsBRZWFQ==} + dependencies: + jiti: 2.4.2 /@prisma/debug@5.3.1: resolution: {integrity: sha512-eYrxqslEKf+wpMFIIHgbcNYuZBXUdiJLA85Or3TwOhgPIN1ZoXT9CwJph3ynW8H1Xg0LkdYLwVmuULCwiMoU5A==} @@ -11471,21 +11477,15 @@ packages: - supports-color dev: true - /@prisma/engines-version@5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f: - resolution: {integrity: sha512-GimI9aZIFy/yvvR11KfXRn3pliFn1QAkdebVlsXlnoh5uk0YhLblVmeYiHfsu+wDA7BeKqYT4sFfzg8mutzuWw==} + /@prisma/debug@6.8.2: + resolution: {integrity: sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg==} /@prisma/engines-version@5.4.1-1.2f302df92bd8945e20ad4595a73def5b96afa54f: resolution: {integrity: sha512-+nUQM/y8C+1GG5Ioeqcu6itFslCfxvQSAUVSMC9XM2G2Fcq0F4Afnp6m0pXF6X6iUBWen7jZBPmM9Qlq4Nr3/A==} dev: false - /@prisma/engines@5.19.0: - resolution: {integrity: sha512-UtW+0m4HYoRSSR3LoDGKF3Ud4BSMWYlLEt4slTnuP1mI+vrV3zaDoiAPmejdAT76vCN5UqnWURbkXxf66nSylQ==} - requiresBuild: true - dependencies: - '@prisma/debug': 5.19.0 - '@prisma/engines-version': 5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f - '@prisma/fetch-engine': 5.19.0 - '@prisma/get-platform': 5.19.0 + /@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e: + resolution: {integrity: sha512-Rkik9lMyHpFNGaLpPF3H5q5TQTkm/aE7DsGM5m92FZTvWQsvmi6Va8On3pWvqLHOt5aPUvFb/FeZTmphI4CPiQ==} /@prisma/engines@5.3.1: resolution: {integrity: sha512-6QkILNyfeeN67BNEPEtkgh3Xo2tm6D7V+UhrkBbRHqKw9CTaz/vvTP/ROwYSP/3JT2MtIutZm/EnhxUiuOPVDA==} @@ -11496,12 +11496,14 @@ packages: resolution: {integrity: sha512-vJTdY4la/5V3N7SFvWRmSMUh4mIQnyb/MNoDjzVbh9iLmEC+uEykj/1GPviVsorvfz7DbYSQC4RiwmlEpTEvGA==} requiresBuild: true - /@prisma/fetch-engine@5.19.0: - resolution: {integrity: sha512-oOiPNtmJX0cP/ebu7BBEouJvCw8T84/MFD/Hf2zlqjxkK4ojl38bB9i9J5LAxotL6WlYVThKdxc7HqoWnPOhqQ==} + /@prisma/engines@6.8.2: + resolution: {integrity: sha512-XqAJ//LXjqYRQ1RRabs79KOY4+v6gZOGzbcwDQl0D6n9WBKjV7qdrbd042CwSK0v0lM9MSHsbcFnU2Yn7z8Zlw==} + requiresBuild: true dependencies: - '@prisma/debug': 5.19.0 - '@prisma/engines-version': 5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f - '@prisma/get-platform': 5.19.0 + '@prisma/debug': 6.8.2 + '@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e + '@prisma/fetch-engine': 6.8.2 + '@prisma/get-platform': 6.8.2 /@prisma/fetch-engine@5.3.1: resolution: {integrity: sha512-w1yk1YiK8N82Pobdq58b85l6e8akyrkxuzwV9DoiUTRf3gpsuhJJesHc4Yi0WzUC9/3znizl1UfCsI6dhkj3Vw==} @@ -11528,6 +11530,13 @@ packages: - supports-color dev: true + /@prisma/fetch-engine@6.8.2: + resolution: {integrity: sha512-lCvikWOgaLOfqXGacEKSNeenvj0n3qR5QvZUOmPE2e1Eh8cMYSobxonCg9rqM6FSdTfbpqp9xwhSAOYfNqSW0g==} + dependencies: + '@prisma/debug': 6.8.2 + '@prisma/engines-version': 6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e + '@prisma/get-platform': 6.8.2 + /@prisma/generator-helper@5.3.1: resolution: {integrity: sha512-zrYS0iHLgPlOJjYnd5KvVMMvSS+ktOL39EwooS5EnyvfzwfzxlKCeOUgxTfiKYs0WUWqzEvyNAYtramYgSknsQ==} dependencies: @@ -11539,11 +11548,6 @@ packages: - supports-color dev: true - /@prisma/get-platform@5.19.0: - resolution: {integrity: sha512-s9DWkZKnuP4Y8uy6yZfvqQ/9X3/+2KYf3IZUVZz5OstJdGBJrBlbmIuMl81917wp5TuK/1k2TpHNCEdpYLPKmg==} - dependencies: - '@prisma/debug': 5.19.0 - /@prisma/get-platform@5.3.1: resolution: {integrity: sha512-3IiZY2BUjKnAuZ0569zppZE6/rZbVAM09//c2nvPbbkGG9MqrirA8fbhhF7tfVmhyVfdmVCHnf/ujWPHJ8B46Q==} dependencies: @@ -11561,6 +11565,11 @@ packages: - supports-color dev: true + /@prisma/get-platform@6.8.2: + resolution: {integrity: sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow==} + dependencies: + '@prisma/debug': 6.8.2 + /@prisma/instrumentation@5.11.0: resolution: {integrity: sha512-ou4nvDpNEY6+t3Dn9juOTz6tK33D0Y4XXkEZ2uPd8KH6Mqmc+4LYOOm470DP7noj7dyJjuGiM+wpPk//HKrcDg==} dependencies: @@ -28097,7 +28106,6 @@ packages: /jiti@2.4.2: resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} hasBin: true - dev: true /joi@17.7.0: resolution: {integrity: sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==} @@ -32461,23 +32469,28 @@ packages: - supports-color dev: true - /prisma@5.19.0: - resolution: {integrity: sha512-Pu7lUKpVyTx8cVwM26dYh8NdvMOkMnJXzE8L6cikFuR4JwyMU5NKofQkWyxJKlTT4fNjmcnibTvklV8oVMrn+g==} + /prisma@5.4.1: + resolution: {integrity: sha512-op9PmU8Bcw5dNAas82wBYTG0yHnpq9/O3bhxbDBrNzwZTwBqsVCxxYRLf6wHNh9HVaDGhgjjHlu1+BcW8qdnBg==} engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 5.19.0 - optionalDependencies: - fsevents: 2.3.3 + '@prisma/engines': 5.4.1 - /prisma@5.4.1: - resolution: {integrity: sha512-op9PmU8Bcw5dNAas82wBYTG0yHnpq9/O3bhxbDBrNzwZTwBqsVCxxYRLf6wHNh9HVaDGhgjjHlu1+BcW8qdnBg==} - engines: {node: '>=16.13'} + /prisma@6.8.2(typescript@5.5.4): + resolution: {integrity: sha512-JNricTXQxzDtRS7lCGGOB4g5DJ91eg3nozdubXze3LpcMl1oWwcFddrj++Up3jnRE6X/3gB/xz3V+ecBk/eEGA==} + engines: {node: '>=18.18'} hasBin: true requiresBuild: true + peerDependencies: + typescript: '>=5.1.0' + peerDependenciesMeta: + typescript: + optional: true dependencies: - '@prisma/engines': 5.4.1 + '@prisma/config': 6.8.2 + '@prisma/engines': 6.8.2 + typescript: 5.5.4 /prismjs@1.29.0: resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} diff --git a/references/v3-catalog/package.json b/references/v3-catalog/package.json index b637520197..c967ca9b37 100644 --- a/references/v3-catalog/package.json +++ b/references/v3-catalog/package.json @@ -20,7 +20,7 @@ "@effect/schema": "^0.75.5", "@infisical/sdk": "^2.3.5", "@opentelemetry/api": "1.4.1", - "@prisma/client": "5.19.0", + "@prisma/client": "6.8.2", "@react-email/components": "0.0.24", "@react-email/render": "1.0.1", "@sentry/esbuild-plugin": "^2.22.2", @@ -87,7 +87,7 @@ "@types/fluent-ffmpeg": "^2.1.26", "@types/react": "^18.3.1", "esbuild": "^0.19.11", - "prisma": "5.19.0", + "prisma": "6.8.2", "prisma-kysely": "^1.8.0", "trigger.dev": "workspace:*", "ts-node": "^10.9.2", diff --git a/references/v3-catalog/prisma/schema/post.prisma b/references/v3-catalog/prisma/models/post.prisma similarity index 100% rename from references/v3-catalog/prisma/schema/post.prisma rename to references/v3-catalog/prisma/models/post.prisma diff --git a/references/v3-catalog/prisma/schema/user.prisma b/references/v3-catalog/prisma/models/user.prisma similarity index 100% rename from references/v3-catalog/prisma/schema/user.prisma rename to references/v3-catalog/prisma/models/user.prisma diff --git a/references/v3-catalog/prisma/schema/schema.prisma b/references/v3-catalog/prisma/schema.prisma similarity index 91% rename from references/v3-catalog/prisma/schema/schema.prisma rename to references/v3-catalog/prisma/schema.prisma index 0834802c1f..5a1fc9638a 100644 --- a/references/v3-catalog/prisma/schema/schema.prisma +++ b/references/v3-catalog/prisma/schema.prisma @@ -6,7 +6,7 @@ generator client { provider = "prisma-client-js" - previewFeatures = ["prismaSchemaFolder", "typedSql"] + previewFeatures = ["typedSql"] } datasource db { diff --git a/references/v3-catalog/trigger.config.ts b/references/v3-catalog/trigger.config.ts index 62698d14fb..cb45459b87 100644 --- a/references/v3-catalog/trigger.config.ts +++ b/references/v3-catalog/trigger.config.ts @@ -42,7 +42,7 @@ export default defineConfig({ emitDecoratorMetadata(), audioWaveform(), prismaExtension({ - schema: "prisma/schema/schema.prisma", + schema: "prisma", migrate: true, directUrlEnvVarName: "DATABASE_URL_UNPOOLED", clientGenerator: "client",