Skip to content

chore: support Prisma's official multi-file schema structure #2135

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/unlucky-ghosts-do.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/build": minor
---

Support Prisma's official multi-file schema structure
36 changes: 25 additions & 11 deletions docs/config/extensions/prismaExtension.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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`:

<CodeGroup>

```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
```

</CodeGroup>

<Note>
To use this feature you must be using `[email protected]` or higher. Their official documentation can be found [here](https://www.prisma.io/docs/orm/prisma-schema/overview/location#multi-file-prisma-schema).
</Note>
53 changes: 24 additions & 29 deletions packages/build/src/extensions/prisma.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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[] = [];

Expand All @@ -127,26 +135,22 @@ 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"))
);

context.logger.debug(`Found sql files`, {
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}`);

Expand All @@ -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}`);

Expand All @@ -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}`
Expand All @@ -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}`
Expand Down
Loading