diff --git a/packages/api-server/src/watchPaths.ts b/packages/api-server/src/watchPaths.ts index 7b64e0338f..83e282120e 100644 --- a/packages/api-server/src/watchPaths.ts +++ b/packages/api-server/src/watchPaths.ts @@ -4,6 +4,7 @@ import path from 'node:path' import { getDbDir, getPaths, + getPrismaGeneratorOutputPath, importStatementPath, } from '@cedarjs/project-config' @@ -94,6 +95,16 @@ async function apiIgnorePaths() { dbDir, ] + // Always ignore the Prisma client generator output. It's usually inside + // `dbDir` (already ignored above), but projects that point the generator + // `output` outside the schema directory — e.g. into `api/src/lib/...` — + // otherwise trigger an infinite rebuild loop: each build regenerates the + // client, chokidar sees the writes, and another build is scheduled. + const prismaGeneratorOutputPath = await getPrismaGeneratorOutputPath() + if (prismaGeneratorOutputPath) { + ignoredApiPaths.push(prismaGeneratorOutputPath) + } + return ignoredApiPaths } diff --git a/packages/project-config/src/prisma.ts b/packages/project-config/src/prisma.ts index ba8fab357b..e22f72a039 100644 --- a/packages/project-config/src/prisma.ts +++ b/packages/project-config/src/prisma.ts @@ -159,6 +159,36 @@ type ResolveReturnType = | { clientPath: string; error: undefined } | { clientPath: string | undefined; error: string } +/** + * Resolves the absolute path the Prisma client generator writes to. + * Returns `undefined` if the schema or generator can't be read. + */ +export async function getPrismaGeneratorOutputPath(): Promise< + string | undefined +> { + try { + const prismaInternalsMod = await import('@prisma/internals') + const { getConfig } = prismaInternalsMod.default || prismaInternalsMod + + const { schemas, schemaRootDir } = await getPrismaSchemas() + const config = await getConfig({ datamodel: schemas }) + const generator = + config.generators.find((entry) => entry.name === 'client') ?? + config.generators[0] + const output = generator?.output?.value + + if (!output) { + return undefined + } + + return path.isAbsolute(output) + ? output + : path.resolve(schemaRootDir, output) + } catch { + return undefined + } +} + export async function resolveGeneratedPrismaClient(): Promise { let generatorOutputPath: string | undefined let ext = 'ts'