graphql-codegen-typescript-validation-schema
Version:
GraphQL Code Generator plugin to generate form validation schema from your GraphQL schema
245 lines (244 loc) • 13.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ZodSchemaVisitor = void 0;
const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common");
const graphql_js_1 = require("../graphql.js");
const schema_visitor_js_1 = require("../schema_visitor.js");
const zod_shared_js_1 = require("../zod_shared.js");
const operation_js_1 = require("./operation.js");
class ZodSchemaVisitor extends schema_visitor_js_1.BaseSchemaVisitor {
constructor(schema, config) {
super(schema, config);
}
importValidationSchema() {
const importPath = this.config.zodImportPath || 'zod';
return `import { z } from '${importPath}'`;
}
initialEmit() {
return (`\n${[
new visitor_plugin_common_1.DeclarationBlock({})
.asKind('type')
.withName('Properties<T>')
.withContent(['Required<{', ' [K in keyof T]: z.ZodType<T[K]>;', '}>'].join('\n'))
.string,
// Unfortunately, zod doesn’t provide non-null defined any schema.
// This is a temporary hack until it is fixed.
// see: https://github.com/colinhacks/zod/issues/884
new visitor_plugin_common_1.DeclarationBlock({}).asKind('type').withName('definedNonNullAny').withContent('{}').string,
new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`isDefinedNonNullAny`)
.withContent(`(v: any): v is definedNonNullAny => v !== undefined && v !== null`)
.string,
new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${zod_shared_js_1.anySchema}`)
.withContent(`z.any().refine((v) => isDefinedNonNullAny(v))`)
.string,
...this.enumDeclarations,
].join('\n')}`);
}
buildOperationDefinitions(documents) {
const visitor = this.createVisitor('output');
const result = (0, operation_js_1.buildZodOperationSchemas)(this.schema, this.config, documents, visitor);
this.importTypes.push(...result.typeNames);
return result.blocks;
}
get InputObjectTypeDefinition() {
return {
leave: (node) => {
const visitor = this.createVisitor('input');
const name = visitor.convertName(node.name.value);
this.importTypes.push(name);
if ((0, zod_shared_js_1.isOneOfInputObject)(node))
return this.buildOneOfInputFields(node.fields ?? [], visitor, name, node.description?.value);
return this.buildInputFields(node.fields ?? [], visitor, name, node.description?.value);
},
};
}
get InterfaceTypeDefinition() {
return {
leave: (0, graphql_js_1.InterfaceTypeDefinitionBuilder)(this.config.withObjectType, (node) => {
const visitor = this.createVisitor('output');
const name = visitor.convertName(node.name.value);
const typeName = visitor.prefixTypeNamespace(name);
this.importTypes.push(name);
// Building schema for field arguments.
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
const appendArguments = argumentBlocks ? `\n${argumentBlocks}` : '';
// Building schema for fields.
const shape = node.fields?.map(field => (0, zod_shared_js_1.generateFieldZodSchema)(this.config, visitor, field, 2, (0, zod_shared_js_1.schemaDepthVariable)(this.config))).join(',\n');
switch (this.config.validationSchemaExportType) {
case 'const':
return (new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${name}Schema: z.ZodObject<Properties<${typeName}>>`)
.withContent((0, zod_shared_js_1.buildObjectExpression)(this.config, shape, node.description?.value))
.string + appendArguments);
case 'function':
default:
return (new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(${(0, zod_shared_js_1.schemaDepthParameter)(this.config)}): z.ZodObject<Properties<${typeName}>>`)
.withBlock((0, zod_shared_js_1.buildObjectReturn)(this.config, shape, node.description?.value))
.string + appendArguments);
}
}),
};
}
get ObjectTypeDefinition() {
return {
leave: (0, graphql_js_1.ObjectTypeDefinitionBuilder)(this.config.withObjectType, (node) => {
const visitor = this.createVisitor('output');
const name = visitor.convertName(node.name.value);
const typeName = visitor.prefixTypeNamespace(name);
this.importTypes.push(name);
// Building schema for field arguments.
const argumentBlocks = this.buildTypeDefinitionArguments(node, visitor);
const appendArguments = argumentBlocks ? `\n${argumentBlocks}` : '';
// Building schema for fields.
const shape = node.fields?.map(field => (0, zod_shared_js_1.generateFieldZodSchema)(this.config, visitor, field, 2, (0, zod_shared_js_1.schemaDepthVariable)(this.config))).join(',\n');
switch (this.config.validationSchemaExportType) {
case 'const':
return (new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${name}Schema: z.ZodObject<Properties<${typeName}>>`)
.withContent((0, zod_shared_js_1.buildObjectExpression)(this.config, [(0, visitor_plugin_common_1.indent)(`__typename: z.literal('${node.name.value}').optional(),`, 2), shape].join('\n'), node.description?.value))
.string + appendArguments);
case 'function':
default:
return (new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(${(0, zod_shared_js_1.schemaDepthParameter)(this.config)}): z.ZodObject<Properties<${typeName}>>`)
.withBlock((0, zod_shared_js_1.buildObjectReturn)(this.config, [(0, visitor_plugin_common_1.indent)(`__typename: z.literal('${node.name.value}').optional(),`, 2), shape].join('\n'), node.description?.value))
.string + appendArguments);
}
}),
};
}
get EnumTypeDefinition() {
return {
leave: (node) => {
const visitor = this.createVisitor('both');
const enumname = visitor.convertSchemaName(node.name.value, node.kind);
const enumTypeName = visitor.prefixTypeNamespace(enumname);
this.importTypes.push(enumname);
if (!this.config.enumsAsTypes)
this.importValueTypes.push(enumname);
const enumValues = node.values?.map(enumOption => enumOption.name.value) ?? [];
// hoist enum declarations
this.enumDeclarations.push(this.config.enumsAsTypes
? new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${enumname}Schema: z.ZodType<${(0, zod_shared_js_1.unionLiterals)(enumValues)}>`)
.withContent(`z.enum([${enumValues.map(enumOption => `'${enumOption}'`).join(', ')}])`)
.string
: new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${enumname}Schema: z.ZodType<${enumTypeName}>`)
.withContent(`z.nativeEnum(${enumTypeName})`)
.string);
},
};
}
get UnionTypeDefinition() {
return {
leave: (node) => {
if (!node.types || !this.config.withObjectType)
return;
const visitor = this.createVisitor('output');
const unionName = visitor.convertName(node.name.value);
const depthVariable = (0, zod_shared_js_1.schemaDepthVariable)(this.config);
const unionElements = node.types.map((t) => {
const element = visitor.convertName(t.name.value);
const typ = visitor.getType(t.name.value);
if (typ?.astNode?.kind === 'EnumTypeDefinition')
return `${element}Schema`;
switch (this.config.validationSchemaExportType) {
case 'const':
return `${element}Schema`;
case 'function':
default:
return depthVariable ? `${element}Schema(depth)` : `${element}Schema()`;
}
}).join(', ');
const unionElementsCount = node.types.length ?? 0;
const union = unionElementsCount > 1 ? `z.union([${unionElements}])` : unionElements;
switch (this.config.validationSchemaExportType) {
case 'const':
return new visitor_plugin_common_1.DeclarationBlock({}).export().asKind('const').withName(`${unionName}Schema`).withContent(union).string;
case 'function':
default:
return new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('function')
.withName(`${unionName}Schema(${(0, zod_shared_js_1.schemaDepthParameter)(this.config)})`)
.withBlock((0, visitor_plugin_common_1.indent)(`return ${union}`))
.string;
}
},
};
}
buildInputFields(fields, visitor, name, description) {
const typeName = visitor.prefixTypeNamespace(name);
const shape = fields.map(field => (0, zod_shared_js_1.generateFieldZodSchema)(this.config, visitor, field, 2)).join(',\n');
const objectSchema = (0, zod_shared_js_1.buildObjectExpression)(this.config, shape, description);
switch (this.config.validationSchemaExportType) {
case 'const':
return new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${name}Schema: z.ZodObject<Properties<${typeName}>>`)
.withContent(objectSchema)
.string;
case 'function':
default:
return new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(): z.ZodObject<Properties<${typeName}>>`)
.withBlock((0, zod_shared_js_1.buildObjectReturn)(this.config, shape, description))
.string;
}
}
buildOneOfInputFields(fields, visitor, name, description) {
const typeName = visitor.prefixTypeNamespace(name);
const variants = fields.map((selectedField) => {
const shape = fields.map((field) => {
if (field === selectedField) {
const gen = (0, zod_shared_js_1.generateFieldTypeZodSchema)(this.config, visitor, field, field.type, undefined, true, true);
return (0, visitor_plugin_common_1.indent)(`${field.name.value}: ${(0, zod_shared_js_1.withDescription)(this.config, field, (0, zod_shared_js_1.maybeLazy)(visitor, field.type, gen))}`, 2);
}
return (0, visitor_plugin_common_1.indent)(`${field.name.value}: z.never().optional()`, 2);
}).join(',\n');
return (0, zod_shared_js_1.buildObjectExpression)(this.config, shape);
});
const schema = (0, zod_shared_js_1.withTypeDescription)(this.config, description, variants.length > 1 ? `z.union([\n${variants.map(variant => (0, visitor_plugin_common_1.indent)(variant, 2)).join(',\n')}\n])` : variants[0]);
switch (this.config.validationSchemaExportType) {
case 'const':
return new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('const')
.withName(`${name}Schema: z.ZodType<${typeName}>`)
.withContent(schema)
.string;
case 'function':
default:
return new visitor_plugin_common_1.DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(): z.ZodType<${typeName}>`)
.withBlock((0, visitor_plugin_common_1.indent)(`return ${schema}`))
.string;
}
}
}
exports.ZodSchemaVisitor = ZodSchemaVisitor;