Skip to content

Upgrade yup v1 #351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 58 additions & 58 deletions example/yup/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,114 +1,114 @@
import * as yup from 'yup'
import { Admin, AttributeInput, ButtonComponentType, ComponentInput, DropDownComponentInput, EventArgumentInput, EventInput, EventOptionType, Guest, HttpInput, HttpMethod, LayoutInput, PageInput, PageType, User, UserKind } from '../types'

function union<T>(...schemas: ReadonlyArray<yup.SchemaOf<T>>): yup.BaseSchema<T> {
return yup.mixed().test({
function union<T extends {}>(...schemas: ReadonlyArray<yup.ObjectSchema<T>>): yup.MixedSchema<T> {
return yup.mixed<T>().test({
test: (value) => schemas.some((schema) => schema.isValidSync(value))
})
}).defined()
}

export function AdminSchema(): yup.SchemaOf<Admin> {
export function AdminSchema(): yup.ObjectSchema<Admin> {
return yup.object({
__typename: yup.mixed().oneOf(['Admin', undefined]),
lastModifiedAt: yup.mixed()
__typename: yup.string<'Admin'>().optional(),
lastModifiedAt: yup.mixed().nullable().optional()
})
}

export function AttributeInputSchema(): yup.SchemaOf<AttributeInput> {
export function AttributeInputSchema(): yup.ObjectSchema<AttributeInput> {
return yup.object({
key: yup.string(),
val: yup.string()
key: yup.string().defined().nullable().optional(),
val: yup.string().defined().nullable().optional()
})
}

export const ButtonComponentTypeSchema = yup.mixed().oneOf([ButtonComponentType.Button, ButtonComponentType.Submit]);
export const ButtonComponentTypeSchema = yup.string<ButtonComponentType>().oneOf([ButtonComponentType.Button, ButtonComponentType.Submit]).defined();

export function ComponentInputSchema(): yup.SchemaOf<ComponentInput> {
export function ComponentInputSchema(): yup.ObjectSchema<ComponentInput> {
return yup.object({
child: yup.lazy(() => ComponentInputSchema()) as never,
childrens: yup.array().of(yup.lazy(() => ComponentInputSchema()) as never).optional(),
event: yup.lazy(() => EventInputSchema()) as never,
name: yup.string().defined(),
type: ButtonComponentTypeSchema.defined()
child: yup.lazy(() => ComponentInputSchema()).optional(),
childrens: yup.array(yup.lazy(() => ComponentInputSchema())).defined().nullable().optional(),
event: yup.lazy(() => EventInputSchema()).optional(),
name: yup.string().defined().nonNullable(),
type: ButtonComponentTypeSchema.nonNullable()
})
}

export function DropDownComponentInputSchema(): yup.SchemaOf<DropDownComponentInput> {
export function DropDownComponentInputSchema(): yup.ObjectSchema<DropDownComponentInput> {
return yup.object({
dropdownComponent: yup.lazy(() => ComponentInputSchema()) as never,
getEvent: yup.lazy(() => EventInputSchema().defined()) as never
dropdownComponent: yup.lazy(() => ComponentInputSchema()).optional(),
getEvent: yup.lazy(() => EventInputSchema().nonNullable())
})
}

export function EventArgumentInputSchema(): yup.SchemaOf<EventArgumentInput> {
export function EventArgumentInputSchema(): yup.ObjectSchema<EventArgumentInput> {
return yup.object({
name: yup.string().defined().min(5),
value: yup.string().defined().matches(/^foo/)
name: yup.string().defined().nonNullable().min(5),
value: yup.string().defined().nonNullable().matches(/^foo/)
})
}

export function EventInputSchema(): yup.SchemaOf<EventInput> {
export function EventInputSchema(): yup.ObjectSchema<EventInput> {
return yup.object({
arguments: yup.array().of(yup.lazy(() => EventArgumentInputSchema().defined()) as never).defined(),
options: yup.array().of(EventOptionTypeSchema.defined()).optional()
arguments: yup.array(yup.lazy(() => EventArgumentInputSchema().nonNullable())).defined(),
options: yup.array(EventOptionTypeSchema.nonNullable()).defined().nullable().optional()
})
}

export const EventOptionTypeSchema = yup.mixed().oneOf([EventOptionType.Reload, EventOptionType.Retry]);
export const EventOptionTypeSchema = yup.string<EventOptionType>().oneOf([EventOptionType.Reload, EventOptionType.Retry]).defined();

export function GuestSchema(): yup.SchemaOf<Guest> {
export function GuestSchema(): yup.ObjectSchema<Guest> {
return yup.object({
__typename: yup.mixed().oneOf(['Guest', undefined]),
lastLoggedIn: yup.mixed()
__typename: yup.string<'Guest'>().optional(),
lastLoggedIn: yup.mixed().nullable().optional()
})
}

export function HttpInputSchema(): yup.SchemaOf<HttpInput> {
export function HttpInputSchema(): yup.ObjectSchema<HttpInput> {
return yup.object({
method: HttpMethodSchema,
url: yup.mixed().defined()
method: HttpMethodSchema.nullable().optional(),
url: yup.mixed().nonNullable()
})
}

export const HttpMethodSchema = yup.mixed().oneOf([HttpMethod.Get, HttpMethod.Post]);
export const HttpMethodSchema = yup.string<HttpMethod>().oneOf([HttpMethod.Get, HttpMethod.Post]).defined();

export function LayoutInputSchema(): yup.SchemaOf<LayoutInput> {
export function LayoutInputSchema(): yup.ObjectSchema<LayoutInput> {
return yup.object({
dropdown: yup.lazy(() => DropDownComponentInputSchema()) as never
dropdown: yup.lazy(() => DropDownComponentInputSchema()).optional()
})
}

export function PageInputSchema(): yup.SchemaOf<PageInput> {
export function PageInputSchema(): yup.ObjectSchema<PageInput> {
return yup.object({
attributes: yup.array().of(yup.lazy(() => AttributeInputSchema().defined()) as never).optional(),
date: yup.mixed(),
height: yup.number().defined(),
id: yup.string().defined(),
layout: yup.lazy(() => LayoutInputSchema().defined()) as never,
pageType: PageTypeSchema.defined(),
postIDs: yup.array().of(yup.string().defined()).optional(),
show: yup.boolean().defined(),
tags: yup.array().of(yup.string()).optional(),
title: yup.string().defined(),
width: yup.number().defined()
attributes: yup.array(yup.lazy(() => AttributeInputSchema().nonNullable())).defined().nullable().optional(),
date: yup.mixed().nullable().optional(),
height: yup.number().defined().nonNullable(),
id: yup.string().defined().nonNullable(),
layout: yup.lazy(() => LayoutInputSchema().nonNullable()),
pageType: PageTypeSchema.nonNullable(),
postIDs: yup.array(yup.string().defined().nonNullable()).defined().nullable().optional(),
show: yup.boolean().defined().nonNullable(),
tags: yup.array(yup.string().defined().nullable()).defined().nullable().optional(),
title: yup.string().defined().nonNullable(),
width: yup.number().defined().nonNullable()
})
}

export const PageTypeSchema = yup.mixed().oneOf([PageType.BasicAuth, PageType.Lp, PageType.Restricted, PageType.Service]);
export const PageTypeSchema = yup.string<PageType>().oneOf([PageType.BasicAuth, PageType.Lp, PageType.Restricted, PageType.Service]).defined();

export function UserSchema(): yup.SchemaOf<User> {
export function UserSchema(): yup.ObjectSchema<User> {
return yup.object({
__typename: yup.mixed().oneOf(['User', undefined]),
createdAt: yup.mixed(),
email: yup.string(),
id: yup.string(),
kind: UserKindSchema(),
name: yup.string(),
password: yup.string(),
updatedAt: yup.mixed()
__typename: yup.string<'User'>().optional(),
createdAt: yup.mixed().nullable().optional(),
email: yup.string().defined().nullable().optional(),
id: yup.string().defined().nullable().optional(),
kind: UserKindSchema().nullable().optional(),
name: yup.string().defined().nullable().optional(),
password: yup.string().defined().nullable().optional(),
updatedAt: yup.mixed().nullable().optional()
})
}

export function UserKindSchema(): yup.BaseSchema<UserKind> {
export function UserKindSchema(): yup.MixedSchema<UserKind> {
return union<UserKind>(AdminSchema(), GuestSchema())
}
79 changes: 37 additions & 42 deletions src/yup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
'\n' +
new DeclarationBlock({})
.asKind('function')
.withName('union<T>(...schemas: ReadonlyArray<yup.SchemaOf<T>>): yup.BaseSchema<T>')
.withName('union<T extends {}>(...schemas: ReadonlyArray<yup.ObjectSchema<T>>): yup.MixedSchema<T>')
.withBlock(
[
indent('return yup.mixed().test({'),
indent('return yup.mixed<T>().test({'),
indent('test: (value) => schemas.some((schema) => schema.isValidSync(value))', 2),
indent('})'),
indent('}).defined()'),
].join('\n')
).string
);
Expand All @@ -52,13 +52,16 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
importTypes.push(name);

const shape = node.fields
?.map(field => generateFieldYupSchema(config, tsVisitor, schema, field, 2))
?.map(field => {
const fieldSchema = generateFieldYupSchema(config, tsVisitor, schema, field, 2);
return isNonNullType(field.type) ? fieldSchema : `${fieldSchema}.optional()`;
})
.join(',\n');

return new DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(): yup.SchemaOf<${name}>`)
.withName(`${name}Schema(): yup.ObjectSchema<${name}>`)
.withBlock([indent(`return yup.object({`), shape, indent('})')].join('\n')).string;
},
},
Expand All @@ -68,17 +71,20 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
importTypes.push(name);

const shape = node.fields
?.map(field => generateFieldYupSchema(config, tsVisitor, schema, field, 2))
?.map(field => {
const fieldSchema = generateFieldYupSchema(config, tsVisitor, schema, field, 2);
return isNonNullType(field.type) ? fieldSchema : `${fieldSchema}.optional()`;
})
.join(',\n');

return new DeclarationBlock({})
.export()
.asKind('function')
.withName(`${name}Schema(): yup.SchemaOf<${name}>`)
.withName(`${name}Schema(): yup.ObjectSchema<${name}>`)
.withBlock(
[
indent(`return yup.object({`),
indent(`__typename: yup.mixed().oneOf(['${node.name.value}', undefined]),`, 2),
indent(`__typename: yup.string<'${node.name.value}'>().optional(),`, 2),
shape,
indent('})'),
].join('\n')
Expand All @@ -91,13 +97,13 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
importTypes.push(enumname);

if (config.enumsAsTypes) {
const enums = node.values?.map(enumOption => `'${enumOption.name.value}'`);

return new DeclarationBlock({})
.export()
.asKind('const')
.withName(`${enumname}Schema`)
.withContent(
`yup.mixed().oneOf([${node.values?.map(enumOption => `'${enumOption.name.value}'`).join(', ')}])`
).string;
.withContent(`yup.string().oneOf([${enums?.join(', ')}]).defined()`).string;
}

const values = node.values
Expand All @@ -113,7 +119,7 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
.export()
.asKind('const')
.withName(`${enumname}Schema`)
.withContent(`yup.mixed().oneOf([${values}])`).string;
.withContent(`yup.string<${enumname}>().oneOf([${values}]).defined()`).string;
},
},
UnionTypeDefinition: {
Expand All @@ -129,7 +135,7 @@ export const YupSchemaVisitor = (schema: GraphQLSchema, config: ValidationSchema
return new DeclarationBlock({})
.export()
.asKind('function')
.withName(`${unionName}Schema(): yup.BaseSchema<${unionName}>`)
.withName(`${unionName}Schema(): yup.MixedSchema<${unionName}>`)
.withBlock(union).string;
},
},
Expand Down Expand Up @@ -180,22 +186,28 @@ const generateFieldTypeYupSchema = (
if (isListType(type)) {
const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type);
if (!isNonNullType(parentType)) {
return `yup.array().of(${maybeLazy(type.type, gen)}).optional()`;
return `yup.array(${maybeLazy(type.type, gen)}).defined().nullable()`;
}
return `yup.array().of(${maybeLazy(type.type, gen)})`;
return `yup.array(${maybeLazy(type.type, gen)}).defined()`;
}
if (isNonNullType(type)) {
const gen = generateFieldTypeYupSchema(config, tsVisitor, schema, type.type, type);
const nonNullGen = maybeNonEmptyString(config, tsVisitor, gen, type.type);
return maybeLazy(type.type, nonNullGen);
return maybeLazy(type.type, gen);
}
if (isNamedType(type)) {
const gen = generateNameNodeYupSchema(config, tsVisitor, schema, type.name);
if (isNonNullType(parentType)) {
if (config.notAllowEmptyString === true) {
const tsType = tsVisitor.scalars[type.name.value];
if (tsType === 'string') return `${gen}.required()`;
}
return `${gen}.nonNullable()`;
}
const typ = schema.getType(type.name.value);
if (typ?.astNode?.kind === 'ObjectTypeDefinition') {
return `${gen}.optional()`;
if (typ?.astNode?.kind === 'InputObjectTypeDefinition') {
return `${gen}`;
}
return gen;
return `${gen}.nullable()`;
}
console.warn('unhandled type:', type);
return '';
Expand Down Expand Up @@ -236,40 +248,23 @@ const generateNameNodeYupSchema = (
const 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}) as never`;
return `yup.lazy(() => ${schema})`;
}
return schema;
};

const maybeNonEmptyString = (
config: ValidationSchemaPluginConfig,
tsVisitor: TsVisitor,
schema: string,
childType: TypeNode
): string => {
if (config.notAllowEmptyString === true && isNamedType(childType)) {
const maybeScalarName = childType.name.value;
const tsType = tsVisitor.scalars[maybeScalarName];
if (tsType === 'string') {
return `${schema}.required()`;
}
}
// fallback
return `${schema}.defined()`;
};

const yup4Scalar = (config: ValidationSchemaPluginConfig, tsVisitor: TsVisitor, scalarName: string): string => {
if (config.scalarSchemas?.[scalarName]) {
return config.scalarSchemas[scalarName];
return `${config.scalarSchemas[scalarName]}.defined()`;
}
const tsType = tsVisitor.scalars[scalarName];
switch (tsType) {
case 'string':
return `yup.string()`;
return `yup.string().defined()`;
case 'number':
return `yup.number()`;
return `yup.number().defined()`;
case 'boolean':
return `yup.boolean()`;
return `yup.boolean().defined()`;
}
console.warn('unhandled name:', scalarName);
return `yup.mixed()`;
Expand Down
Loading