Skip to content

Commit 63b6a11

Browse files
committed
Add support for unions in yup
1 parent 510d990 commit 63b6a11

File tree

2 files changed

+128
-1
lines changed

2 files changed

+128
-1
lines changed

src/yup/index.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
EnumTypeDefinitionNode,
1010
ObjectTypeDefinitionNode,
1111
FieldDefinitionNode,
12+
UnionTypeDefinitionNode,
1213
} from 'graphql';
1314
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
1415
import { TsVisitor } from '@graphql-codegen/typescript';
@@ -28,7 +29,17 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
2829
}
2930
return [importYup];
3031
},
31-
initialEmit: (): string => '',
32+
initialEmit: (): string =>
33+
new DeclarationBlock({})
34+
.asKind('function')
35+
.withName('union<T>(...schemas: ReadonlyArray<yup.SchemaOf<T>>): yup.BaseSchema<T>')
36+
.withBlock(
37+
[
38+
indent('return yup.mixed().test({'),
39+
indent('test: (value) => schemas.some((schema) => schema.isValidSync(value))', 2),
40+
indent('})'),
41+
].join('\n')
42+
).string,
3243
InputObjectTypeDefinition: (node: InputObjectTypeDefinitionNode) => {
3344
const name = tsVisitor.convertName(node.name.value);
3445
importTypes.push(name);
@@ -89,6 +100,24 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
89100
.withName(`${enumname}Schema`)
90101
.withContent(`yup.mixed().oneOf([${values}])`).string;
91102
},
103+
UnionTypeDefinition: (node: UnionTypeDefinitionNode) => {
104+
const unionName = tsVisitor.convertName(node.name.value);
105+
const unionElements = node.types?.map(t => `${tsVisitor.convertName(t.name.value)}Schema()`).join(', ');
106+
const unionElementsCount = node.types?.length ?? 0;
107+
108+
const union =
109+
unionElementsCount > 1
110+
? indent(`return union<${unionName}>(${unionElements})`)
111+
: indent(`return ${unionElements}`);
112+
113+
const result = new DeclarationBlock({})
114+
.export()
115+
.asKind('function')
116+
.withName(`${unionName}Schema(): yup.BaseSchema<${unionName}>`)
117+
.withBlock(union);
118+
119+
return result.string;
120+
},
92121
// ScalarTypeDefinition: (node) => {
93122
// const decl = new DeclarationBlock({})
94123
// .export()

tests/yup.spec.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,104 @@ describe('yup', () => {
451451
expect(result.content).not.toContain(wantNotContain);
452452
}
453453
});
454+
455+
it('generate union types', async () => {
456+
const schema = buildSchema(/* GraphQL */ `
457+
type Square {
458+
size: Int
459+
}
460+
type Circle {
461+
radius: Int
462+
}
463+
union Shape = Circle | Square
464+
`);
465+
466+
const result = await plugin(
467+
schema,
468+
[],
469+
{
470+
schema: 'yup',
471+
withObjectType: true,
472+
},
473+
{}
474+
);
475+
476+
const wantContains = [
477+
// Shape Schema
478+
'export function ShapeSchema(): yup.BaseSchema<Shape> {',
479+
'union<Shape>(CircleSchema(), SquareSchema())',
480+
'}',
481+
];
482+
for (const wantContain of wantContains) {
483+
expect(result.content).toContain(wantContain);
484+
}
485+
});
486+
487+
it('generate union types with single element', async () => {
488+
const schema = buildSchema(/* GraphQL */ `
489+
type Square {
490+
size: Int
491+
}
492+
type Circle {
493+
radius: Int
494+
}
495+
union Shape = Circle | Square
496+
497+
type Geometry {
498+
shape: Shape
499+
}
500+
`);
501+
502+
const result = await plugin(
503+
schema,
504+
[],
505+
{
506+
schema: 'yup',
507+
withObjectType: true,
508+
},
509+
{}
510+
);
511+
512+
const wantContains = [
513+
'export function GeometrySchema(): yup.SchemaOf<Geometry> {',
514+
'return yup.object({',
515+
"__typename: yup.mixed().oneOf(['Geometry', undefined]),",
516+
'shape: yup.mixed()',
517+
'})',
518+
];
519+
for (const wantContain of wantContains) {
520+
expect(result.content).toContain(wantContain);
521+
}
522+
});
523+
524+
it('correctly reference generated union types', async () => {
525+
const schema = buildSchema(/* GraphQL */ `
526+
type Circle {
527+
radius: Int
528+
}
529+
union Shape = Circle
530+
`);
531+
532+
const result = await plugin(
533+
schema,
534+
[],
535+
{
536+
schema: 'yup',
537+
withObjectType: true,
538+
},
539+
{}
540+
);
541+
542+
const wantContains = [
543+
// Shape Schema
544+
'export function ShapeSchema(): yup.BaseSchema<Shape> {',
545+
'CircleSchema()',
546+
'}',
547+
];
548+
for (const wantContain of wantContains) {
549+
expect(result.content).toContain(wantContain);
550+
}
551+
});
454552
});
455553

456554
it('properly generates custom directive values', async () => {

0 commit comments

Comments
 (0)