@@ -7,10 +7,36 @@ import {
77 GraphQLObjectType ,
88 GraphQLScalarType ,
99 Kind ,
10+ type ConstDirectiveNode ,
1011 type GraphQLNamedType ,
1112 type SelectionSetNode ,
1213} from "graphql" ;
1314
15+ // TODO: get some of these directives added to the schema
16+ type DirectivesConfig = {
17+ nonnegative : boolean ;
18+ } ;
19+
20+ function parseDirectives (
21+ directives : readonly ConstDirectiveNode [ ] ,
22+ ) : DirectivesConfig {
23+ const res = { nonnegative : false } ;
24+ for ( const directive of directives ) {
25+ switch ( directive . name . value ) {
26+ case "nonnegative" : {
27+ invariant ( ! directive . arguments ?. length , "No arguments allowed!" ) ;
28+ res . nonnegative = true ;
29+ break ;
30+ }
31+ default : {
32+ throw new Error ( "Unsupported directive " + directive . name . value ) ;
33+ }
34+ }
35+ }
36+
37+ return res ;
38+ }
39+
1440class ZodOutput {
1541 constructor (
1642 public text : string ,
@@ -34,19 +60,29 @@ class ZodOutput {
3460function generateZod (
3561 currentType : GraphQLNamedType ,
3662 selectionSet : SelectionSetNode | undefined ,
63+ directives : readonly ConstDirectiveNode [ ] = [ ] ,
3764) : ZodOutput {
3865 if ( currentType instanceof GraphQLScalarType ) {
3966 switch ( currentType . name ) {
4067 case "Boolean" : {
68+ invariant ( directives . length === 0 , "Directives not supported here." ) ;
4169 return new ZodOutput ( "z.boolean()" , true ) ;
4270 }
4371 case "Int" : {
44- return new ZodOutput ( "z.number().int()" , true ) ;
72+ const directiveConfig = parseDirectives ( directives ) ;
73+ return new ZodOutput (
74+ [
75+ "z.number().int()" ,
76+ directiveConfig . nonnegative ? ".nonnegative()" : "" ,
77+ ] . join ( "" ) ,
78+ true ,
79+ ) ;
4580 }
4681 case "Date" :
4782 case "DateTime" :
4883 case "ID" :
4984 case "String" : {
85+ invariant ( directives . length === 0 , "Directives not supported here." ) ;
5086 return new ZodOutput ( "z.string()" , true ) ;
5187 }
5288 default : {
@@ -56,37 +92,46 @@ function generateZod(
5692 }
5793
5894 if ( currentType instanceof GraphQLObjectType ) {
95+ invariant ( directives . length === 0 , "Directives not supported here." ) ;
96+
5997 const selections = nullthrows ( selectionSet ) . selections . map ( ( s ) => {
6098 invariant ( s . kind === Kind . FIELD , "Expected direct fields!" ) ;
6199
62100 const processStack : ( ( prev : ZodOutput ) => ZodOutput ) [ ] = [ ] ;
63101
64- let fieldType = currentType . getFields ( ) [ s . name . value ] . type ;
65- while (
66- fieldType instanceof GraphQLList ||
67- fieldType instanceof GraphQLNonNull
68- ) {
102+ const field = currentType . getFields ( ) [ s . name . value ] ;
103+ let fieldType = field . type ;
104+
105+ while ( true ) {
69106 if ( fieldType instanceof GraphQLList ) {
70107 processStack . push (
71108 ( prev ) => new ZodOutput ( `z.array(${ prev . stringify ( false ) } )` , true ) ,
72109 ) ;
73- } else {
110+ fieldType = fieldType . ofType ;
111+ continue ;
112+ }
113+
114+ if ( fieldType instanceof GraphQLNonNull ) {
74115 processStack . push ( ( prev ) => prev . setIsNullable ( false ) ) ;
116+ fieldType = fieldType . ofType ;
117+ continue ;
75118 }
76119
77- fieldType = fieldType . ofType ;
120+ return (
121+ JSON . stringify ( s . name . value ) +
122+ ": " +
123+ processStack
124+ . reduceRight (
125+ ( acc , fn ) => fn ( acc ) ,
126+ generateZod (
127+ fieldType ,
128+ s . selectionSet ,
129+ field . astNode ?. directives ?? [ ] ,
130+ ) ,
131+ )
132+ . stringify ( )
133+ ) ;
78134 }
79-
80- return (
81- JSON . stringify ( s . name . value ) +
82- ": " +
83- processStack
84- . reduceRight (
85- ( acc , fn ) => fn ( acc ) ,
86- generateZod ( fieldType , s . selectionSet ) ,
87- )
88- . stringify ( )
89- ) ;
90135 } ) ;
91136
92137 return new ZodOutput ( `z.object({${ selections . join ( "," ) } })` , true ) ;
0 commit comments