Skip to content

Commit b83c0e2

Browse files
committed
feat: support interface type
1 parent 7aa54a9 commit b83c0e2

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

src/valibot/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
GraphQLSchema,
66
InputObjectTypeDefinitionNode,
77
InputValueDefinitionNode,
8+
InterfaceTypeDefinitionNode,
89
NameNode,
910
ObjectTypeDefinitionNode,
1011
TypeNode,
@@ -16,6 +17,7 @@ import { BaseSchemaVisitor } from '../schema_visitor';
1617
import type { Visitor } from '../visitor';
1718
import { buildApiForValibot, formatDirectiveConfig } from '../directive';
1819
import {
20+
InterfaceTypeDefinitionBuilder,
1921
ObjectTypeDefinitionBuilder,
2022
isInput,
2123
isListType,
@@ -51,6 +53,34 @@ export class ValibotSchemaVisitor extends BaseSchemaVisitor {
5153
};
5254
}
5355

56+
get InterfaceTypeDefinition() {
57+
return {
58+
leave: InterfaceTypeDefinitionBuilder(this.config.withObjectType, (node: InterfaceTypeDefinitionNode) => {
59+
const visitor = this.createVisitor('output');
60+
const name = visitor.convertName(node.name.value);
61+
this.importTypes.push(name);
62+
63+
// Building schema for field arguments.
64+
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
65+
const appendArguments = argumentBlocks ? `\n${argumentBlocks}` : '';
66+
67+
// Building schema for fields.
68+
const shape = node.fields?.map(field => generateFieldValibotSchema(this.config, visitor, field, 2)).join(',\n');
69+
70+
switch (this.config.validationSchemaExportType) {
71+
default:
72+
return (
73+
new DeclarationBlock({})
74+
.export()
75+
.asKind('function')
76+
.withName(`${name}Schema(): v.GenericSchema<${name}>`)
77+
.withBlock([indent(`return v.object({`), shape, indent('})')].join('\n')).string + appendArguments
78+
);
79+
}
80+
}),
81+
};
82+
}
83+
5484
get ObjectTypeDefinition() {
5585
return {
5686
leave: ObjectTypeDefinitionBuilder(this.config.withObjectType, (node: ObjectTypeDefinitionNode) => {
@@ -220,6 +250,7 @@ function generateNameNodeValibotSchema(config: ValidationSchemaPluginConfig, vis
220250
const converter = visitor.getNameNodeConverter(node);
221251

222252
switch (converter?.targetKind) {
253+
case 'InterfaceTypeDefinition':
223254
case 'InputObjectTypeDefinition':
224255
case 'ObjectTypeDefinition':
225256
case 'UnionTypeDefinition':

tests/valibot.spec.ts

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,4 +778,161 @@ describe('valibot', () => {
778778
"
779779
`)
780780
});
781+
describe('with InterfaceType', () => {
782+
it('not generate if withObjectType false', async () => {
783+
const schema = buildSchema(/* GraphQL */ `
784+
interface User {
785+
id: ID!
786+
name: String
787+
}
788+
`);
789+
const result = await plugin(
790+
schema,
791+
[],
792+
{
793+
schema: 'valibot',
794+
withObjectType: false,
795+
},
796+
{},
797+
);
798+
expect(result.content).not.toContain('export function UserSchema(): v.GenericSchema<User>');
799+
});
800+
it('generate if withObjectType true', async () => {
801+
const schema = buildSchema(/* GraphQL */ `
802+
interface Book {
803+
title: String
804+
}
805+
`);
806+
const result = await plugin(
807+
schema,
808+
[],
809+
{
810+
schema: 'valibot',
811+
withObjectType: true,
812+
},
813+
{},
814+
);
815+
expect(result.content).toMatchInlineSnapshot(`
816+
"
817+
818+
export function BookSchema(): v.GenericSchema<Book> {
819+
return v.object({
820+
title: v.nullish(v.string())
821+
})
822+
}
823+
"
824+
`)
825+
});
826+
it('generate interface type contains interface type', async () => {
827+
const schema = buildSchema(/* GraphQL */ `
828+
interface Book {
829+
author: Author
830+
title: String
831+
}
832+
833+
interface Author {
834+
books: [Book]
835+
name: String
836+
}
837+
`);
838+
const result = await plugin(
839+
schema,
840+
[],
841+
{
842+
schema: 'valibot',
843+
withObjectType: true,
844+
},
845+
{},
846+
);
847+
expect(result.content).toMatchInlineSnapshot(`
848+
"
849+
850+
export function BookSchema(): v.GenericSchema<Book> {
851+
return v.object({
852+
author: v.nullish(AuthorSchema()),
853+
title: v.nullish(v.string())
854+
})
855+
}
856+
857+
export function AuthorSchema(): v.GenericSchema<Author> {
858+
return v.object({
859+
books: v.nullish(v.array(v.nullable(BookSchema()))),
860+
name: v.nullish(v.string())
861+
})
862+
}
863+
"
864+
`)
865+
});
866+
it('generate object type contains interface type', async () => {
867+
const schema = buildSchema(/* GraphQL */ `
868+
interface Book {
869+
title: String!
870+
author: Author!
871+
}
872+
873+
type Textbook implements Book {
874+
title: String!
875+
author: Author!
876+
courses: [String!]!
877+
}
878+
879+
type ColoringBook implements Book {
880+
title: String!
881+
author: Author!
882+
colors: [String!]!
883+
}
884+
885+
type Author {
886+
books: [Book!]
887+
name: String
888+
}
889+
`);
890+
const result = await plugin(
891+
schema,
892+
[],
893+
{
894+
schema: 'valibot',
895+
withObjectType: true,
896+
},
897+
{},
898+
);
899+
expect(result.content).toMatchInlineSnapshot(`
900+
"
901+
902+
export function BookSchema(): v.GenericSchema<Book> {
903+
return v.object({
904+
title: v.string(),
905+
author: AuthorSchema()
906+
})
907+
}
908+
909+
export function TextbookSchema(): v.GenericSchema<Textbook> {
910+
return v.object({
911+
__typename: v.optional(v.literal('Textbook')),
912+
title: v.string(),
913+
author: AuthorSchema(),
914+
courses: v.array(v.string())
915+
})
916+
}
917+
918+
export function ColoringBookSchema(): v.GenericSchema<ColoringBook> {
919+
return v.object({
920+
__typename: v.optional(v.literal('ColoringBook')),
921+
title: v.string(),
922+
author: AuthorSchema(),
923+
colors: v.array(v.string())
924+
})
925+
}
926+
927+
export function AuthorSchema(): v.GenericSchema<Author> {
928+
return v.object({
929+
__typename: v.optional(v.literal('Author')),
930+
books: v.nullish(v.array(BookSchema())),
931+
name: v.nullish(v.string())
932+
})
933+
}
934+
"
935+
`)
936+
});
937+
})
781938
})

0 commit comments

Comments
 (0)