From 7b0071fd2734a4c7917a50a08e011ff77cd2c145 Mon Sep 17 00:00:00 2001
From: omar-dulaimi
Date: Fri, 7 Nov 2025 14:43:44 +0300
Subject: [PATCH 1/2] feat: support prisma-client generator provider
---
README.md | 9 ++++-
prisma/schema.prisma | 3 +-
src/index.ts | 1 -
src/prisma-generator.ts | 15 +++++++-
tests/basic-generation.test.ts | 14 +++----
tests/comprehensive-schema.test.ts | 14 +++----
tests/prisma-client-js-provider.test.ts | 34 +++++++++++++++++
tests/prisma-client-provider.test.ts | 38 +++++++++++++++++++
tests/relation-splitting.test.ts | 15 +++-----
tests/schemas/basic.prisma | 5 ++-
.../schemas/prisma-client-js-provider.prisma | 18 +++++++++
tests/schemas/prisma-client-provider.prisma | 19 ++++++++++
tests/self-relation.test.ts | 15 +++-----
tests/setup.ts | 11 ++++++
tests/swagger-generation.test.ts | 22 ++++-------
tests/utils/prisma-test-helpers.ts | 25 ++++++++++++
vitest.config.js | 3 +-
17 files changed, 205 insertions(+), 56 deletions(-)
create mode 100644 tests/prisma-client-js-provider.test.ts
create mode 100644 tests/prisma-client-provider.test.ts
create mode 100644 tests/schemas/prisma-client-js-provider.prisma
create mode 100644 tests/schemas/prisma-client-provider.prisma
create mode 100644 tests/setup.ts
create mode 100644 tests/utils/prisma-test-helpers.ts
diff --git a/README.md b/README.md
index ef46b1c..2a06f99 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,14 @@ pnpm add prisma-class-validator-generator
1. Add the generator to your Prisma schema:
+> **Heads up:** Keep a Prisma Client generator in the same schema. Both `provider = "prisma-client"` (recommended) and `provider = "prisma-client-js"` are supported.
+
```prisma
+generator client {
+ provider = "prisma-client" // or "prisma-client-js"
+ output = "../generated/prisma-client" // required when using prisma-client
+}
+
generator class_validator {
provider = "prisma-class-validator-generator"
output = "./generated" // optional, defaults to ./generated
@@ -472,4 +479,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
🐛 Report Issues •
💬 Discussions
-
\ No newline at end of file
+
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 6620013..0c512e1 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -1,5 +1,6 @@
generator client {
- provider = "prisma-client-js"
+ provider = "prisma-client"
+ output = "../generated/prisma-client"
}
generator class_validator {
diff --git a/src/index.ts b/src/index.ts
index 1d6d559..2c6745e 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -5,7 +5,6 @@ generatorHandler({
onManifest: () => ({
defaultOutput: './generated',
prettyName: 'Prisma Class Validator Generator',
- requiresGenerators: ['prisma-client-js'],
}),
onGenerate: generate,
});
diff --git a/src/prisma-generator.ts b/src/prisma-generator.ts
index 7286784..18aeb0a 100644
--- a/src/prisma-generator.ts
+++ b/src/prisma-generator.ts
@@ -15,6 +15,11 @@ export interface GeneratorConfig {
separateRelationFields: boolean;
}
+const SUPPORTED_PRISMA_CLIENT_PROVIDERS = new Set([
+ 'prisma-client',
+ 'prisma-client-js',
+]);
+
export async function generate(options: GeneratorOptions) {
const outputDir = parseEnvValue(options.generator.output as EnvValue);
@@ -27,10 +32,16 @@ export async function generate(options: GeneratorOptions) {
await fs.mkdir(outputDir, { recursive: true });
await removeDir(outputDir, true);
- const prismaClientProvider = options.otherGenerators.find(
- (it) => parseEnvValue(it.provider) === 'prisma-client-js',
+ const prismaClientProvider = options.otherGenerators.find((it) =>
+ SUPPORTED_PRISMA_CLIENT_PROVIDERS.has(parseEnvValue(it.provider)),
);
+ if (!prismaClientProvider) {
+ throw new Error(
+ 'Prisma Class Validator Generator requires a Prisma Client generator with provider "prisma-client" or "prisma-client-js".',
+ );
+ }
+
const prismaClientDmmf = await getDMMF({
datamodel: options.datamodel,
previewFeatures: prismaClientProvider?.previewFeatures || [],
diff --git a/tests/basic-generation.test.ts b/tests/basic-generation.test.ts
index 0ed5495..58a7ecc 100644
--- a/tests/basic-generation.test.ts
+++ b/tests/basic-generation.test.ts
@@ -1,23 +1,21 @@
-import { exec } from 'child_process';
-import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
import { describe, it, expect, beforeAll } from 'vitest';
import path from 'path';
-
-const execAsync = promisify(exec);
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
describe('Basic Class Validator Generation', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
+ await ensureGeneratorBuilt();
}, 60000);
it('should generate basic models with class validators', async () => {
const schemaPath = path.resolve(__dirname, 'schemas/basic.prisma');
const outputPath = path.resolve(__dirname, 'generated/basic');
- // Generate using the basic schema
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
// Check if generated files exist
expect(existsSync(path.join(outputPath, 'models', 'User.model.ts'))).toBe(
diff --git a/tests/comprehensive-schema.test.ts b/tests/comprehensive-schema.test.ts
index 6e45a20..c1a507e 100644
--- a/tests/comprehensive-schema.test.ts
+++ b/tests/comprehensive-schema.test.ts
@@ -1,23 +1,21 @@
-import { exec } from 'child_process';
-import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
import { describe, it, expect, beforeAll } from 'vitest';
import path from 'path';
-
-const execAsync = promisify(exec);
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
describe('Comprehensive Schema Generation', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
+ await ensureGeneratorBuilt();
}, 60000);
it('should generate comprehensive models with enums and relations', async () => {
const schemaPath = path.resolve(__dirname, 'schemas/comprehensive.prisma');
const outputPath = path.resolve(__dirname, 'generated/comprehensive');
- // Generate using the comprehensive schema
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
// Check if generated files exist
expect(existsSync(path.join(outputPath, 'models', 'User.model.ts'))).toBe(
diff --git a/tests/prisma-client-js-provider.test.ts b/tests/prisma-client-js-provider.test.ts
new file mode 100644
index 0000000..5e37f19
--- /dev/null
+++ b/tests/prisma-client-js-provider.test.ts
@@ -0,0 +1,34 @@
+import { existsSync } from 'fs';
+import { describe, it, expect, beforeAll } from 'vitest';
+import path from 'path';
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
+
+const schemaPath = path.resolve(
+ __dirname,
+ 'schemas/prisma-client-js-provider.prisma',
+);
+const classValidatorOutput = path.resolve(
+ __dirname,
+ 'generated/providers/prisma-client-js-class-validator',
+);
+
+
+describe('Prisma Client JS generator provider', () => {
+ beforeAll(async () => {
+ await ensureGeneratorBuilt();
+ }, 60000);
+
+ it('generates artifacts when using provider = "prisma-client-js"', async () => {
+ await runPrismaGenerate({ schemaPath });
+
+ expect(
+ existsSync(path.join(classValidatorOutput, 'models', 'User.model.ts')),
+ ).toBe(true);
+ expect(
+ existsSync(path.join(classValidatorOutput, 'helpers', 'index.ts')),
+ ).toBe(true);
+ });
+});
diff --git a/tests/prisma-client-provider.test.ts b/tests/prisma-client-provider.test.ts
new file mode 100644
index 0000000..393b28a
--- /dev/null
+++ b/tests/prisma-client-provider.test.ts
@@ -0,0 +1,38 @@
+import { existsSync } from 'fs';
+import { describe, it, expect, beforeAll } from 'vitest';
+import path from 'path';
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
+
+const schemaPath = path.resolve(
+ __dirname,
+ 'schemas/prisma-client-provider.prisma',
+);
+const classValidatorOutput = path.resolve(
+ __dirname,
+ 'generated/providers/prisma-client-class-validator',
+);
+const prismaClientOutput = path.resolve(
+ __dirname,
+ 'generated/providers/prisma-client',
+);
+
+describe('Prisma Client generator provider', () => {
+ beforeAll(async () => {
+ await ensureGeneratorBuilt();
+ }, 60000);
+
+ it('generates artifacts when using provider = "prisma-client"', async () => {
+ await runPrismaGenerate({ schemaPath });
+
+ expect(
+ existsSync(path.join(classValidatorOutput, 'models', 'User.model.ts')),
+ ).toBe(true);
+ expect(
+ existsSync(path.join(classValidatorOutput, 'helpers', 'index.ts')),
+ ).toBe(true);
+ expect(existsSync(prismaClientOutput)).toBe(true);
+ });
+});
diff --git a/tests/relation-splitting.test.ts b/tests/relation-splitting.test.ts
index d0dcd1b..2274241 100644
--- a/tests/relation-splitting.test.ts
+++ b/tests/relation-splitting.test.ts
@@ -1,19 +1,16 @@
-import { exec } from 'child_process';
-import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
import { describe, it, expect, beforeAll } from 'vitest';
import path from 'path';
-
-const execAsync = promisify(exec);
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
describe('Relation Splitting Generation', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
-
- // Generate models for full-features schema
+ await ensureGeneratorBuilt();
const schemaPath = path.resolve(__dirname, 'schemas/full-features.prisma');
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
}, 60000);
it('should generate separate base and relation classes', () => {
diff --git a/tests/schemas/basic.prisma b/tests/schemas/basic.prisma
index 259a5fb..65f5e03 100644
--- a/tests/schemas/basic.prisma
+++ b/tests/schemas/basic.prisma
@@ -1,5 +1,6 @@
generator client {
- provider = "prisma-client-js"
+ provider = "prisma-client"
+ output = "../generated/basic-client"
}
generator class_validator {
@@ -30,4 +31,4 @@ model Post {
author User? @relation(fields: [authorId], references: [id])
authorId Int?
rating Float
-}
\ No newline at end of file
+}
diff --git a/tests/schemas/prisma-client-js-provider.prisma b/tests/schemas/prisma-client-js-provider.prisma
new file mode 100644
index 0000000..f5755fc
--- /dev/null
+++ b/tests/schemas/prisma-client-js-provider.prisma
@@ -0,0 +1,18 @@
+generator client {
+ provider = "prisma-client-js"
+}
+
+generator class_validator {
+ provider = "node ./lib/generator.js"
+ output = "../generated/providers/prisma-client-js-class-validator"
+}
+
+datasource db {
+ provider = "sqlite"
+ url = "file:./provider-client-js.db"
+}
+
+model User {
+ id Int @id @default(autoincrement())
+ email String @unique
+}
diff --git a/tests/schemas/prisma-client-provider.prisma b/tests/schemas/prisma-client-provider.prisma
new file mode 100644
index 0000000..f7ad532
--- /dev/null
+++ b/tests/schemas/prisma-client-provider.prisma
@@ -0,0 +1,19 @@
+generator client {
+ provider = "prisma-client"
+ output = "../generated/providers/prisma-client"
+}
+
+generator class_validator {
+ provider = "node ./lib/generator.js"
+ output = "../generated/providers/prisma-client-class-validator"
+}
+
+datasource db {
+ provider = "sqlite"
+ url = "file:./provider-client.db"
+}
+
+model User {
+ id Int @id @default(autoincrement())
+ email String @unique
+}
diff --git a/tests/self-relation.test.ts b/tests/self-relation.test.ts
index 67631a5..d52d964 100644
--- a/tests/self-relation.test.ts
+++ b/tests/self-relation.test.ts
@@ -1,19 +1,16 @@
-import { exec } from 'child_process';
-import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
import { describe, it, expect, beforeAll } from 'vitest';
import path from 'path';
-
-const execAsync = promisify(exec);
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
describe('Self-Relation Generation', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
-
- // Generate models for self-relation schema
+ await ensureGeneratorBuilt();
const schemaPath = path.resolve(__dirname, 'schemas/self-relation.prisma');
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
}, 60000);
it('should generate UserBase class without self-relations', () => {
diff --git a/tests/setup.ts b/tests/setup.ts
new file mode 100644
index 0000000..bd4edf0
--- /dev/null
+++ b/tests/setup.ts
@@ -0,0 +1,11 @@
+import { beforeEach, afterEach } from 'vitest';
+
+const baseEnv = { ...process.env };
+
+beforeEach(() => {
+ process.env = { ...baseEnv } as NodeJS.ProcessEnv;
+});
+
+afterEach(() => {
+ process.env = { ...baseEnv } as NodeJS.ProcessEnv;
+});
diff --git a/tests/swagger-generation.test.ts b/tests/swagger-generation.test.ts
index 97c91d5..0a3ad0d 100644
--- a/tests/swagger-generation.test.ts
+++ b/tests/swagger-generation.test.ts
@@ -1,19 +1,16 @@
-import { exec } from 'child_process';
-import { promisify } from 'util';
import { existsSync, readFileSync } from 'fs';
import { describe, it, expect, beforeAll } from 'vitest';
import path from 'path';
-
-const execAsync = promisify(exec);
+import {
+ ensureGeneratorBuilt,
+ runPrismaGenerate,
+} from './utils/prisma-test-helpers';
describe('Swagger Generation', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
-
- // Generate models for swagger schema
+ await ensureGeneratorBuilt();
const schemaPath = path.resolve(__dirname, 'schemas/swagger.prisma');
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
}, 60000);
it('should generate models with Swagger decorators when enabled', () => {
@@ -73,12 +70,9 @@ describe('Swagger Generation', () => {
describe('Combined Class with separateRelationFields', () => {
beforeAll(async () => {
- // Build the generator first
- await execAsync('npm run build');
-
- // Generate models for full-features schema (has both swagger and separateRelationFields)
+ await ensureGeneratorBuilt();
const schemaPath = path.resolve(__dirname, 'schemas/full-features.prisma');
- await execAsync(`npx prisma generate --schema="${schemaPath}"`);
+ await runPrismaGenerate({ schemaPath });
}, 60000);
it('should generate combined class with class-validator and Swagger decorators for relations', () => {
diff --git a/tests/utils/prisma-test-helpers.ts b/tests/utils/prisma-test-helpers.ts
new file mode 100644
index 0000000..cfcbf1b
--- /dev/null
+++ b/tests/utils/prisma-test-helpers.ts
@@ -0,0 +1,25 @@
+import { exec } from 'child_process';
+import { promisify } from 'util';
+
+const execAsync = promisify(exec);
+
+let buildPromise: Promise | null = null;
+
+export async function ensureGeneratorBuilt() {
+ if (!buildPromise) {
+ buildPromise = execAsync('npm run build');
+ }
+ return buildPromise;
+}
+
+interface GenerateOptions {
+ schemaPath: string;
+ env?: NodeJS.ProcessEnv;
+}
+
+export async function runPrismaGenerate(options: GenerateOptions) {
+ const { schemaPath, env } = options;
+ await execAsync(`npx prisma generate --schema="${schemaPath}"`, {
+ env: { ...process.env, ...env },
+ });
+}
diff --git a/vitest.config.js b/vitest.config.js
index a08032d..18e3da9 100644
--- a/vitest.config.js
+++ b/vitest.config.js
@@ -6,6 +6,7 @@ export default defineConfig({
testTimeout: 300000, // 5 minutes for complex generation tests
hookTimeout: 60000, // 1 minute for setup/teardown
globals: true,
+ setupFiles: ['tests/setup.ts'],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
@@ -19,4 +20,4 @@ export default defineConfig({
]
}
}
-});
\ No newline at end of file
+});
From fc8422097fcea1771c90b2a3139ff7153d4939ec Mon Sep 17 00:00:00 2001
From: omar-dulaimi
Date: Fri, 7 Nov 2025 14:47:04 +0300
Subject: [PATCH 2/2] chore: format provider test
---
tests/prisma-client-js-provider.test.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/tests/prisma-client-js-provider.test.ts b/tests/prisma-client-js-provider.test.ts
index 5e37f19..45cdae0 100644
--- a/tests/prisma-client-js-provider.test.ts
+++ b/tests/prisma-client-js-provider.test.ts
@@ -15,7 +15,6 @@ const classValidatorOutput = path.resolve(
'generated/providers/prisma-client-js-class-validator',
);
-
describe('Prisma Client JS generator provider', () => {
beforeAll(async () => {
await ensureGeneratorBuilt();