Skip to content

Commit 68204e8

Browse files
committed
feat: support directives
1 parent 0efd767 commit 68204e8

File tree

2 files changed

+97
-11
lines changed

2 files changed

+97
-11
lines changed

src/valibot/index.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
2+
import type {
3+
EnumTypeDefinitionNode,
4+
FieldDefinitionNode,
5+
GraphQLSchema,
6+
InputObjectTypeDefinitionNode,
7+
InputValueDefinitionNode,
8+
NameNode,
9+
TypeNode,
10+
} from 'graphql';
211
import {
312
Kind,
4-
type EnumTypeDefinitionNode,
5-
type FieldDefinitionNode,
6-
type GraphQLSchema,
7-
type InputObjectTypeDefinitionNode,
8-
type InputValueDefinitionNode,
9-
type NameNode,
10-
type TypeNode,
1113
} from 'graphql';
1214

1315
import type { ValidationSchemaPluginConfig } from '../config';
16+
import { buildApiForValibot, formatDirectiveConfig } from '../directive';
1417
import { BaseSchemaVisitor } from '../schema_visitor';
1518
import type { Visitor } from '../visitor';
1619
import {
@@ -118,28 +121,38 @@ function generateFieldTypeValibotSchema(config: ValidationSchemaPluginConfig, vi
118121
if (isListType(parentType))
119122
return `v.nullable(${gen})`;
120123

124+
let appliedDirectivesGen = applyDirectives(config, field, gen);
125+
121126
if (field.kind === Kind.INPUT_VALUE_DEFINITION) {
122127
const { defaultValue } = field;
123128
if (defaultValue?.kind === Kind.INT || defaultValue?.kind === Kind.FLOAT || defaultValue?.kind === Kind.BOOLEAN)
124-
gen = `v.optional(${gen}, ${defaultValue.value})`;
129+
appliedDirectivesGen = `v.optional(${appliedDirectivesGen}, ${defaultValue.value})`;
125130

126131
if (defaultValue?.kind === Kind.STRING || defaultValue?.kind === Kind.ENUM)
127-
gen = `v.optional(${gen}, "${defaultValue.value}")`;
132+
appliedDirectivesGen = `v.optional(${appliedDirectivesGen}, "${defaultValue.value}")`;
128133

129134
}
130135
if (isNonNullType(parentType)) {
131136
if (visitor.shouldEmitAsNotAllowEmptyString(type.name.value))
132137
return "v.string([v.minLength(1)])"; // TODO
133138

134-
return gen;
139+
return appliedDirectivesGen;
135140
}
136141

137-
return `v.nullish(${gen})`;
142+
return `v.nullish(${appliedDirectivesGen})`;
138143
}
139144
console.warn('unhandled type:', type);
140145
return '';
141146
}
142147

148+
function applyDirectives(config: ValidationSchemaPluginConfig, field: InputValueDefinitionNode | FieldDefinitionNode, gen: string): string {
149+
if (config.directives && field.directives) {
150+
const formatted = formatDirectiveConfig(config.directives);
151+
return `v.pipe(${gen}, ${buildApiForValibot(formatted, field.directives).join(', ')})`;
152+
}
153+
return gen;
154+
}
155+
143156
function generateNameNodeValibotSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, node: NameNode): string {
144157
const converter = visitor.getNameNodeConverter(node);
145158

tests/valibot.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,4 +366,77 @@ describe('valibot', () => {
366366
"
367367
`)
368368
});
369+
370+
describe('issues #19', () => {
371+
it('string field', async () => {
372+
const schema = buildSchema(/* GraphQL */ `
373+
input UserCreateInput {
374+
profile: String @constraint(minLength: 1, maxLength: 5000)
375+
}
376+
377+
directive @constraint(minLength: Int!, maxLength: Int!) on INPUT_FIELD_DEFINITION
378+
`);
379+
const result = await plugin(
380+
schema,
381+
[],
382+
{
383+
schema: 'valibot',
384+
directives: {
385+
constraint: {
386+
minLength: ['minLength', '$1', 'Please input more than $1'],
387+
maxLength: ['maxLength', '$1', 'Please input less than $1'],
388+
},
389+
},
390+
},
391+
{},
392+
);
393+
expect(result.content).toMatchInlineSnapshot(`
394+
"
395+
396+
export function UserCreateInputSchema() {
397+
return v.object({
398+
profile: v.nullish(v.pipe(v.string(), v.minLength(1, "Please input more than 1"), v.maxLength(5000, "Please input less than 5000")))
399+
})
400+
}
401+
"
402+
`)
403+
});
404+
405+
it('not null field', async () => {
406+
const schema = buildSchema(/* GraphQL */ `
407+
input UserCreateInput {
408+
profile: String! @constraint(minLength: 1, maxLength: 5000)
409+
}
410+
411+
directive @constraint(minLength: Int!, maxLength: Int!) on INPUT_FIELD_DEFINITION
412+
`);
413+
const result = await plugin(
414+
schema,
415+
[],
416+
{
417+
schema: 'valibot',
418+
directives: {
419+
constraint: {
420+
minLength: ['minLength', '$1', 'Please input more than $1'],
421+
maxLength: ['maxLength', '$1', 'Please input less than $1'],
422+
},
423+
},
424+
},
425+
{},
426+
);
427+
428+
expect(result.content).toMatchInlineSnapshot(`
429+
"
430+
431+
export function UserCreateInputSchema() {
432+
return v.object({
433+
profile: v.pipe(v.string(), v.minLength(1, "Please input more than 1"), v.maxLength(5000, "Please input less than 5000"))
434+
})
435+
}
436+
"
437+
`)
438+
});
439+
440+
it.todo('list field');
441+
})
369442
})

0 commit comments

Comments
 (0)