5
5
getAttributeArg ,
6
6
getAuthModel ,
7
7
getDataModels ,
8
+ getLiteral ,
8
9
isDelegateModel ,
9
10
type PluginOptions ,
10
11
} from '@zenstackhq/sdk' ;
@@ -14,12 +15,14 @@ import {
14
15
ReferenceExpr ,
15
16
isArrayExpr ,
16
17
isDataModel ,
18
+ isGeneratorDecl ,
17
19
isReferenceExpr ,
18
20
type Model ,
19
21
} from '@zenstackhq/sdk/ast' ;
20
- import { getDMMF , getPrismaClientImportSpec , type DMMF } from '@zenstackhq/sdk/prisma' ;
22
+ import { getDMMF , getPrismaClientImportSpec , getPrismaVersion , type DMMF } from '@zenstackhq/sdk/prisma' ;
21
23
import fs from 'fs' ;
22
24
import path from 'path' ;
25
+ import semver from 'semver' ;
23
26
import {
24
27
FunctionDeclarationStructure ,
25
28
InterfaceDeclaration ,
@@ -42,6 +45,8 @@ import { generateAuthType } from './auth-type-generator';
42
45
// information of delegate models and their sub models
43
46
type DelegateInfo = [ DataModel , DataModel [ ] ] [ ] ;
44
47
48
+ const LOGICAL_CLIENT_GENERATION_PATH = './.logical-prisma-client' ;
49
+
45
50
export class EnhancerGenerator {
46
51
constructor (
47
52
private readonly model : Model ,
@@ -60,7 +65,7 @@ export class EnhancerGenerator {
60
65
// schema contains delegate models, need to generate a logical prisma schema
61
66
const result = await this . generateLogicalPrisma ( ) ;
62
67
63
- logicalPrismaClientDir = './.logical-prisma-client' ;
68
+ logicalPrismaClientDir = LOGICAL_CLIENT_GENERATION_PATH ;
64
69
dmmf = result . dmmf ;
65
70
66
71
// create a reexport of the logical prisma client
@@ -190,40 +195,76 @@ export function enhance(prisma: any, context?: EnhancementContext<${authTypePara
190
195
191
196
private async generateLogicalPrisma ( ) {
192
197
const prismaGenerator = new PrismaSchemaGenerator ( this . model ) ;
193
- const prismaClientOutDir = './.logical-prisma-client' ;
194
- const logicalPrismaFile = path . join ( this . outDir , 'logical.prisma' ) ;
195
- await prismaGenerator . generate ( {
196
- provider : '@internal' , // doesn't matter
197
- schemaPath : this . options . schemaPath ,
198
- output : logicalPrismaFile ,
199
- overrideClientGenerationPath : prismaClientOutDir ,
200
- mode : 'logical' ,
201
- } ) ;
202
198
203
- // generate the prisma client
204
- const generateCmd = `prisma generate --schema "${ logicalPrismaFile } " --no-engine` ;
199
+ // dir of the zmodel file
200
+ const zmodelDir = path . dirname ( this . options . schemaPath ) ;
201
+
202
+ // generate a temp logical prisma schema in zmodel's dir
203
+ const logicalPrismaFile = path . join ( zmodelDir , `logical-${ Date . now ( ) } .prisma` ) ;
204
+
205
+ // calculate a relative output path to output the logical prisma client into enhancer's output dir
206
+ const prismaClientOutDir = path . join ( path . relative ( zmodelDir , this . outDir ) , LOGICAL_CLIENT_GENERATION_PATH ) ;
205
207
try {
206
- // run 'prisma generate'
207
- await execPackage ( generateCmd , { stdio : 'ignore' } ) ;
208
- } catch {
209
- await trackPrismaSchemaError ( logicalPrismaFile ) ;
208
+ await prismaGenerator . generate ( {
209
+ provider : '@internal' , // doesn't matter
210
+ schemaPath : this . options . schemaPath ,
211
+ output : logicalPrismaFile ,
212
+ overrideClientGenerationPath : prismaClientOutDir ,
213
+ mode : 'logical' ,
214
+ } ) ;
215
+
216
+ // generate the prisma client
217
+
218
+ // only run prisma client generator for the logical schema
219
+ const prismaClientGeneratorName = this . getPrismaClientGeneratorName ( this . model ) ;
220
+ let generateCmd = `prisma generate --schema "${ logicalPrismaFile } " --generator=${ prismaClientGeneratorName } ` ;
221
+
222
+ const prismaVersion = getPrismaVersion ( ) ;
223
+ if ( ! prismaVersion || semver . gte ( prismaVersion , '5.2.0' ) ) {
224
+ // add --no-engine to reduce generation size if the prisma version supports
225
+ generateCmd += ' --no-engine' ;
226
+ }
227
+
210
228
try {
211
- // run 'prisma generate' again with output to the console
212
- await execPackage ( generateCmd ) ;
229
+ // run 'prisma generate'
230
+ await execPackage ( generateCmd , { stdio : 'ignore' } ) ;
213
231
} catch {
214
- // noop
232
+ await trackPrismaSchemaError ( logicalPrismaFile ) ;
233
+ try {
234
+ // run 'prisma generate' again with output to the console
235
+ await execPackage ( generateCmd ) ;
236
+ } catch {
237
+ // noop
238
+ }
239
+ throw new PluginError ( name , `Failed to run "prisma generate" on logical schema: ${ logicalPrismaFile } ` ) ;
215
240
}
216
- throw new PluginError ( name , `Failed to run "prisma generate" on logical schema: ${ logicalPrismaFile } ` ) ;
217
- }
218
241
219
- // make a bunch of typing fixes to the generated prisma client
220
- await this . processClientTypes ( path . join ( this . outDir , prismaClientOutDir ) ) ;
242
+ // make a bunch of typing fixes to the generated prisma client
243
+ await this . processClientTypes ( path . join ( this . outDir , LOGICAL_CLIENT_GENERATION_PATH ) ) ;
244
+
245
+ return {
246
+ prismaSchema : logicalPrismaFile ,
247
+ // load the dmmf of the logical prisma schema
248
+ dmmf : await getDMMF ( { datamodel : fs . readFileSync ( logicalPrismaFile , { encoding : 'utf-8' } ) } ) ,
249
+ } ;
250
+ } finally {
251
+ if ( fs . existsSync ( logicalPrismaFile ) ) {
252
+ fs . rmSync ( logicalPrismaFile ) ;
253
+ }
254
+ }
255
+ }
221
256
222
- return {
223
- prismaSchema : logicalPrismaFile ,
224
- // load the dmmf of the logical prisma schema
225
- dmmf : await getDMMF ( { datamodel : fs . readFileSync ( logicalPrismaFile , { encoding : 'utf-8' } ) } ) ,
226
- } ;
257
+ private getPrismaClientGeneratorName ( model : Model ) {
258
+ for ( const generator of model . declarations . filter ( isGeneratorDecl ) ) {
259
+ if (
260
+ generator . fields . some (
261
+ ( f ) => f . name === 'provider' && getLiteral < string > ( f . value ) === 'prisma-client-js'
262
+ )
263
+ ) {
264
+ return generator . name ;
265
+ }
266
+ }
267
+ throw new PluginError ( name , `Cannot find prisma-client-js generator in the schema` ) ;
227
268
}
228
269
229
270
private async processClientTypes ( prismaClientDir : string ) {
0 commit comments