From 3e8aeb9782a3cf604b31cfd8f2acd87e634664c1 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Mon, 14 Jul 2025 11:05:16 -0400 Subject: [PATCH 1/3] fix: Update re-exports for new prisma-client generator --- .../src/plugins/enhancer/enhance/index.ts | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index cdfdb006b..71f0b7152 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -109,28 +109,36 @@ export class EnhancerGenerator { const prismaImport = getPrismaClientImportSpec(this.outDir, this.options); let prismaTypesFixed = false; - let resultPrismaTypeImport = prismaImport; + let resultPrismaBaseImport = prismaImport; if (this.needsLogicalClient) { prismaTypesFixed = true; - resultPrismaTypeImport = LOGICAL_CLIENT_GENERATION_PATH; - if (this.isNewPrismaClientGenerator) { - resultPrismaTypeImport += '/client'; - } + resultPrismaBaseImport = LOGICAL_CLIENT_GENERATION_PATH; const result = await this.generateLogicalPrisma(); dmmf = result.dmmf; } - // reexport PrismaClient types (original or fixed) - const modelsTsContent = `export * from '${resultPrismaTypeImport}';${ - this.isNewPrismaClientGenerator ? "\nexport * from './json-types';" : '' - }`; + const modelsTsContent = this.isNewPrismaClientGenerator + ? `export * from '${resultPrismaBaseImport}/models';\nexport * from './json-types';` + : `export * from '${resultPrismaBaseImport}';`; const modelsTs = this.project.createSourceFile(path.join(this.outDir, 'models.ts'), modelsTsContent, { overwrite: true, }); this.saveSourceFile(modelsTs); + if (this.isNewPrismaClientGenerator) { + const enumsTs = this.project.createSourceFile(path.join(this.outDir, 'enums.ts'), `export * from '${resultPrismaBaseImport}/enums';`, { + overwrite: true, + }); + this.saveSourceFile(enumsTs); + + const clientTs = this.project.createSourceFile(path.join(this.outDir, 'client.ts'), `export * from '${resultPrismaBaseImport}/client';`, { + overwrite: true, + }); + this.saveSourceFile(clientTs); + } + const authDecl = getAuthDecl(getDataModelAndTypeDefs(this.model)); const authTypes = authDecl ? generateAuthType(this.model, authDecl) : ''; const authTypeParam = authDecl ? `auth.${authDecl.name}` : 'AuthUser'; From 85f8e1c0832901d58a415e90be93ebdeebb2a410 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Mon, 14 Jul 2025 11:14:51 -0400 Subject: [PATCH 2/3] fix build issues --- packages/schema/src/plugins/enhancer/enhance/index.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index 71f0b7152..2b25c8329 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -110,10 +110,14 @@ export class EnhancerGenerator { const prismaImport = getPrismaClientImportSpec(this.outDir, this.options); let prismaTypesFixed = false; let resultPrismaBaseImport = prismaImport; + let resultPrismaClientImport = prismaImport; if (this.needsLogicalClient) { prismaTypesFixed = true; resultPrismaBaseImport = LOGICAL_CLIENT_GENERATION_PATH; + if (this.isNewPrismaClientGenerator) { + resultPrismaClientImport = `${LOGICAL_CLIENT_GENERATION_PATH}/client`; + } const result = await this.generateLogicalPrisma(); dmmf = result.dmmf; } @@ -133,7 +137,7 @@ export class EnhancerGenerator { }); this.saveSourceFile(enumsTs); - const clientTs = this.project.createSourceFile(path.join(this.outDir, 'client.ts'), `export * from '${resultPrismaBaseImport}/client';`, { + const clientTs = this.project.createSourceFile(path.join(this.outDir, 'client.ts'), `export * from '${resultPrismaClientImport}';`, { overwrite: true, }); this.saveSourceFile(clientTs); @@ -163,7 +167,7 @@ ${ ${ prismaTypesFixed - ? this.createLogicalPrismaImports(prismaImport, resultPrismaTypeImport, target) + ? this.createLogicalPrismaImports(prismaImport, resultPrismaClientImport, target) : this.createSimplePrismaImports(prismaImport, target) } From be3f72fd6fdf66a641729bc38f86a64d9ed9b32e Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Tue, 15 Jul 2025 21:23:47 +0800 Subject: [PATCH 3/3] refactor enhancer generation logic --- .../src/plugins/enhancer/enhance/index.ts | 148 +++++--- packages/testtools/src/schema.ts | 158 ++++---- .../misc/prisma-client-generator.test.ts | 344 ++++++++++++++---- 3 files changed, 461 insertions(+), 189 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index 2b25c8329..8e6e3719d 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -1,4 +1,5 @@ import { DELEGATE_AUX_RELATION_PREFIX } from '@zenstackhq/runtime'; +import { invariant, upperCaseFirst } from '@zenstackhq/runtime/local-helpers'; import { PluginError, getAttribute, @@ -26,7 +27,6 @@ import { type Model, } from '@zenstackhq/sdk/ast'; import { getDMMF, getPrismaClientImportSpec, getPrismaVersion, type DMMF } from '@zenstackhq/sdk/prisma'; -import { invariant, upperCaseFirst } from '@zenstackhq/runtime/local-helpers'; import fs from 'fs'; import path from 'path'; import semver from 'semver'; @@ -105,56 +105,122 @@ export class EnhancerGenerator { } async generate(): Promise<{ dmmf: DMMF.Document | undefined; newPrismaClientDtsPath: string | undefined }> { - let dmmf: DMMF.Document | undefined; + if (this.isNewPrismaClientGenerator) { + // "prisma-client" generator + return this.generateForNewClientGenerator(); + } else { + // "prisma-client-js" generator + return this.generateForOldClientGenerator(); + } + } + // logic for "prisma-client" generator + private async generateForNewClientGenerator() { + const needsLogicalClient = this.needsLogicalClient; const prismaImport = getPrismaClientImportSpec(this.outDir, this.options); - let prismaTypesFixed = false; - let resultPrismaBaseImport = prismaImport; - let resultPrismaClientImport = prismaImport; + let resultPrismaBaseImport = path.dirname(prismaImport); // get to the parent folder of "client" + let dmmf: DMMF.Document | undefined; - if (this.needsLogicalClient) { - prismaTypesFixed = true; + if (needsLogicalClient) { + // use logical client, note we use the parent of "client" folder here too resultPrismaBaseImport = LOGICAL_CLIENT_GENERATION_PATH; - if (this.isNewPrismaClientGenerator) { - resultPrismaClientImport = `${LOGICAL_CLIENT_GENERATION_PATH}/client`; - } const result = await this.generateLogicalPrisma(); dmmf = result.dmmf; } - const modelsTsContent = this.isNewPrismaClientGenerator - ? `export * from '${resultPrismaBaseImport}/models';\nexport * from './json-types';` - : `export * from '${resultPrismaBaseImport}';`; - + // `models.ts` for exporting model types + const modelsTsContent = [ + `export * from '${resultPrismaBaseImport}/models';`, + `export * from './json-types';`, + ].join('\n'); const modelsTs = this.project.createSourceFile(path.join(this.outDir, 'models.ts'), modelsTsContent, { overwrite: true, }); this.saveSourceFile(modelsTs); - if (this.isNewPrismaClientGenerator) { - const enumsTs = this.project.createSourceFile(path.join(this.outDir, 'enums.ts'), `export * from '${resultPrismaBaseImport}/enums';`, { + // `enums.ts` for exporting enums + const enumsTs = this.project.createSourceFile( + path.join(this.outDir, 'enums.ts'), + `export * from '${resultPrismaBaseImport}/enums';`, + { overwrite: true, - }); - this.saveSourceFile(enumsTs); + } + ); + this.saveSourceFile(enumsTs); - const clientTs = this.project.createSourceFile(path.join(this.outDir, 'client.ts'), `export * from '${resultPrismaClientImport}';`, { + // `client.ts` for exporting `PrismaClient` and `Prisma` namespace + const clientTs = this.project.createSourceFile( + path.join(this.outDir, 'client.ts'), + `export * from '${resultPrismaBaseImport}/client';`, + { overwrite: true, - }); - this.saveSourceFile(clientTs); + } + ); + this.saveSourceFile(clientTs); + + // `enhance.ts` and `enhance-edge.ts` + for (const target of ['node', 'edge'] as const) { + this.generateEnhance(prismaImport, `${resultPrismaBaseImport}/client`, needsLogicalClient, target); } + return { + // logical dmmf if there is one + dmmf, + // new client generator doesn't have a barrel .d.ts file + newPrismaClientDtsPath: undefined, + }; + } + + // logic for "prisma-client-js" generator + private async generateForOldClientGenerator() { + const needsLogicalClient = this.needsLogicalClient; + const prismaImport = getPrismaClientImportSpec(this.outDir, this.options); + let resultPrismaClientImport = prismaImport; + let dmmf: DMMF.Document | undefined; + + if (needsLogicalClient) { + // redirect `PrismaClient` import to the logical client + resultPrismaClientImport = LOGICAL_CLIENT_GENERATION_PATH; + const result = await this.generateLogicalPrisma(); + dmmf = result.dmmf; + } + + // `models.ts` for exporting model types + const modelsTsContent = `export * from '${resultPrismaClientImport}';`; + const modelsTs = this.project.createSourceFile(path.join(this.outDir, 'models.ts'), modelsTsContent, { + overwrite: true, + }); + this.saveSourceFile(modelsTs); + + // `enhance.ts` and `enhance-edge.ts` + for (const target of ['node', 'edge'] as const) { + this.generateEnhance(prismaImport, resultPrismaClientImport, needsLogicalClient, target); + } + + return { + // logical dmmf if there is one + dmmf, + newPrismaClientDtsPath: needsLogicalClient + ? path.resolve(this.outDir, LOGICAL_CLIENT_GENERATION_PATH, 'index.d.ts') + : undefined, + }; + } + + private generateEnhance( + prismaImport: string, + prismaClientImport: string, + needsLogicalClient: boolean, + target: 'node' | 'edge' + ) { const authDecl = getAuthDecl(getDataModelAndTypeDefs(this.model)); const authTypes = authDecl ? generateAuthType(this.model, authDecl) : ''; const authTypeParam = authDecl ? `auth.${authDecl.name}` : 'AuthUser'; - const checkerTypes = this.generatePermissionChecker ? generateCheckerType(this.model) : ''; - for (const target of ['node', 'edge']) { - // generate separate `enhance()` for node and edge runtime - const outFile = target === 'node' ? 'enhance.ts' : 'enhance-edge.ts'; - const enhanceTs = this.project.createSourceFile( - path.join(this.outDir, outFile), - `/* eslint-disable */ + const outFile = target === 'node' ? 'enhance.ts' : 'enhance-edge.ts'; + const enhanceTs = this.project.createSourceFile( + path.join(this.outDir, outFile), + `/* eslint-disable */ import { type EnhancementContext, type EnhancementOptions, type ZodSchemas, type AuthUser } from '@zenstackhq/runtime'; import { createEnhancement } from '@zenstackhq/runtime/enhancements/${target}'; import modelMeta from './model-meta'; @@ -166,8 +232,8 @@ ${ } ${ - prismaTypesFixed - ? this.createLogicalPrismaImports(prismaImport, resultPrismaClientImport, target) + needsLogicalClient + ? this.createLogicalPrismaImports(prismaImport, prismaClientImport, target) : this.createSimplePrismaImports(prismaImport, target) } @@ -176,23 +242,15 @@ ${authTypes} ${checkerTypes} ${ - prismaTypesFixed + needsLogicalClient ? this.createLogicalPrismaEnhanceFunction(authTypeParam) : this.createSimplePrismaEnhanceFunction(authTypeParam) } `, - { overwrite: true } - ); - - this.saveSourceFile(enhanceTs); - } + { overwrite: true } + ); - return { - dmmf, - newPrismaClientDtsPath: prismaTypesFixed - ? path.resolve(this.outDir, LOGICAL_CLIENT_GENERATION_PATH, 'index.d.ts') - : undefined, - }; + this.saveSourceFile(enhanceTs); } private getZodImport() { @@ -222,7 +280,7 @@ ${ return normalizedRelative(this.outDir, zodAbsPath); } - private createSimplePrismaImports(prismaImport: string, target: string) { + private createSimplePrismaImports(prismaImport: string, target: string | undefined) { const prismaTargetImport = target === 'edge' ? `${prismaImport}/edge` : prismaImport; return `import { Prisma, type PrismaClient } from '${prismaTargetImport}'; @@ -253,10 +311,10 @@ export function enhance(prisma: DbClient, context?: Enh `; } - private createLogicalPrismaImports(prismaImport: string, prismaClientImport: string, target: string) { + private createLogicalPrismaImports(prismaImport: string, prismaClientImport: string, target: string | undefined) { const prismaTargetImport = target === 'edge' ? `${prismaImport}/edge` : prismaImport; const runtimeLibraryImport = this.isNewPrismaClientGenerator - ? // new generator has these typed only in "@prisma/client" + ? // new generator has these types only in "@prisma/client" '@prisma/client/runtime/library' : // old generator has these types generated with the client `${prismaImport}/runtime/library`; diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index c1b8c6aeb..152b05ada 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -171,6 +171,84 @@ export async function loadSchemaFromFile(schemaFile: string, options?: SchemaLoa } export async function loadSchema(schema: string, options?: SchemaLoadOptions) { + const { projectDir, options: mergedOptions } = createProjectAndCompile(schema, options); + + const prismaLoadPath = + mergedOptions?.prismaLoadPath && mergedOptions.prismaLoadPath !== '@prisma/client' + ? path.isAbsolute(mergedOptions.prismaLoadPath) + ? mergedOptions.prismaLoadPath + : path.join(projectDir, mergedOptions.prismaLoadPath) + : path.join(projectDir, 'node_modules/.prisma/client'); + const prismaModule = require(prismaLoadPath); + const PrismaClient = prismaModule.PrismaClient; + + let clientOptions: object = { log: ['info', 'warn', 'error'] }; + if (mergedOptions?.prismaClientOptions) { + clientOptions = { ...clientOptions, ...mergedOptions.prismaClientOptions }; + } + let prisma = new PrismaClient(clientOptions); + // https://github.com/prisma/prisma/issues/18292 + prisma[Symbol.for('nodejs.util.inspect.custom')] = 'PrismaClient'; + + if (mergedOptions.pulseApiKey) { + const withPulse = loadModule('@prisma/extension-pulse/node', projectDir).withPulse; + prisma = prisma.$extends(withPulse({ apiKey: mergedOptions.pulseApiKey })); + } + + if (mergedOptions?.getPrismaOnly) { + return { + prisma, + prismaModule, + projectDir, + enhance: undefined as any, + enhanceRaw: undefined as any, + policy: undefined as unknown as PolicyDef, + modelMeta: undefined as any, + zodSchemas: undefined as any, + }; + } + + const outputPath = mergedOptions.output + ? path.isAbsolute(mergedOptions.output) + ? mergedOptions.output + : path.join(projectDir, mergedOptions.output) + : path.join(projectDir, 'node_modules', DEFAULT_RUNTIME_LOAD_PATH); + + const policy: PolicyDef = require(path.join(outputPath, 'policy')).default; + const modelMeta = require(path.join(outputPath, 'model-meta')).default; + + let zodSchemas: any; + try { + zodSchemas = require(path.join(outputPath, 'zod')); + } catch { + /* noop */ + } + + const enhance = require(path.join(outputPath, 'enhance')).enhance; + + return { + projectDir: projectDir, + prisma, + enhance: (user?: AuthUser, options?: EnhancementOptions): FullDbClientContract => + enhance( + prisma, + { user }, + { + logPrismaQuery: mergedOptions.logPrismaQuery, + transactionTimeout: 1000000, + kinds: mergedOptions.enhancements, + ...(options ?? mergedOptions.enhanceOptions), + } + ), + enhanceRaw: enhance, + policy, + modelMeta, + zodSchemas, + prismaModule, + }; +} + +export function createProjectAndCompile(schema: string, options: SchemaLoadOptions | undefined) { const opt = { ...defaultOptions, ...options }; let projectDir = opt.projectDir; @@ -282,11 +360,7 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { fs.writeFileSync(path.join(projectDir, name), content); }); - if (opt.extraSourceFiles && opt.extraSourceFiles.length > 0 && !opt.compile) { - console.warn('`extraSourceFiles` is true but `compile` is false.'); - } - - if (opt.compile) { + if (opt.compile || opt.extraSourceFiles) { console.log('Compiling...'); run('npx tsc --init'); @@ -303,79 +377,7 @@ export async function loadSchema(schema: string, options?: SchemaLoadOptions) { fs.writeFileSync(path.join(projectDir, './tsconfig.json'), JSON.stringify(tsconfig, null, 2)); run('npx tsc --project tsconfig.json'); } - - const prismaLoadPath = options?.prismaLoadPath - ? path.isAbsolute(options.prismaLoadPath) - ? options.prismaLoadPath - : path.join(projectDir, options.prismaLoadPath) - : path.join(projectDir, 'node_modules/.prisma/client'); - const prismaModule = require(prismaLoadPath); - const PrismaClient = prismaModule.PrismaClient; - - let clientOptions: object = { log: ['info', 'warn', 'error'] }; - if (options?.prismaClientOptions) { - clientOptions = { ...clientOptions, ...options.prismaClientOptions }; - } - let prisma = new PrismaClient(clientOptions); - // https://github.com/prisma/prisma/issues/18292 - prisma[Symbol.for('nodejs.util.inspect.custom')] = 'PrismaClient'; - - if (opt.pulseApiKey) { - const withPulse = loadModule('@prisma/extension-pulse/node', projectDir).withPulse; - prisma = prisma.$extends(withPulse({ apiKey: opt.pulseApiKey })); - } - - if (options?.getPrismaOnly) { - return { - prisma, - prismaModule, - projectDir, - enhance: undefined as any, - enhanceRaw: undefined as any, - policy: undefined as unknown as PolicyDef, - modelMeta: undefined as any, - zodSchemas: undefined as any, - }; - } - - const outputPath = opt.output - ? path.isAbsolute(opt.output) - ? opt.output - : path.join(projectDir, opt.output) - : path.join(projectDir, 'node_modules', DEFAULT_RUNTIME_LOAD_PATH); - - const policy: PolicyDef = require(path.join(outputPath, 'policy')).default; - const modelMeta = require(path.join(outputPath, 'model-meta')).default; - - let zodSchemas: any; - try { - zodSchemas = require(path.join(outputPath, 'zod')); - } catch { - /* noop */ - } - - const enhance = require(path.join(outputPath, 'enhance')).enhance; - - return { - projectDir: projectDir, - prisma, - enhance: (user?: AuthUser, options?: EnhancementOptions): FullDbClientContract => - enhance( - prisma, - { user }, - { - logPrismaQuery: opt.logPrismaQuery, - transactionTimeout: 1000000, - kinds: opt.enhancements, - ...(options ?? opt.enhanceOptions), - } - ), - enhanceRaw: enhance, - policy, - modelMeta, - zodSchemas, - prismaModule, - }; + return { projectDir, options: opt }; } /** diff --git a/tests/integration/tests/misc/prisma-client-generator.test.ts b/tests/integration/tests/misc/prisma-client-generator.test.ts index fbb2813c9..4b0d48151 100644 --- a/tests/integration/tests/misc/prisma-client-generator.test.ts +++ b/tests/integration/tests/misc/prisma-client-generator.test.ts @@ -1,20 +1,240 @@ -import { loadSchema } from '@zenstackhq/testtools'; - -describe('New prisma-client generator tests', () => { - it('works with `auth` in `@default`', async () => { - const { enhance, prisma } = await loadSchema( - ` - datasource db { - provider = "sqlite" - url = "file:./dev.db" +import { createProjectAndCompile, loadSchema } from '@zenstackhq/testtools'; + +const PRISMA_CLIENT_JS_GENERATOR = ` +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +generator client { + provider = "prisma-client-js" +} +`; + +const PRISMA_CLIENT_GENERATOR = ` +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +generator client { + provider = "prisma-client" + output = "../generated/prisma" + moduleFormat = "cjs" +} +`; + +describe.each([ + { + isNewGenerator: false, + generator: PRISMA_CLIENT_JS_GENERATOR, + prismaLoadPath: '@prisma/client', + clientLoadPath: '@prisma/client', + modelsLoadPath: '@prisma/client', + enumsLoadPath: '@prisma/client', + enhanceLoadPath: '.zenstack/enhance', + output: undefined, + }, + { + isNewGenerator: true, + generator: PRISMA_CLIENT_GENERATOR, + prismaLoadPath: './generated/prisma/client', + clientLoadPath: './generated/zenstack/client', + modelsLoadPath: './generated/zenstack/models', + enumsLoadPath: './generated/zenstack/enums', + enhanceLoadPath: './generated/zenstack/enhance', + output: './generated/zenstack', + }, +])('Prisma-client generator tests', (config) => { + describe('regular client', () => { + it('exports expected modules', async () => { + await createProjectAndCompile( + ` + ${config.generator} + + enum Role { + USER + ADMIN + } + + model User { + id Int @id + name String + role Role + } + `, + { + addPrelude: false, + output: config.output, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { Prisma, type User } from '${config.clientLoadPath}'; +${config.isNewGenerator ? "import type { UserModel } from '" + config.modelsLoadPath + "';" : ''} +import { Role } from '${config.enumsLoadPath}'; + +const user: User = { id: 1, name: 'Alice', role: Role.USER }; +console.log(user); + +${config.isNewGenerator ? "const user1: UserModel = { id: 1, name: 'Alice', role: 'USER' };\nconsole.log(user1);" : ''} + +const role = Role.USER; +console.log(role); + `, + }, + ], + } + ); + }); + + it('works with client extension', async () => { + await createProjectAndCompile( + ` + ${config.generator} + model User { + id Int @id @default(autoincrement()) + email String } + `, + { + addPrelude: false, + output: config.output, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { Prisma } from '${config.clientLoadPath}'; +import { PrismaClient } from '${config.prismaLoadPath}'; +import { enhance } from '${config.enhanceLoadPath}'; + +const prisma = new PrismaClient().$extends({ + model: { + user: { + async signUp(email: string) { + return prisma.user.create({ data: { email } }); + }, + }, + }, +}); + +async function main() { + const db = enhance(prisma); + const newUser = await db.user.signUp('user@test.com') +} + +main() + `, + }, + ], + } + ); + }); + }); - generator client { - provider = "prisma-client" - output = "./prisma-generated" - moduleFormat = "cjs" + describe('logical client', () => { + it('exports expected modules', async () => { + await createProjectAndCompile( + ` + ${config.generator} + + enum Role { + USER + ADMIN } + model User { + id Int @id + name String + role Role + } + + model Post { + id Int @id + authorId Int @default(auth().id) + } + `, + { + addPrelude: false, + output: config.output, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { Prisma, type User } from '${config.clientLoadPath}'; +${config.isNewGenerator ? "import type { UserModel } from '" + config.modelsLoadPath + "';" : ''} +import { Role } from '${config.enumsLoadPath}'; + +const user: User = { id: 1, name: 'Alice', role: Role.USER }; +console.log(user); + +${config.isNewGenerator ? "const user1: UserModel = { id: 1, name: 'Alice', role: 'USER' };\nconsole.log(user1);" : ''} + +const role = Role.USER; +console.log(role); + `, + }, + ], + } + ); + }); + + it('works with client extension', async () => { + await createProjectAndCompile( + ` + ${config.generator} + model User { + id Int @id @default(autoincrement()) + email String + } + + model Post { + id Int @id @default(autoincrement()) + authorId Int @default(auth().id) + } + `, + { + addPrelude: false, + output: config.output, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { PrismaClient } from '${config.prismaLoadPath}'; +import { enhance } from '${config.enhanceLoadPath}'; + +const prisma = new PrismaClient().$extends({ + model: { + user: { + async signUp(email: string) { + return prisma.user.create({ data: { email } }); + }, + }, + }, +}); + +async function main() { + const db = enhance(prisma); + const newUser = await db.user.signUp('user@test.com') +} + +main() + `, + }, + ], + } + ); + }); + + it('works with `auth` in `@default`', async () => { + const { enhance, prisma } = await loadSchema( + ` + ${config.generator} + model User { id Int @id posts Post[] @@ -29,17 +249,17 @@ describe('New prisma-client generator tests', () => { @@allow('all', true) } `, - { - addPrelude: false, - output: './zenstack', - compile: true, - prismaLoadPath: './prisma/prisma-generated/client', - extraSourceFiles: [ - { - name: 'main.ts', - content: ` -import { PrismaClient } from './prisma/prisma-generated/client'; -import { enhance } from './zenstack/enhance'; + { + addPrelude: false, + output: config.output, + compile: true, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { PrismaClient } from '${config.prismaLoadPath}'; +import { enhance } from '${config.enhanceLoadPath}'; const prisma = new PrismaClient(); const db = enhance(prisma); @@ -51,31 +271,22 @@ async function main() { main(); `, - }, - ], - } - ); + }, + ], + } + ); - const user = await prisma.user.create({ data: { id: 1 } }); - const db = enhance({ id: user.id }); - await expect(db.post.create({ data: { id: 1, title: 'Hello World' } })).resolves.toMatchObject({ - authorId: user.id, + const user = await prisma.user.create({ data: { id: 1 } }); + const db = enhance({ id: user.id }); + await expect(db.post.create({ data: { id: 1, title: 'Hello World' } })).resolves.toMatchObject({ + authorId: user.id, + }); }); - }); - - it('works with delegate models', async () => { - const { enhance } = await loadSchema( - ` - datasource db { - provider = "sqlite" - url = "file:./dev.db" - } - generator client { - provider = "prisma-client" - output = "./prisma-generated" - moduleFormat = "cjs" - } + it('works with delegate models', async () => { + const { enhance } = await loadSchema( + ` + ${config.generator} model Asset { id Int @id @@ -88,18 +299,18 @@ main(); title String } `, - { - enhancements: ['delegate'], - addPrelude: false, - output: './zenstack', - compile: true, - prismaLoadPath: './prisma/prisma-generated/client', - extraSourceFiles: [ - { - name: 'main.ts', - content: ` -import { PrismaClient } from './prisma/prisma-generated/client'; -import { enhance } from './zenstack/enhance'; + { + enhancements: ['delegate'], + addPrelude: false, + output: config.output, + compile: true, + prismaLoadPath: config.prismaLoadPath, + extraSourceFiles: [ + { + name: 'main.ts', + content: ` +import { PrismaClient } from '${config.prismaLoadPath}'; +import { enhance } from '${config.enhanceLoadPath}'; const prisma = new PrismaClient(); const db = enhance(prisma); @@ -111,14 +322,15 @@ async function main() { main(); `, - }, - ], - } - ); + }, + ], + } + ); - const db = enhance(); - await expect( - db.post.create({ data: { id: 1, name: 'Test Post', title: 'Hello World' } }) - ).resolves.toMatchObject({ id: 1, name: 'Test Post', type: 'Post', title: 'Hello World' }); + const db = enhance(); + await expect( + db.post.create({ data: { id: 1, name: 'Test Post', title: 'Hello World' } }) + ).resolves.toMatchObject({ id: 1, name: 'Test Post', type: 'Post', title: 'Hello World' }); + }); }); });