refactor(Form)!: drop explicit support for zod and valibot (#3617)

This commit is contained in:
Romain Hamel
2025-03-19 12:18:26 +01:00
committed by GitHub
parent 02184b016a
commit 9a4bb34d7d
9 changed files with 14 additions and 95 deletions

View File

@@ -22,7 +22,7 @@ async function onSubmit(event: FormSubmitEvent<Schema>) {
</script> </script>
<template> <template>
<UForm :schema="v.safeParser(schema)" :state="state" class="space-y-4" @submit="onSubmit"> <UForm :schema="schema" :state="state" class="space-y-4" @submit="onSubmit">
<UFormField label="Email" name="email"> <UFormField label="Email" name="email">
<UInput v-model="state.email" /> <UInput v-model="state.email" />
</UFormField> </UFormField>

View File

@@ -9,7 +9,7 @@ links:
## Usage ## Usage
Use the Form component to validate form data using schema libraries such as [Valibot](https://github.com/fabian-hiller/valibot), [Zod](https://github.com/colinhacks/zod), [Yup](https://github.com/jquense/yup), [Joi](https://github.com/hapijs/joi), [Superstruct](https://github.com/ianstormtaylor/superstruct) or your own validation logic. Use the Form component to validate form data using validation libraries such as [Valibot](https://github.com/fabian-hiller/valibot), [Zod](https://github.com/colinhacks/zod), [Yup](https://github.com/jquense/yup), [Joi](https://github.com/hapijs/joi), [Superstruct](https://github.com/ianstormtaylor/superstruct) or your own validation logic.
It works with the [FormField](/components/form-field) component to display error messages around form elements automatically. It works with the [FormField](/components/form-field) component to display error messages around form elements automatically.
@@ -18,7 +18,7 @@ It works with the [FormField](/components/form-field) component to display error
It requires two props: It requires two props:
- `state` - a reactive object holding the form's state. - `state` - a reactive object holding the form's state.
- `schema` - a schema object from a validation library like [Valibot](https://github.com/fabian-hiller/valibot), [Zod](https://github.com/colinhacks/zod), [Yup](https://github.com/jquense/yup), [Joi](https://github.com/hapijs/joi) or [Superstruct](https://github.com/ianstormtaylor/superstruct). - `schema` - any [Standard Schema](https://standardschema.dev/) or a schema from [Yup](https://github.com/jquense/yup), [Joi](https://github.com/hapijs/joi) or [Superstruct](https://github.com/ianstormtaylor/superstruct).
::warning ::warning
**No validation library is included** by default, ensure you **install the one you need**. **No validation library is included** by default, ensure you **install the one you need**.

View File

@@ -27,7 +27,7 @@
"shiki-transformer-color-highlight": "^1.0.0", "shiki-transformer-color-highlight": "^1.0.0",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",
"ufo": "^1.5.4", "ufo": "^1.5.4",
"valibot": "^0.42.1", "valibot": "^1.0.0",
"yup": "^1.6.1", "yup": "^1.6.1",
"zod": "^3.24.2" "zod": "^3.24.2"
}, },

View File

@@ -134,7 +134,7 @@
"nuxt": "^3.16.0", "nuxt": "^3.16.0",
"release-it": "^18.1.2", "release-it": "^18.1.2",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",
"valibot": "^0.42.1", "valibot": "^1.0.0",
"vitest": "^3.0.9", "vitest": "^3.0.9",
"vitest-environment-nuxt": "^1.0.1", "vitest-environment-nuxt": "^1.0.1",
"vue-tsc": "^2.2.0", "vue-tsc": "^2.2.0",

14
pnpm-lock.yaml generated
View File

@@ -184,8 +184,8 @@ importers:
specifier: ^2.0.2 specifier: ^2.0.2
version: 2.0.2 version: 2.0.2
valibot: valibot:
specifier: ^0.42.1 specifier: ^1.0.0
version: 0.42.1(typescript@5.6.3) version: 1.0.0(typescript@5.6.3)
vitest: vitest:
specifier: ^3.0.9 specifier: ^3.0.9
version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.10)(happy-dom@17.4.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0) version: 3.0.9(@types/debug@4.1.12)(@types/node@22.13.10)(happy-dom@17.4.4)(jiti@2.4.2)(lightningcss@1.29.2)(terser@5.39.0)(yaml@2.7.0)
@@ -292,8 +292,8 @@ importers:
specifier: ^1.5.4 specifier: ^1.5.4
version: 1.5.4 version: 1.5.4
valibot: valibot:
specifier: ^0.42.1 specifier: ^1.0.0
version: 0.42.1(typescript@5.6.3) version: 1.0.0(typescript@5.6.3)
yup: yup:
specifier: ^1.6.1 specifier: ^1.6.1
version: 1.6.1 version: 1.6.1
@@ -6827,8 +6827,8 @@ packages:
util-deprecate@1.0.2: util-deprecate@1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
valibot@0.42.1: valibot@1.0.0:
resolution: {integrity: sha512-3keXV29Ar5b//Hqi4MbSdV7lfVp6zuYLZuA9V1PvQUsXqogr+u5lvLPLk3A4f74VUXDnf/JfWMN6sB+koJ/FFw==} resolution: {integrity: sha512-1Hc0ihzWxBar6NGeZv7fPLY0QuxFMyxwYR2sF1Blu7Wq7EnremwY2W02tit2ij2VJT8HcSkHAQqmFfl77f73Yw==}
peerDependencies: peerDependencies:
typescript: 5.6.3 typescript: 5.6.3
peerDependenciesMeta: peerDependenciesMeta:
@@ -14879,7 +14879,7 @@ snapshots:
util-deprecate@1.0.2: {} util-deprecate@1.0.2: {}
valibot@0.42.1(typescript@5.6.3): valibot@1.0.0(typescript@5.6.3):
optionalDependencies: optionalDependencies:
typescript: 5.6.3 typescript: 5.6.3

View File

@@ -6,8 +6,6 @@
"enabled": true "enabled": true
}, },
"ignoreDeps": [ "ignoreDeps": [
"valibot30",
"valibot31",
"typescript", "typescript",
"vaul-vue", "vaul-vue",
"vue-tsc" "vue-tsc"

View File

@@ -1,9 +1,7 @@
import type { StandardSchemaV1 } from '@standard-schema/spec' import type { StandardSchemaV1 } from '@standard-schema/spec'
import type { ComputedRef, DeepReadonly, Ref } from 'vue' import type { ComputedRef, DeepReadonly, Ref } from 'vue'
import type { ZodSchema } from 'zod'
import type { Schema as JoiSchema } from 'joi' import type { Schema as JoiSchema } from 'joi'
import type { ObjectSchema as YupObjectSchema } from 'yup' import type { ObjectSchema as YupObjectSchema } from 'yup'
import type { GenericSchema as ValibotSchema, GenericSchemaAsync as ValibotSchemaAsync, SafeParser as ValibotSafeParser, SafeParserAsync as ValibotSafeParserAsync } from 'valibot'
import type { GetObjectField } from './utils' import type { GetObjectField } from './utils'
import type { Struct as SuperstructSchema } from 'superstruct' import type { Struct as SuperstructSchema } from 'superstruct'
@@ -23,12 +21,7 @@ export interface Form<T extends object> {
} }
export type FormSchema<T extends object> = export type FormSchema<T extends object> =
| ZodSchema
| YupObjectSchema<T> | YupObjectSchema<T>
| ValibotSchema
| ValibotSchemaAsync
| ValibotSafeParser<any, any>
| ValibotSafeParserAsync<any, any>
| JoiSchema<T> | JoiSchema<T>
| SuperstructSchema<any, any> | SuperstructSchema<any, any>
| StandardSchemaV1 | StandardSchemaV1

View File

@@ -1,8 +1,6 @@
import type { StandardSchemaV1 } from '@standard-schema/spec' import type { StandardSchemaV1 } from '@standard-schema/spec'
import type { ZodSchema } from 'zod'
import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi' import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } from 'yup' 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 { Struct } from 'superstruct'
import type { FormSchema, ValidateReturnSchema } from '../types/form' import type { FormSchema, ValidateReturnSchema } from '../types/form'
@@ -23,10 +21,6 @@ export function isSuperStructSchema(schema: any): schema is Struct<any, any> {
) )
} }
export function isZodSchema(schema: any): schema is ZodSchema {
return schema.parse !== undefined
}
export function isJoiSchema(schema: any): schema is JoiSchema { export function isJoiSchema(schema: any): schema is JoiSchema {
return schema.validateAsync !== undefined && schema.id !== undefined return schema.validateAsync !== undefined && schema.id !== undefined
} }
@@ -35,10 +29,6 @@ export function isJoiError(error: any): error is JoiError {
return error.isJoi === true return error.isJoi === true
} }
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 function isStandardSchema(schema: any): schema is StandardSchemaV1 { export function isStandardSchema(schema: any): schema is StandardSchemaV1 {
return '~standard' in schema return '~standard' in schema
} }
@@ -112,28 +102,6 @@ async function validateSuperstructSchema(state: any, schema: Struct<any, any>):
} }
} }
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( async function validateJoiSchema(
state: any, state: any,
schema: JoiSchema schema: JoiSchema
@@ -161,44 +129,11 @@ async function validateJoiSchema(
} }
} }
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 validateSchema<T extends object>(state: T, schema: FormSchema<T>): Promise<ValidateReturnSchema<typeof state>> { export function validateSchema<T extends object>(state: T, schema: FormSchema<T>): Promise<ValidateReturnSchema<typeof state>> {
if (isZodSchema(schema)) { if (isStandardSchema(schema)) {
return validateZodSchema(state, schema) return validateStandardSchema(state, schema)
} else if (isJoiSchema(schema)) { } else if (isJoiSchema(schema)) {
return validateJoiSchema(state, schema) return validateJoiSchema(state, schema)
} else if (isStandardSchema(schema)) {
return validateStandardSchema(state, schema)
} else if (isValibotSchema(schema)) {
return validateValibotSchema(state, schema)
} else if (isYupSchema(schema)) { } else if (isYupSchema(schema)) {
return validateYupSchema(state, schema) return validateYupSchema(state, schema)
} else if (isSuperStructSchema(schema)) { } else if (isSuperStructSchema(schema)) {

View File

@@ -59,13 +59,6 @@ describe('Form', () => {
}) })
} }
], ],
['valibot safeParser', {
schema: valibot.safeParser(valibot.object({
email: valibot.string(),
password: valibot.pipe(valibot.string(), valibot.minLength(8, 'Must be at least 8 characters'))
}))
}
],
['superstruct', { ['superstruct', {
schema: object({ schema: object({
email: nonempty(string()), email: nonempty(string()),