1+ import { ImportYup , YupGenerator } from "./yup/index" ;
12import { ValidationSchema , ValidationSchemaPluginConfig } from "./types" ;
23import { PluginFunction , Types } from "@graphql-codegen/plugin-helpers" ;
3- import { indent } from "@graphql-codegen/visitor-plugin-common" ;
4- import {
5- EnumTypeDefinitionNode ,
6- GraphQLSchema ,
7- InputObjectTypeDefinitionNode ,
8- InputValueDefinitionNode ,
9- ScalarTypeDefinitionNode ,
10- visit ,
11- NamedTypeNode ,
12- TypeNode ,
13- ListTypeNode ,
14- NonNullTypeNode ,
15- NameNode ,
16- } from "graphql" ;
17- import { transformSchemaAST } from "@graphql-codegen/schema-ast" ;
4+ import { GraphQLSchema } from "graphql" ;
5+ import { retrieveSchema } from "./graphql" ;
186
197export const plugin : PluginFunction < ValidationSchemaPluginConfig > = async (
208 schema : GraphQLSchema ,
@@ -26,209 +14,15 @@ export const plugin: PluginFunction<ValidationSchemaPluginConfig> = async (
2614 return {
2715 prepend : [ importSchema ( config . schema ) ] ,
2816 content : [
29- generateYupSchema ( { inputObjects, enums, scalars } ) ,
17+ new YupGenerator ( { inputObjects, enums, scalars } ) . generate ( ) ,
3018 ] . join ( "\n" ) ,
3119 } ;
3220} ;
3321
3422const importSchema = ( schema ?: ValidationSchema ) : string => {
3523 if ( schema === "yup" ) {
36- return `import * as yup from 'yup'` ;
24+ return ImportYup ( ) ;
3725 }
3826 // TODO(codehex): support zod
39- return `import * as yup from 'yup'` ;
27+ return ImportYup ( ) ;
4028} ;
41-
42- interface Nodes {
43- inputObjects : InputObjectTypeDefinitionNode [ ] ;
44- enums : Record < string , EnumTypeDefinitionNode > ;
45- scalars : Record < string , ScalarTypeDefinitionNode > ;
46- }
47-
48- const retrieveSchema = (
49- schema : GraphQLSchema ,
50- config : ValidationSchemaPluginConfig
51- ) : Nodes => {
52- const { ast } = transformSchemaAST ( schema , config ) ;
53-
54- const inputObjects : InputObjectTypeDefinitionNode [ ] = [ ] ;
55- const enums : Record < string , EnumTypeDefinitionNode > = { } ;
56- const scalars : Record < string , ScalarTypeDefinitionNode > = { } ;
57-
58- visit ( ast , {
59- leave : {
60- InputObjectTypeDefinition : ( node ) => {
61- inputObjects . unshift ( node ) ;
62- } ,
63- EnumTypeDefinition : ( node ) => {
64- if ( node . values ) {
65- enums [ node . name . value ] = node ;
66- }
67- } ,
68- ScalarTypeDefinition : ( node ) => {
69- scalars [ node . name . value ] = node ;
70- } ,
71- } ,
72- } ) ;
73-
74- return { inputObjects, enums, scalars } ;
75- } ;
76-
77- const generateYupSchema = ( { inputObjects, enums, scalars } : Nodes ) : string => {
78- return inputObjects
79- . map ( ( inputObject ) =>
80- generateInputObjectYupSchema ( { inputObject, enums, scalars } )
81- )
82- . join ( "\n\n" ) ;
83- } ;
84-
85- interface InputObjectGeneratorParams {
86- inputObject : InputObjectTypeDefinitionNode ;
87- enums : Record < string , EnumTypeDefinitionNode > ;
88- scalars : Record < string , ScalarTypeDefinitionNode > ;
89- }
90-
91- const generateInputObjectYupSchema = ( {
92- inputObject,
93- enums,
94- scalars,
95- } : InputObjectGeneratorParams ) : string => {
96- const name = inputObject . name . value ;
97- const { fields } = inputObject ;
98- if ( ! fields ) return `` ;
99-
100- const shape = fields
101- . map ( ( field ) =>
102- generateInputObjectFieldYupSchema ( {
103- field,
104- enums,
105- scalars,
106- indentCount : 2 ,
107- } )
108- )
109- . join ( ",\n" ) ;
110- return [
111- `export function ${ name } Schema(): yup.SchemaOf<${ name } > {` ,
112- indent ( `return yup.object({` ) ,
113- shape ,
114- indent ( "})" ) ,
115- `}` ,
116- ] . join ( "\n" ) ;
117- } ;
118-
119- interface InputObjectFieldGeneratorParams {
120- field : InputValueDefinitionNode ;
121- enums : Record < string , EnumTypeDefinitionNode > ;
122- scalars : Record < string , ScalarTypeDefinitionNode > ;
123- indentCount ?: number ;
124- }
125-
126- const generateInputObjectFieldYupSchema = ( {
127- field,
128- enums,
129- scalars,
130- indentCount,
131- } : InputObjectFieldGeneratorParams ) : string => {
132- // TOOD(codehex): handle directive
133- let schema = generateInputObjectFieldTypeYupSchema ( {
134- type : field . type ,
135- enums,
136- scalars,
137- } )
138- return indent (
139- `${ field . name . value } : ${ maybeLazy ( field . type , schema ) } ` ,
140- indentCount
141- ) ;
142- } ;
143-
144- interface InputObjectFieldTypeGeneratorParams {
145- type : TypeNode ;
146- enums : Record < string , EnumTypeDefinitionNode > ;
147- scalars : Record < string , ScalarTypeDefinitionNode > ;
148- }
149-
150- const generateInputObjectFieldTypeYupSchema = ( {
151- type,
152- enums,
153- scalars,
154- } : InputObjectFieldTypeGeneratorParams ) : string => {
155- if ( isListType ( type ) ) {
156- return `yup.array().of(${ generateInputObjectFieldTypeYupSchema ( {
157- type : type . type ,
158- enums,
159- scalars,
160- } ) } )`;
161- }
162- if ( isNonNullType ( type ) ) {
163- const schema = generateInputObjectFieldTypeYupSchema ( {
164- type : type . type ,
165- enums,
166- scalars,
167- } )
168- return maybeLazy ( type . type , `${ schema } .required()` ) ;
169- }
170- if ( isNamedType ( type ) ) {
171- return generateNameNodeYupSchema ( {
172- node : type . name ,
173- enums,
174- scalars,
175- } ) ;
176- }
177- console . warn ( "unhandled type:" , type ) ;
178- return "" ;
179- } ;
180-
181- interface NameNodeGeneratorParams {
182- node : NameNode ;
183- enums : Record < string , EnumTypeDefinitionNode > ;
184- scalars : Record < string , ScalarTypeDefinitionNode > ;
185- }
186-
187- const generateNameNodeYupSchema = ( {
188- node,
189- enums,
190- scalars,
191- } : NameNodeGeneratorParams ) : string => {
192- if ( isRef ( node . value ) ) {
193- return `${ node . value } Schema()` ;
194- }
195- if ( isString ( node . value ) ) {
196- return `yup.string()` ;
197- }
198- if ( isBoolean ( node . value ) ) {
199- return `yup.boolean()` ;
200- }
201- if ( isNumber ( node . value ) ) {
202- return `yup.number()` ;
203- }
204- if ( enums [ node . value ] ) {
205- const enumdef = enums [ node . value ] ;
206- return `yup.mixed().oneOf(Object.values(${ enumdef . name . value } ))` ;
207- }
208- if ( scalars [ node . value ] ) {
209- console . warn ( "unhandled scalar:" , scalars [ node . value ] ) ;
210- return "yup.mixed()" ;
211- }
212- console . warn ( "unhandled name:" , node ) ;
213- return "yup.mixed()" ;
214- } ;
215-
216- const isListType = ( typ : TypeNode ) : typ is ListTypeNode =>
217- typ . kind === "ListType" ;
218- const isNonNullType = ( typ : TypeNode ) : typ is NonNullTypeNode =>
219- typ . kind === "NonNullType" ;
220- const isNamedType = ( typ : TypeNode ) : typ is NamedTypeNode =>
221- typ . kind === "NamedType" ;
222-
223- const isRef = ( kind : string ) => kind . includes ( "Input" ) ;
224- const isBoolean = ( kind : string ) => kind === "Boolean" ;
225- const isString = ( kind : string ) => [ "ID" , "String" ] . includes ( kind ) ;
226- const isNumber = ( kind : string ) => [ "Int" , "Float" ] . includes ( kind ) ;
227-
228- const maybeLazy = ( type : TypeNode , schema : string ) : string => {
229- if ( isNamedType ( type ) && isRef ( type . name . value ) ) {
230- // https://github.com/jquense/yup/issues/1283#issuecomment-786559444
231- return `yup.lazy(() => ${ schema } ) as never` ;
232- }
233- return schema
234- }
0 commit comments