Skip to content

Commit

Permalink
🐛 Fix Code-Hex#599: add lazy support for potential self-referential t…
Browse files Browse the repository at this point in the history
…ypes
  • Loading branch information
nickthegroot committed Oct 3, 2024
1 parent 1048292 commit 3420e07
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 40 deletions.
2 changes: 1 addition & 1 deletion example/myzod/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export function UserSchema(): myzod.Type<User> {
createdAt: definedNonNullAnySchema.optional().nullable(),
email: myzod.string().optional().nullable(),
id: myzod.string().optional().nullable(),
kind: UserKindSchema().optional().nullable(),
kind: myzod.lazy(() => UserKindSchema().optional().nullable()),
name: myzod.string().optional().nullable(),
password: myzod.string().optional().nullable(),
updatedAt: definedNonNullAnySchema.optional().nullable()
Expand Down
2 changes: 1 addition & 1 deletion example/valibot/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export function UserSchema(): v.GenericSchema<User> {
createdAt: v.nullish(v.any()),
email: v.nullish(v.string()),
id: v.nullish(v.string()),
kind: v.nullish(UserKindSchema()),
kind: v.lazy(() => v.nullish(UserKindSchema())),
name: v.nullish(v.string()),
password: v.nullish(v.string()),
updatedAt: v.nullish(v.any())
Expand Down
2 changes: 1 addition & 1 deletion example/yup/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export function UserSchema(): yup.ObjectSchema<User> {
createdAt: yup.mixed().nullable().optional(),
email: yup.string().defined().nullable().optional(),
id: yup.string().defined().nullable().optional(),
kind: UserKindSchema().nullable().optional(),
kind: yup.lazy(() => UserKindSchema().nullable()).optional(),
name: yup.string().defined().nullable().optional(),
password: yup.string().defined().nullable().optional(),
updatedAt: yup.mixed().nullable().optional()
Expand Down
2 changes: 1 addition & 1 deletion example/zod/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ export function UserSchema(): z.ZodObject<Properties<User>> {
createdAt: definedNonNullAnySchema.nullish(),
email: z.string().nullish(),
id: z.string().nullish(),
kind: UserKindSchema().nullish(),
kind: z.lazy(() => UserKindSchema().nullish()),
name: z.string().nullish(),
password: z.string().nullish(),
updatedAt: definedNonNullAnySchema.nullish()
Expand Down
22 changes: 13 additions & 9 deletions src/myzod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import type { Visitor } from '../visitor.js';
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
import {
isEnumType,
isScalarType,
Kind,
} from 'graphql';
import { buildApi, formatDirectiveConfig } from '../directive.js';
import {
escapeGraphQLCharacters,
InterfaceTypeDefinitionBuilder,
isInput,
isListType,
isNamedType,
isNonNullType,
Expand Down Expand Up @@ -258,22 +259,22 @@ export class MyZodSchemaVisitor extends BaseSchemaVisitor {

function generateFieldMyZodSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number): string {
const gen = generateFieldTypeMyZodSchema(config, visitor, field, field.type);
return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount);
return indent(`${field.name.value}: ${maybeLazy(visitor, field.type, gen)}`, indentCount);
}

function generateFieldTypeMyZodSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode): string {
if (isListType(type)) {
const gen = generateFieldTypeMyZodSchema(config, visitor, field, type.type, type);
if (!isNonNullType(parentType)) {
const arrayGen = `myzod.array(${maybeLazy(type.type, gen)})`;
const arrayGen = `myzod.array(${maybeLazy(visitor, type.type, gen)})`;
const maybeLazyGen = applyDirectives(config, field, arrayGen);
return `${maybeLazyGen}.optional().nullable()`;
}
return `myzod.array(${maybeLazy(type.type, gen)})`;
return `myzod.array(${maybeLazy(visitor, type.type, gen)})`;
}
if (isNonNullType(type)) {
const gen = generateFieldTypeMyZodSchema(config, visitor, field, type.type, type);
return maybeLazy(type.type, gen);
return maybeLazy(visitor, type.type, gen);
}
if (isNamedType(type)) {
const gen = generateNameNodeMyZodSchema(config, visitor, type.name);
Expand Down Expand Up @@ -354,11 +355,14 @@ function generateNameNodeMyZodSchema(config: ValidationSchemaPluginConfig, visit
}
}

function maybeLazy(type: TypeNode, schema: string): string {
if (isNamedType(type) && isInput(type.name.value))
return `myzod.lazy(() => ${schema})`;
function maybeLazy(visitor: Visitor, type: TypeNode, schema: string): string {
if (!isNamedType(type)) {
return schema;
}

return schema;
const schemaType = visitor.getType(type.name.value);
const isComplexType = !isScalarType(schemaType) && !isEnumType(schemaType);
return isComplexType ? `myzod.lazy(() => ${schema})` : schema;
}

function myzod4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string {
Expand Down
19 changes: 11 additions & 8 deletions src/valibot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import type {
TypeNode,
UnionTypeDefinitionNode,
} from 'graphql';
import { isEnumType, isScalarType } from 'graphql';
import type { ValidationSchemaPluginConfig } from '../config.js';

import type { Visitor } from '../visitor.js';
import { DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
import { buildApiForValibot, formatDirectiveConfig } from '../directive.js';
import {
InterfaceTypeDefinitionBuilder,
isInput,
isListType,
isNamedType,
isNonNullType,
Expand Down Expand Up @@ -201,21 +201,21 @@ export class ValibotSchemaVisitor extends BaseSchemaVisitor {

function generateFieldValibotSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number): string {
const gen = generateFieldTypeValibotSchema(config, visitor, field, field.type);
return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount);
return indent(`${field.name.value}: ${maybeLazy(visitor, field.type, gen)}`, indentCount);
}

function generateFieldTypeValibotSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode): string {
if (isListType(type)) {
const gen = generateFieldTypeValibotSchema(config, visitor, field, type.type, type);
const arrayGen = `v.array(${maybeLazy(type.type, gen)})`;
const arrayGen = `v.array(${maybeLazy(visitor, type.type, gen)})`;
if (!isNonNullType(parentType))
return `v.nullish(${arrayGen})`;

return arrayGen;
}
if (isNonNullType(type)) {
const gen = generateFieldTypeValibotSchema(config, visitor, field, type.type, type);
return maybeLazy(type.type, gen);
return maybeLazy(visitor, type.type, gen);
}
if (isNamedType(type)) {
const gen = generateNameNodeValibotSchema(config, visitor, type.name);
Expand Down Expand Up @@ -274,11 +274,14 @@ function generateNameNodeValibotSchema(config: ValidationSchemaPluginConfig, vis
}
}

function maybeLazy(type: TypeNode, schema: string): string {
if (isNamedType(type) && isInput(type.name.value))
return `v.lazy(() => ${schema})`;
function maybeLazy(visitor: Visitor, type: TypeNode, schema: string): string {
if (!isNamedType(type)) {
return schema;
}

return schema;
const schemaType = visitor.getType(type.name.value);
const isComplexType = !isScalarType(schemaType) && !isEnumType(schemaType);
return isComplexType ? `v.lazy(() => ${schema})` : schema;
}

function valibot4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string {
Expand Down
23 changes: 13 additions & 10 deletions src/yup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import type { Visitor } from '../visitor.js';
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
import {
isEnumType,
isScalarType,
Kind,
} from 'graphql';
import { buildApi, formatDirectiveConfig } from '../directive.js';
import {
escapeGraphQLCharacters,
InterfaceTypeDefinitionBuilder,
isInput,
isListType,
isNamedType,
isNonNullType,
Expand Down Expand Up @@ -319,20 +320,20 @@ function generateFieldYupSchema(config: ValidationSchemaPluginConfig, visitor: V
const formatted = formatDirectiveConfig(config.directives);
gen += buildApi(formatted, field.directives);
}
return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount);
return indent(`${field.name.value}: ${maybeLazy(visitor, field.type, gen)}`, indentCount);
}

function generateFieldTypeYupSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, type: TypeNode, parentType?: TypeNode): string {
if (isListType(type)) {
const gen = generateFieldTypeYupSchema(config, visitor, type.type, type);
if (!isNonNullType(parentType))
return `yup.array(${maybeLazy(type.type, gen)}).defined().nullable()`;
return `yup.array(${maybeLazy(visitor, type.type, gen)}).defined().nullable()`;

return `yup.array(${maybeLazy(type.type, gen)}).defined()`;
return `yup.array(${maybeLazy(visitor, type.type, gen)}).defined()`;
}
if (isNonNullType(type)) {
const gen = generateFieldTypeYupSchema(config, visitor, type.type, type);
return maybeLazy(type.type, gen);
return maybeLazy(visitor, type.type, gen);
}
if (isNamedType(type)) {
const gen = generateNameNodeYupSchema(config, visitor, type.name);
Expand Down Expand Up @@ -375,12 +376,14 @@ function generateNameNodeYupSchema(config: ValidationSchemaPluginConfig, visitor
}
}

function maybeLazy(type: TypeNode, schema: string): string {
if (isNamedType(type) && isInput(type.name.value)) {
// https://github.com/jquense/yup/issues/1283#issuecomment-786559444
return `yup.lazy(() => ${schema})`;
function maybeLazy(visitor: Visitor, type: TypeNode, schema: string): string {
if (!isNamedType(type)) {
return schema;
}
return schema;

const schemaType = visitor.getType(type.name.value);
const isComplexType = !isScalarType(schemaType) && !isEnumType(schemaType);
return isComplexType ? `yup.lazy(() => ${schema})` : schema;
}

function yup4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string {
Expand Down
22 changes: 13 additions & 9 deletions src/zod/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@ import type { Visitor } from '../visitor.js';
import { resolveExternalModuleAndFn } from '@graphql-codegen/plugin-helpers';
import { convertNameParts, DeclarationBlock, indent } from '@graphql-codegen/visitor-plugin-common';
import {
isEnumType,
isScalarType,
Kind,
} from 'graphql';
import { buildApi, formatDirectiveConfig } from '../directive.js';
import {
escapeGraphQLCharacters,
InterfaceTypeDefinitionBuilder,
isInput,
isListType,
isNamedType,
isNonNullType,
Expand Down Expand Up @@ -274,22 +275,22 @@ export class ZodSchemaVisitor extends BaseSchemaVisitor {

function generateFieldZodSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, indentCount: number): string {
const gen = generateFieldTypeZodSchema(config, visitor, field, field.type);
return indent(`${field.name.value}: ${maybeLazy(field.type, gen)}`, indentCount);
return indent(`${field.name.value}: ${maybeLazy(visitor, field.type, gen)}`, indentCount);
}

function generateFieldTypeZodSchema(config: ValidationSchemaPluginConfig, visitor: Visitor, field: InputValueDefinitionNode | FieldDefinitionNode, type: TypeNode, parentType?: TypeNode): string {
if (isListType(type)) {
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type);
if (!isNonNullType(parentType)) {
const arrayGen = `z.array(${maybeLazy(type.type, gen)})`;
const arrayGen = `z.array(${maybeLazy(visitor, type.type, gen)})`;
const maybeLazyGen = applyDirectives(config, field, arrayGen);
return `${maybeLazyGen}.nullish()`;
}
return `z.array(${maybeLazy(type.type, gen)})`;
return `z.array(${maybeLazy(visitor, type.type, gen)})`;
}
if (isNonNullType(type)) {
const gen = generateFieldTypeZodSchema(config, visitor, field, type.type, type);
return maybeLazy(type.type, gen);
return maybeLazy(visitor, type.type, gen);
}
if (isNamedType(type)) {
const gen = generateNameNodeZodSchema(config, visitor, type.name);
Expand Down Expand Up @@ -370,11 +371,14 @@ function generateNameNodeZodSchema(config: ValidationSchemaPluginConfig, visitor
}
}

function maybeLazy(type: TypeNode, schema: string): string {
if (isNamedType(type) && isInput(type.name.value))
return `z.lazy(() => ${schema})`;
function maybeLazy(visitor: Visitor, type: TypeNode, schema: string): string {
if (!isNamedType(type)) {
return schema;
}

return schema;
const schemaType = visitor.getType(type.name.value);
const isComplexType = !isScalarType(schemaType) && !isEnumType(schemaType);
return isComplexType ? `z.lazy(() => ${schema})` : schema;
}

function zod4Scalar(config: ValidationSchemaPluginConfig, visitor: Visitor, scalarName: string): string {
Expand Down

0 comments on commit 3420e07

Please sign in to comment.