UNPKG

graphql-codegen-typescript-validation-schema

Version:

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

240 lines (239 loc) 12.3 kB
"use strict"; // Shared utilities used by both ZodSchemaVisitor (zod) and ZodV4SchemaVisitor (zodv4). // These functions are identical across both implementations and are extracted here to // eliminate duplication. Object.defineProperty(exports, "__esModule", { value: true }); exports.anySchema = void 0; exports.generateFieldZodSchema = generateFieldZodSchema; exports.generateFieldTypeZodSchema = generateFieldTypeZodSchema; exports.isOneOfInputObject = isOneOfInputObject; exports.buildObjectExpression = buildObjectExpression; exports.buildObjectReturn = buildObjectReturn; exports.strictObjectSuffix = strictObjectSuffix; exports.descriptionSuffix = descriptionSuffix; exports.withTypeDescription = withTypeDescription; exports.zodOptionalType = zodOptionalType; exports.withNullDefault = withNullDefault; exports.schemaDepthVariable = schemaDepthVariable; exports.schemaDepthParameter = schemaDepthParameter; exports.withDescription = withDescription; exports.applyDefaultValue = applyDefaultValue; exports.defaultValueExpression = defaultValueExpression; exports.hasNullDefault = hasNullDefault; exports.inputObjectFields = inputObjectFields; exports.enumDefaultTypeName = enumDefaultTypeName; exports.enumDefaultValueName = enumDefaultValueName; exports.applyDirectives = applyDirectives; exports.generateNameNodeZodSchema = generateNameNodeZodSchema; exports.maybeLazy = maybeLazy; exports.zod4Scalar = zod4Scalar; exports.unionLiterals = unionLiterals; 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 lazy_js_1 = require("./lazy.js"); const scalar_js_1 = require("./scalar.js"); exports.anySchema = `definedNonNullAnySchema`; function generateFieldZodSchema(config, visitor, field, indentCount, depthVariable) { const gen = generateFieldTypeZodSchema(config, visitor, field, field.type, undefined, true, false, depthVariable); return (0, visitor_plugin_common_1.indent)(`${field.name.value}: ${withDescription(config, field, maybeLazy(visitor, field.type, gen))}`, indentCount); } function generateFieldTypeZodSchema(config, visitor, field, type, parentType, isRoot = true, forceRequired = false, depthVariable) { if ((0, graphql_js_1.isListType)(type)) { const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type, false, false, depthVariable); const arrayGen = `z.array(${maybeLazy(visitor, type.type, gen)})`; const maybeDirectivesGen = isRoot ? applyDirectives(config, field, arrayGen) : arrayGen; const maybeDefaultGen = hasNullDefault(field) ? maybeDirectivesGen : applyDefaultValue(config, visitor, field, type, maybeDirectivesGen); if (!(0, graphql_js_1.isNonNullType)(parentType) && !forceRequired) { if (hasNullDefault(field)) return withNullDefault(config, maybeDirectivesGen); return `${maybeDefaultGen}.${zodOptionalType(config)}()`; } return maybeDefaultGen; } if ((0, graphql_js_1.isNonNullType)(type)) { const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type, isRoot, forceRequired, depthVariable); return maybeLazy(visitor, type.type, gen); } if ((0, graphql_js_1.isNamedType)(type)) { const gen = generateNameNodeZodSchema(config, visitor, type.name, depthVariable); if ((0, graphql_js_1.isListType)(parentType)) return `${gen}.nullable()`; const appliedDirectivesGen = isRoot ? hasNullDefault(field) ? applyDirectives(config, field, gen) : applyDefaultValue(config, visitor, field, type, applyDirectives(config, field, gen)) : gen; if ((0, graphql_js_1.isNonNullType)(parentType)) { if (visitor.shouldEmitAsNotAllowEmptyString(type.name.value)) return `${appliedDirectivesGen}.min(1)`; return appliedDirectivesGen; } if ((0, graphql_js_1.isListType)(parentType)) return `${appliedDirectivesGen}.nullable()`; if (forceRequired) return appliedDirectivesGen; return hasNullDefault(field) ? withNullDefault(config, appliedDirectivesGen) : `${appliedDirectivesGen}.${zodOptionalType(config)}()`; } console.warn('unhandled type:', type); return ''; } function isOneOfInputObject(node) { return node.directives?.some(directive => directive.name.value === 'oneOf') === true; } function buildObjectExpression(config, shape, description) { return ['z.object({', shape, `})${strictObjectSuffix(config)}${descriptionSuffix(config, description)}`].join('\n'); } function buildObjectReturn(config, shape, description) { return [(0, visitor_plugin_common_1.indent)('return z.object({'), shape, (0, visitor_plugin_common_1.indent)(`})${strictObjectSuffix(config)}${descriptionSuffix(config, description)}`)].join('\n'); } function strictObjectSuffix(config) { return config.strictObjectSchemas === true ? '.strict()' : ''; } function descriptionSuffix(config, description) { if (config.withDescriptions !== true || !description) return ''; return `.describe(${JSON.stringify(description)})`; } function withTypeDescription(config, description, gen) { return `${gen}${descriptionSuffix(config, description)}`; } function zodOptionalType(config) { return config.nullishBehavior ?? config.zodOptionalType ?? 'nullish'; } function withNullDefault(config, gen) { if (zodOptionalType(config) === 'optional') return `${gen}.nullable().optional().default(null)`; return `${gen}.${zodOptionalType(config)}().default(null)`; } function schemaDepthVariable(config) { return typeof config.maxDepth === 'number' && config.validationSchemaExportType !== 'const' ? 'depth' : undefined; } function schemaDepthParameter(config) { return schemaDepthVariable(config) ? 'depth = 0' : ''; } function withDescription(config, field, gen) { if (config.withDescriptions !== true || !field.description?.value) return gen; return `${gen}.describe(${JSON.stringify(field.description.value)})`; } function applyDefaultValue(config, visitor, field, type, gen) { if (field.kind !== graphql_1.Kind.INPUT_VALUE_DEFINITION || !field.defaultValue) return gen; return `${gen}.default(${defaultValueExpression(config, visitor, type, field.defaultValue)})`; } function defaultValueExpression(config, visitor, type, value) { if (value.kind === graphql_1.Kind.NULL) return 'null'; if ((0, graphql_js_1.isNonNullType)(type)) return defaultValueExpression(config, visitor, type.type, value); if ((0, graphql_js_1.isListType)(type)) { if (value.kind === graphql_1.Kind.LIST) return `[${value.values.map(item => defaultValueExpression(config, visitor, type.type, item)).join(', ')}]`; return `[${defaultValueExpression(config, visitor, type.type, value)}]`; } if ((0, graphql_js_1.isNamedType)(type) && visitor.getType(type.name.value)?.astNode?.kind === 'EnumTypeDefinition' && value.kind === graphql_1.Kind.ENUM) { if (!config.enumsAsTypes) return `${enumDefaultTypeName(visitor, type)}.${enumDefaultValueName(config, value.value)}`; return JSON.stringify(value.value); } if ((0, graphql_js_1.isNamedType)(type) && value.kind === graphql_1.Kind.OBJECT) { const graphQLType = visitor.getType(type.name.value); const astNode = graphQLType?.astNode; if (astNode?.kind === graphql_1.Kind.INPUT_OBJECT_TYPE_DEFINITION && (0, graphql_1.isInputObjectType)(graphQLType)) { const explicitFields = new Map(value.fields.map(field => [field.name.value, field.value])); const fields = inputObjectFields(astNode, graphQLType.extensionASTNodes).flatMap((field) => { const fieldValue = explicitFields.get(field.name.value) ?? field.defaultValue; if (!fieldValue) return []; return `${field.name.value}: ${defaultValueExpression(config, visitor, field.type, fieldValue)}`; }); return `{ ${fields.join(', ')} }`; } } if (value.kind === graphql_1.Kind.INT || value.kind === graphql_1.Kind.FLOAT || value.kind === graphql_1.Kind.BOOLEAN) return `${value.value}`; if (value.kind === graphql_1.Kind.STRING) return `"${(0, graphql_js_1.escapeGraphQLCharacters)(value.value)}"`; return JSON.stringify((0, graphql_1.valueFromASTUntyped)(value)); } function hasNullDefault(field) { return field.kind === graphql_1.Kind.INPUT_VALUE_DEFINITION && field.defaultValue?.kind === graphql_1.Kind.NULL; } function inputObjectFields(astNode, extensionASTNodes) { return [ ...(astNode.fields ?? []), ...(extensionASTNodes?.flatMap(extension => extension.fields ?? []) ?? []), ]; } function enumDefaultTypeName(visitor, type) { if ((0, graphql_js_1.isNonNullType)(type)) return enumDefaultTypeName(visitor, type.type); if ((0, graphql_js_1.isNamedType)(type)) return visitor.prefixTypeNamespace(visitor.convertSchemaName(type.name.value, visitor.getType(type.name.value)?.astNode?.kind)); return ''; } function enumDefaultValueName(config, value) { let enumValue = (0, visitor_plugin_common_1.convertNameParts)(value, (0, plugin_helpers_1.resolveExternalModuleAndFn)('change-case-all#pascalCase'), config.namingConvention?.transformUnderscore); if (config.namingConvention?.enumValues) enumValue = (0, visitor_plugin_common_1.convertNameParts)(value, (0, plugin_helpers_1.resolveExternalModuleAndFn)(config.namingConvention?.enumValues), config.namingConvention?.transformUnderscore); return enumValue; } function applyDirectives(config, field, gen) { if (config.directives && field.directives) { const formatted = (0, directive_js_1.formatDirectiveConfig)(config.directives); return gen + (0, directive_js_1.buildApi)(formatted, field.directives); } return gen; } function generateNameNodeZodSchema(config, visitor, node, depthVariable) { 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: if (depthVariable && (converter.targetKind === 'InterfaceTypeDefinition' || converter.targetKind === 'ObjectTypeDefinition' || converter.targetKind === 'UnionTypeDefinition')) { return `${depthVariable} >= ${config.maxDepth} ? ${exports.anySchema} : ${converter.convertName()}Schema(${depthVariable} + 1)`; } return `${converter.convertName()}Schema()`; } case 'EnumTypeDefinition': return `${converter.convertName()}Schema`; case 'ScalarTypeDefinition': return zod4Scalar(config, visitor, node.value); default: if (converter?.targetKind) console.warn('Unknown targetKind', converter?.targetKind); return zod4Scalar(config, visitor, node.value); } } function maybeLazy(visitor, type, schema) { return (0, lazy_js_1.buildMaybeLazy)(visitor, type, schema, s => `z.lazy(() => ${s})`); } function zod4Scalar(config, visitor, scalarName) { return (0, scalar_js_1.buildScalarSchema)(config, visitor, scalarName, { typeMap: { string: 'z.string()', number: 'z.number()', boolean: 'z.boolean()' }, fallback: exports.anySchema, }); } function unionLiterals(values) { if (values.length === 0) return 'never'; return values.map(value => JSON.stringify(value)).join(' | '); }