From 2e64b3eb083a8f200042131cb1e1e17e96ba3fdc Mon Sep 17 00:00:00 2001 From: Mathieu Post Date: Mon, 9 Mar 2026 21:45:34 +0100 Subject: [PATCH] fix: make $queryRawTyped generation conditional on typedSql preview feature Fixes issue #20 where $queryRawTyped was always generated even when the typedSql preview feature was not enabled, causing TypeScript errors. The generator now detects whether any Prisma generator has the typedSql preview feature enabled and only generates $queryRawTyped and imports the runtime module when it's available. Added tests to verify: - $queryRawTyped is generated when typedSql is enabled - $queryRawTyped is not generated when typedSql is not enabled --- scripts/runTests.ts | 1 + src/index.ts | 35 ++++++++++++++++++------------ tests/integration.test.ts | 13 +++++++++++ tests/no-typedsql/prisma.config.ts | 7 ++++++ tests/no-typedsql/schema.prisma | 19 ++++++++++++++++ 5 files changed, 61 insertions(+), 14 deletions(-) create mode 100644 tests/no-typedsql/prisma.config.ts create mode 100644 tests/no-typedsql/schema.prisma diff --git a/scripts/runTests.ts b/scripts/runTests.ts index ced3df9..9110fbe 100644 --- a/scripts/runTests.ts +++ b/scripts/runTests.ts @@ -37,6 +37,7 @@ const program = Effect.gen(function* () { yield* run("./tests", "prisma", "db", "push"); } yield* run("./tests", "prisma", "generate", "--sql"); + yield* run("./tests/no-typedsql", "prisma", "generate"); yield* run("./tests", "tsc", "--noEmit"); yield* run("./tests", "vitest", "run"); }).pipe( diff --git a/src/index.ts b/src/index.ts index 2d1f021..e1b1b67 100644 --- a/src/index.ts +++ b/src/index.ts @@ -37,14 +37,27 @@ generatorHandler({ throw new Error("effect-prisma-generator: outputPath must be a ts file"); } + const hasTypedSql = [options.generator, ...options.otherGenerators] + .some(g => g.previewFeatures.includes("typedSql")); + const outputDir = path.dirname(outputPath); await fs.mkdir(outputDir, { recursive: true }); - await generateUnifiedService([...models], outputPath, clientImportPath); + await generateUnifiedService([...models], outputPath, clientImportPath, hasTypedSql); }, }); -function generateRawSqlOperations() { +function generateRawSqlOperations(hasTypedSql: boolean) { + const typedSqlOperation = hasTypedSql ? ` + + $queryRawTyped: (typedQuery: runtime.TypedSql) => + Effect.flatMap(clientOrTx(client), client => + Effect.tryPromise({ + try: () => client.$queryRawTyped(typedQuery), + catch: (error) => mapError(error, "$queryRawTyped", "Prisma") + }), + ),` : ''; + return ` $executeRaw: (args: Prisma.Sql | [Prisma.Sql, ...any[]]) => Effect.flatMap(clientOrTx(client), client => @@ -76,15 +89,7 @@ function generateRawSqlOperations() { try: () => client.$queryRawUnsafe(query, ...values), catch: (error) => mapError(error, "$queryRawUnsafe", "Prisma") }), - ), - - $queryRawTyped: (typedQuery: runtime.TypedSql) => - Effect.flatMap(clientOrTx(client), client => - Effect.tryPromise({ - try: () => client.$queryRawTyped(typedQuery), - catch: (error) => mapError(error, "$queryRawTyped", "Prisma") - }), - ),`; + ),${typedSqlOperation}`; } function generateModelOperations(models: DMMF.Model[]) { @@ -310,15 +315,17 @@ async function generateUnifiedService( models: DMMF.Model[], outputPath: string, clientImportPath: string, + hasTypedSql: boolean, ) { - const rawSqlOperations = generateRawSqlOperations(); + const rawSqlOperations = generateRawSqlOperations(hasTypedSql); const modelOperations = generateModelOperations(models); + const runtimeImport = hasTypedSql ? `\nimport * as runtime from "@prisma/client/runtime/client"` : ''; + const serviceContent = `${header} import { Cause, Context, Data, Effect, Exit, Option, Runtime } from "effect" import { Service } from "effect/Effect" -import { Prisma, PrismaClient } from "${clientImportPath}" -import * as runtime from "@prisma/client/runtime/client" +import { Prisma, PrismaClient } from "${clientImportPath}"${runtimeImport} export class PrismaClientService extends Context.Tag("PrismaClientService")< PrismaClientService, diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 5bd7ba7..aa6b05d 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -1,6 +1,7 @@ import { describe, expect, it } from "@effect/vitest"; import { PrismaBetterSqlite3 } from "@prisma/adapter-better-sqlite3"; import { Data, Effect, Layer } from "effect"; +import * as fs from "fs"; import { PrismaClient } from "./prisma/generated/client"; import { PrismaClientService, @@ -290,4 +291,16 @@ describe("Prisma Effect Generator", () => { yield* prisma.user.delete({ where: { email } }); }).pipe(Effect.provide(MainLayer)), ); + + it("should include $queryRawTyped when typedSql preview feature is enabled", () => { + const generated = fs.readFileSync("prisma/generated/effect.ts", "utf-8"); + expect(generated).toContain("$queryRawTyped"); + expect(generated).toContain('import * as runtime from "@prisma/client/runtime/client"'); + }); + + it("should not include $queryRawTyped when typedSql preview feature is not enabled", () => { + const generated = fs.readFileSync("no-typedsql/generated/effect.ts", "utf-8"); + expect(generated).not.toContain("$queryRawTyped"); + expect(generated).not.toContain('import * as runtime from "@prisma/client/runtime/client"'); + }); }); diff --git a/tests/no-typedsql/prisma.config.ts b/tests/no-typedsql/prisma.config.ts new file mode 100644 index 0000000..621ec3a --- /dev/null +++ b/tests/no-typedsql/prisma.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "prisma/config"; + +export default defineConfig({ + datasource: { + url: "file:dev.db", + }, +}); diff --git a/tests/no-typedsql/schema.prisma b/tests/no-typedsql/schema.prisma new file mode 100644 index 0000000..46d54fc --- /dev/null +++ b/tests/no-typedsql/schema.prisma @@ -0,0 +1,19 @@ +datasource db { + provider = "sqlite" +} + +generator client { + provider = "prisma-client" + output = "./generated" +} + +generator effect { + provider = "node ../../dist/index.js" + output = "./generated/effect.ts" + clientImportPath = "./client" +} + +model User { + id Int @id @default(autoincrement()) + email String @unique +}