Skip to content

Commit fa51854

Browse files
committed
added yup directory
1 parent eb9dc91 commit fa51854

File tree

4 files changed

+204
-224
lines changed

4 files changed

+204
-224
lines changed

src/graphql.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import {
2+
ListTypeNode,
3+
NonNullTypeNode,
4+
NamedTypeNode,
5+
TypeNode,
6+
GraphQLSchema,
7+
InputObjectTypeDefinitionNode,
8+
EnumTypeDefinitionNode,
9+
ScalarTypeDefinitionNode,
10+
visit,
11+
} from "graphql";
12+
import { transformSchemaAST } from "@graphql-codegen/schema-ast";
13+
import { Nodes, ValidationSchemaPluginConfig } from "./types";
14+
15+
export const isListType = (typ: TypeNode): typ is ListTypeNode =>
16+
typ.kind === "ListType";
17+
export const isNonNullType = (typ: TypeNode): typ is NonNullTypeNode =>
18+
typ.kind === "NonNullType";
19+
export const isNamedType = (typ: TypeNode): typ is NamedTypeNode =>
20+
typ.kind === "NamedType";
21+
22+
export const isRef = (kind: string) => kind.includes("Input");
23+
export const isBoolean = (kind: string) => kind === "Boolean";
24+
export const isString = (kind: string) => ["ID", "String"].includes(kind);
25+
export const isNumber = (kind: string) => ["Int", "Float"].includes(kind);
26+
27+
28+
export const retrieveSchema = (
29+
schema: GraphQLSchema,
30+
config: ValidationSchemaPluginConfig
31+
): Nodes => {
32+
const { ast } = transformSchemaAST(schema, config);
33+
34+
const inputObjects: InputObjectTypeDefinitionNode[] = [];
35+
const enums: Record<string, EnumTypeDefinitionNode> = {};
36+
const scalars: Record<string, ScalarTypeDefinitionNode> = {};
37+
38+
visit(ast, {
39+
leave: {
40+
InputObjectTypeDefinition: (node) => {
41+
inputObjects.unshift(node);
42+
},
43+
EnumTypeDefinition: (node) => {
44+
if (node.values) {
45+
enums[node.name.value] = node;
46+
}
47+
},
48+
ScalarTypeDefinition: (node) => {
49+
scalars[node.name.value] = node;
50+
},
51+
},
52+
});
53+
54+
return { inputObjects, enums, scalars };
55+
};

src/index.ts

Lines changed: 6 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,8 @@
1+
import { ImportYup, YupGenerator } from "./yup/index";
12
import { ValidationSchema, ValidationSchemaPluginConfig } from "./types";
23
import { 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

197
export 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

3422
const 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-
}

src/types.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,9 @@
1-
import { TypeScriptPluginConfig } from '@graphql-codegen/typescript';
2-
3-
// THIS ARE TYPES IN THE CONTEXT OF THE FIELD
4-
export const TYPE_LIST = "ListType";
5-
export const TYPE_NONULL = "NonNullType";
6-
export const TYPE_NAMED = "NamedType";
7-
8-
// THIS ARE TYPES OF FIELD VALUE
9-
export const TYPE_INPUT = "Input";
10-
export const TYPE_BOOLEAN = "Boolean";
11-
export const TYPE_STRINGS = ["ID", "String"];
12-
export const TYPE_NUMBERS = ["Int", "Float"];
1+
import { TypeScriptPluginConfig } from "@graphql-codegen/typescript";
2+
import {
3+
EnumTypeDefinitionNode,
4+
InputObjectTypeDefinitionNode,
5+
ScalarTypeDefinitionNode,
6+
} from "graphql";
137

148
export type ValidationSchema = "yup";
159

@@ -32,3 +26,8 @@ export interface ValidationSchemaPluginConfig extends TypeScriptPluginConfig {
3226
schema?: ValidationSchema;
3327
}
3428

29+
export interface Nodes {
30+
inputObjects: InputObjectTypeDefinitionNode[];
31+
enums: Record<string, EnumTypeDefinitionNode>;
32+
scalars: Record<string, ScalarTypeDefinitionNode>;
33+
}

0 commit comments

Comments
 (0)