11/* eslint-disable @typescript-eslint/ban-ts-comment */
2- import { indentString , isDiscriminatorField , type PluginOptions } from '@zenstackhq/sdk' ;
3- import { DataModel , Enum , isDataModel , isEnum , isTypeDef , type Model } from '@zenstackhq/sdk/ast' ;
2+ import {
3+ getForeignKeyFields ,
4+ hasAttribute ,
5+ indentString ,
6+ isDiscriminatorField ,
7+ type PluginOptions ,
8+ } from '@zenstackhq/sdk' ;
9+ import { DataModel , DataModelField , Enum , isDataModel , isEnum , isTypeDef , type Model } from '@zenstackhq/sdk/ast' ;
410import { checkModelHasModelRelation , findModelByName , isAggregateInputType } from '@zenstackhq/sdk/dmmf-helpers' ;
511import { supportCreateMany , type DMMF as PrismaDMMF } from '@zenstackhq/sdk/prisma' ;
612import path from 'path' ;
@@ -241,7 +247,8 @@ export default class Transformer {
241247 this . addSchemaImport ( inputType . type ) ;
242248 }
243249
244- result . push ( this . generatePrismaStringLine ( field , inputType , lines . length ) ) ;
250+ const contextField = contextDataModel ?. fields . find ( ( f ) => f . name === field . name ) ;
251+ result . push ( this . generatePrismaStringLine ( field , inputType , lines . length , contextField ) ) ;
245252 }
246253 }
247254
@@ -315,7 +322,12 @@ export default class Transformer {
315322 this . schemaImports . add ( upperCaseFirst ( name ) ) ;
316323 }
317324
318- generatePrismaStringLine ( field : PrismaDMMF . SchemaArg , inputType : PrismaDMMF . InputTypeRef , inputsLength : number ) {
325+ generatePrismaStringLine (
326+ field : PrismaDMMF . SchemaArg ,
327+ inputType : PrismaDMMF . InputTypeRef ,
328+ inputsLength : number ,
329+ contextField : DataModelField | undefined
330+ ) {
319331 const isEnum = inputType . location === 'enumTypes' ;
320332
321333 const { isModelQueryType, modelName, queryName } = this . checkIsModelQueryType ( inputType . type as string ) ;
@@ -330,11 +342,36 @@ export default class Transformer {
330342
331343 const arr = inputType . isList ? '.array()' : '' ;
332344
333- const opt = ! field . isRequired ? '.optional()' : '' ;
345+ const optional =
346+ ! field . isRequired ||
347+ // also check if the zmodel field infers the field as optional
348+ ( contextField && this . isFieldOptional ( contextField ) ) ;
334349
335350 return inputsLength === 1
336- ? ` ${ field . name } : z.lazy(() => ${ schema } )${ arr } ${ opt } `
337- : `z.lazy(() => ${ schema } )${ arr } ${ opt } ` ;
351+ ? ` ${ field . name } : z.lazy(() => ${ schema } )${ arr } ${ optional ? '.optional()' : '' } `
352+ : `z.lazy(() => ${ schema } )${ arr } ${ optional ? '.optional()' : '' } ` ;
353+ }
354+
355+ private isFieldOptional ( dmField : DataModelField ) {
356+ if ( hasAttribute ( dmField , '@default' ) ) {
357+ // it's possible that ZModel field has a default but it's transformed away
358+ // when generating Prisma schema, e.g.: `@default(auth().id)`
359+ return true ;
360+ }
361+
362+ if ( isDataModel ( dmField . type . reference ?. ref ) ) {
363+ // if field is a relation, we need to check if the corresponding fk field has a default
364+ // {
365+ // authorId Int @default (auth().id)
366+ // author User @relation (...) // <- author should be optional
367+ // }
368+ const fkFields = getForeignKeyFields ( dmField ) ;
369+ if ( fkFields . every ( ( fkField ) => hasAttribute ( fkField , '@default' ) ) ) {
370+ return true ;
371+ }
372+ }
373+
374+ return false ;
338375 }
339376
340377 generateFieldValidators ( zodStringWithMainType : string , field : PrismaDMMF . SchemaArg ) {
0 commit comments