feat(Form): apply transformations (#2550)

Co-authored-by: Romain Hamel <rom.hml@gmail.com>
This commit is contained in:
kyyy
2024-11-12 22:43:40 +07:00
committed by GitHub
parent 95aa6f68b3
commit 75c5e87724
4 changed files with 240 additions and 82 deletions

View File

@@ -4,7 +4,7 @@ import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup'
import type { GenericSchema as ValibotSchema, GenericSchemaAsync as ValibotSchemaAsync, SafeParser as ValibotSafeParser, SafeParserAsync as ValibotSafeParserAsync } from 'valibot'
import type { Struct } from 'superstruct'
import type { FormError } from '../types/form'
import type { FormSchema, ValidateReturnSchema } from '../types/form'
export function isYupSchema(schema: any): schema is YupObjectSchema<any> {
return schema.validate && schema.__isYupSchema__
@@ -14,22 +14,6 @@ export function isYupError(error: any): error is YupError {
return error.inner !== undefined
}
export async function getYupErrors(state: any, schema: YupObjectSchema<any>): Promise<FormError[]> {
try {
await schema.validate(state, { abortEarly: false })
return []
} catch (error) {
if (isYupError(error)) {
return error.inner.map(issue => ({
name: issue.path ?? '',
message: issue.message
}))
} else {
throw error
}
}
}
export function isSuperStructSchema(schema: any): schema is Struct<any, any> {
return (
'schema' in schema
@@ -43,17 +27,6 @@ export function isZodSchema(schema: any): schema is ZodSchema {
return schema.parse !== undefined
}
export async function getZodErrors(state: any, schema: ZodSchema): Promise<FormError[]> {
const result = await schema.safeParseAsync(state)
if (result.success === false) {
return result.error.issues.map(issue => ({
name: issue.path.join('.'),
message: issue.message
}))
}
return []
}
export function isJoiSchema(schema: any): schema is JoiSchema {
return schema.validateAsync !== undefined && schema.id !== undefined
}
@@ -62,61 +35,177 @@ export function isJoiError(error: any): error is JoiError {
return error.isJoi === true
}
export async function getJoiErrors(state: any, schema: JoiSchema): Promise<FormError[]> {
try {
await schema.validateAsync(state, { abortEarly: false })
return []
} catch (error) {
if (isJoiError(error)) {
return error.details.map(detail => ({
name: detail.path.join('.'),
message: detail.message
}))
} else {
throw error
}
}
}
export function isValibotSchema(schema: any): schema is ValibotSchema | ValibotSchemaAsync | ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any> {
return '_run' in schema || (typeof schema === 'function' && 'schema' in schema)
}
export async function getValibotErrors(
state: any,
schema: ValibotSchema | ValibotSchemaAsync | ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>
): Promise<FormError[]> {
const result = await ('_run' in schema ? schema._run({ typed: false, value: state }, {}) : schema(state))
return result.issues?.map(issue => ({
// We know that the key for a form schema is always a string or a number
name: issue.path?.map((item: any) => item.key).join('.') || '',
message: issue.message
})) || []
}
export function isStandardSchema(schema: any): schema is v1.StandardSchema {
return '~standard' in schema
}
export async function getStandardErrors(
export async function validateStandarSchema(
state: any,
schema: v1.StandardSchema
): Promise<FormError[]> {
const result = await schema['~standard'].validate(state)
return result.issues?.map(issue => ({
name: issue.path?.map(item => typeof item === 'object' ? item.key : item).join('.') || '',
message: issue.message
})) || []
): Promise<ValidateReturnSchema<typeof state>> {
const result = await schema['~standard'].validate({
value: state
})
if (result.issues) {
return {
errors: result.issues?.map(issue => ({
name: issue.path?.map(item => typeof item === 'object' ? item.key : item).join('.') || '',
message: issue.message
})) || [],
result: null
}
}
return {
errors: null,
result: result.value
}
}
export async function getSuperStructErrors(state: any, schema: Struct<any, any>): Promise<FormError[]> {
const [err] = schema.validate(state)
async function validateYupSchema(
state: any,
schema: YupObjectSchema<any>
): Promise<ValidateReturnSchema<typeof state>> {
try {
const result = schema.validateSync(state, { abortEarly: false })
return {
errors: null,
result
}
} catch (error) {
if (isYupError(error)) {
const errors = error.inner.map(issue => ({
name: issue.path ?? '',
message: issue.message
}))
return {
errors,
result: null
}
} else {
throw error
}
}
}
async function validateSuperstructSchema(state: any, schema: Struct<any, any>): Promise<ValidateReturnSchema<typeof state>> {
const [err, result] = schema.validate(state)
if (err) {
const errors = err.failures()
return errors.map(error => ({
const errors = err.failures().map(error => ({
message: error.message,
name: error.path.join('.')
}))
return {
errors,
result: null
}
}
return {
errors: null,
result
}
}
async function validateZodSchema(
state: any,
schema: ZodSchema
): Promise<ValidateReturnSchema<typeof state>> {
const result = await schema.safeParseAsync(state)
if (result.success === false) {
const errors = result.error.issues.map(issue => ({
name: issue.path.join('.'),
message: issue.message
}))
return {
errors,
result: null
}
}
return {
result: result.data,
errors: null
}
}
async function validateJoiSchema(
state: any,
schema: JoiSchema
): Promise<ValidateReturnSchema<typeof state>> {
try {
const result = await schema.validateAsync(state, { abortEarly: false })
return {
errors: null,
result
}
} catch (error) {
if (isJoiError(error)) {
const errors = error.details.map(issue => ({
name: issue.path.join('.'),
message: issue.message
}))
return {
errors,
result: null
}
} else {
throw error
}
}
}
async function validateValibotSchema(
state: any,
schema: ValibotSchema | ValibotSchemaAsync | ValibotSafeParser<any, any> | ValibotSafeParserAsync<any, any>
): Promise<ValidateReturnSchema<typeof state>> {
const result = await ('_run' in schema ? schema._run({ typed: false, value: state }, {}) : schema(state))
if (!result.issues || result.issues.length === 0) {
const output = ('output' in result
? result.output
: 'value' in result
? result.value
: null)
return {
errors: null,
result: output
}
}
const errors = result.issues.map(issue => ({
name: issue.path?.map((item: any) => item.key).join('.') || '',
message: issue.message
}))
return {
errors,
result: null
}
}
export function parseSchema<T extends object>(state: T, schema: FormSchema<T>): Promise<ValidateReturnSchema<typeof state>> {
if (isZodSchema(schema)) {
return validateZodSchema(state, schema)
} else if (isJoiSchema(schema)) {
return validateJoiSchema(state, schema)
} else if (isValibotSchema(schema)) {
return validateValibotSchema(state, schema)
} else if (isYupSchema(schema)) {
return validateYupSchema(state, schema)
} else if (isSuperStructSchema(schema)) {
return validateSuperstructSchema(state, schema)
} else if (isStandardSchema(schema)) {
return validateStandarSchema(state, schema)
} else {
throw new Error('Form validation failed: Unsupported form schema')
}
return []
}