UNPKG

graphql-codegen-typescript-validation-schema

Version:

GraphQL Code Generator plugin to generate form validation schema from your GraphQL schema

317 lines (316 loc) 15.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.YupSchemaVisitor = void 0; const plugin_helpers_1 = require("@graphql-codegen/plugin-helpers"); const visitor_plugin_common_1 = require("@graphql-codegen/visitor-plugin-common"); const graphql_1 = require("graphql"); const directive_js_1 = require("../directive.js"); const graphql_js_1 = require("../graphql.js"); const schema_visitor_js_1 = require("../schema_visitor.js"); class YupSchemaVisitor extends schema_visitor_js_1.BaseSchemaVisitor { constructor(schema, config) { super(schema, config); } importValidationSchema() { return `import * as yup from 'yup'`; } initialEmit() { if (!this.config.withObjectType) return `\n${this.enumDeclarations.join('\n')}`; return (`\n${this.enumDeclarations.join('\n')}\n${new visitor_plugin_common_1.DeclarationBlock({}) .asKind('function') .withName('union<T extends {}>(...schemas: ReadonlyArray<yup.Schema<T>>): yup.MixedSchema<T>') .withBlock([ (0, visitor_plugin_common_1.indent)('return yup.mixed<T>().test({'), (0, visitor_plugin_common_1.indent)('test: (value) => schemas.some((schema) => schema.isValidSync(value))', 2), (0, visitor_plugin_common_1.indent)('}).defined()'), ].join('\n')) .string}`); } get InputObjectTypeDefinition() { return { leave: (node) => { const visitor = this.createVisitor('input'); const name = visitor.convertName(node.name.value); this.importTypes.push(name); return this.buildInputFields(node.fields ?? [], visitor, name); }, }; } 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) => { const fieldSchema = generateFieldYupSchema(this.config, visitor, field, 2); return (0, graphql_js_1.isNonNullType)(field.type) ? fieldSchema : `${fieldSchema}.optional()`; }).join(',\n'); switch (this.config.validationSchemaExportType) { case 'const': return (new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${name}Schema: yup.ObjectSchema<${typeName}>`) .withContent([`yup.object({`, shape, '})'].join('\n')) .string + appendArguments); case 'function': default: return (new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('function') .withName(`${name}Schema(): yup.ObjectSchema<${typeName}>`) .withBlock([(0, visitor_plugin_common_1.indent)(`return yup.object({`), shape, (0, visitor_plugin_common_1.indent)('})')].join('\n')) .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 = shapeFields(node.fields, this.config, visitor); switch (this.config.validationSchemaExportType) { case 'const': return (new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${name}Schema: yup.ObjectSchema<${typeName}>`) .withContent([ `yup.object({`, (0, visitor_plugin_common_1.indent)(`__typename: yup.string<'${node.name.value}'>().optional(),`, 2), shape, '})', ].join('\n')) .string + appendArguments); case 'function': default: return (new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('function') .withName(`${name}Schema(): yup.ObjectSchema<${typeName}>`) .withBlock([ (0, visitor_plugin_common_1.indent)(`return yup.object({`), (0, visitor_plugin_common_1.indent)(`__typename: yup.string<'${node.name.value}'>().optional(),`, 2), shape, (0, visitor_plugin_common_1.indent)('})'), ].join('\n')) .string + appendArguments); } }), }; } get EnumTypeDefinition() { return { leave: (node) => { const visitor = this.createVisitor('both'); const enumname = visitor.convertName(node.name.value); const enumTypeName = visitor.prefixTypeNamespace(enumname); this.importTypes.push(enumname); // hoise enum declarations if (this.config.enumsAsTypes) { const enums = node.values?.map(enumOption => `'${enumOption.name.value}'`); this.enumDeclarations.push(new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${enumname}Schema`) .withContent(`yup.string().oneOf([${enums?.join(', ')}]).defined()`).string); } else { this.enumDeclarations.push(new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${enumname}Schema`) .withContent(`yup.string<${enumTypeName}>().oneOf(Object.values(${enumTypeName})).defined()`).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 unionTypeName = visitor.prefixTypeNamespace(unionName); this.importTypes.push(unionName); 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 `${element}Schema()`; } }).join(', '); switch (this.config.validationSchemaExportType) { case 'const': return new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${unionName}Schema: yup.MixedSchema<${unionTypeName}>`) .withContent(`union<${unionTypeName}>(${unionElements})`) .string; case 'function': default: return new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('function') .withName(`${unionName}Schema(): yup.MixedSchema<${unionTypeName}>`) .withBlock((0, visitor_plugin_common_1.indent)(`return union<${unionTypeName}>(${unionElements})`)) .string; } }, }; } buildInputFields(fields, visitor, name) { const typeName = visitor.prefixTypeNamespace(name); const shape = shapeFields(fields, this.config, visitor); switch (this.config.validationSchemaExportType) { case 'const': return new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('const') .withName(`${name}Schema: yup.ObjectSchema<${typeName}>`) .withContent(['yup.object({', shape, '})'].join('\n')) .string; case 'function': default: return new visitor_plugin_common_1.DeclarationBlock({}) .export() .asKind('function') .withName(`${name}Schema(): yup.ObjectSchema<${typeName}>`) .withBlock([(0, visitor_plugin_common_1.indent)(`return yup.object({`), shape, (0, visitor_plugin_common_1.indent)('})')].join('\n')) .string; } } } exports.YupSchemaVisitor = YupSchemaVisitor; function shapeFields(fields, config, visitor) { return fields ?.map((field) => { let fieldSchema = generateFieldYupSchema(config, visitor, field, 2); if (field.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION) { const { defaultValue } = field; if (defaultValue?.kind === graphql_1.Kind.INT || defaultValue?.kind === graphql_1.Kind.FLOAT || defaultValue?.kind === graphql_1.Kind.BOOLEAN) { fieldSchema = `${fieldSchema}.default(${defaultValue.value})`; } if (defaultValue?.kind === graphql_1.Kind.STRING || defaultValue?.kind === graphql_1.Kind.ENUM) { if (config.useEnumTypeAsDefaultValue && defaultValue?.kind !== graphql_1.Kind.STRING) { let value = (0, visitor_plugin_common_1.convertNameParts)(defaultValue.value, (0, plugin_helpers_1.resolveExternalModuleAndFn)('change-case-all#pascalCase'), config?.namingConvention?.transformUnderscore); if (config.namingConvention?.enumValues) value = (0, visitor_plugin_common_1.convertNameParts)(defaultValue.value, (0, plugin_helpers_1.resolveExternalModuleAndFn)(config.namingConvention?.enumValues), config?.namingConvention?.transformUnderscore); fieldSchema = `${fieldSchema}.default(${visitor.convertName(field.name.value)}.${value})`; } else { fieldSchema = `${fieldSchema}.default("${(0, graphql_js_1.escapeGraphQLCharacters)(defaultValue.value)}")`; } } } if ((0, graphql_js_1.isNonNullType)(field.type)) return fieldSchema; return `${fieldSchema}.optional()`; }) .join(',\n'); } function generateFieldYupSchema(config, visitor, field, indentCount) { let gen = generateFieldTypeYupSchema(config, visitor, field.type); if (config.directives && field.directives) { const formatted = (0, directive_js_1.formatDirectiveConfig)(config.directives); gen += (0, directive_js_1.buildApi)(formatted, field.directives); } return (0, visitor_plugin_common_1.indent)(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount); } function generateFieldTypeYupSchema(config, visitor, type, parentType) { if ((0, graphql_js_1.isListType)(type)) { const gen = generateFieldTypeYupSchema(config, visitor, type.type, type); if (!(0, graphql_js_1.isNonNullType)(parentType)) return `yup.array(${maybeLazy(type.type, gen)}).defined().nullable()`; return `yup.array(${maybeLazy(type.type, gen)}).defined()`; } if ((0, graphql_js_1.isNonNullType)(type)) { const gen = generateFieldTypeYupSchema(config, visitor, type.type, type); return maybeLazy(type.type, gen); } if ((0, graphql_js_1.isNamedType)(type)) { const gen = generateNameNodeYupSchema(config, visitor, type.name); if ((0, graphql_js_1.isNonNullType)(parentType)) { if (visitor.shouldEmitAsNotAllowEmptyString(type.name.value)) return `${gen}.required()`; return `${gen}.nonNullable()`; } const typ = visitor.getType(type.name.value); if (typ?.astNode?.kind === 'InputObjectTypeDefinition') return `${gen}`; return `${gen}.nullable()`; } console.warn('unhandled type:', type); return ''; } function generateNameNodeYupSchema(config, visitor, node) { const converter = visitor.getNameNodeConverter(node); switch (converter?.targetKind) { case 'InterfaceTypeDefinition': case 'InputObjectTypeDefinition': case 'ObjectTypeDefinition': case 'UnionTypeDefinition': // using switch-case rather than if-else to allow for future expansion switch (config.validationSchemaExportType) { case 'const': return `${converter.convertName()}Schema`; case 'function': default: return `${converter.convertName()}Schema()`; } case 'EnumTypeDefinition': return `${converter.convertName()}Schema`; default: return yup4Scalar(config, visitor, node.value); } } function maybeLazy(type, schema) { if ((0, graphql_js_1.isNamedType)(type) && (0, graphql_js_1.isInput)(type.name.value)) { // https://github.com/jquense/yup/issues/1283#issuecomment-786559444 return `yup.lazy(() => ${schema})`; } return schema; } function yup4Scalar(config, visitor, scalarName) { if (config.scalarSchemas?.[scalarName]) return `${config.scalarSchemas[scalarName]}.defined()`; const tsType = visitor.getScalarType(scalarName); switch (tsType) { case 'string': return `yup.string().defined()`; case 'number': return `yup.number().defined()`; case 'boolean': return `yup.boolean().defined()`; } if (config.defaultScalarTypeSchema) { return config.defaultScalarTypeSchema; } console.warn('unhandled name:', scalarName); return `yup.mixed()`; }