Skip to content

Commit ddc2151

Browse files
feat(valibot): upgrade and migrate to v0.31.0 (#4770)
* feat(valibot): upgrade and migrate to v0.31.0 * feat(valibot): refactor resolver with new util * docs: update docs to align with 31 rc * feat: bump valibot to 0.31.0 release * chore: added changeset entry --------- Co-authored-by: Abdelrahman Awad <[email protected]>
1 parent b251ad0 commit ddc2151

File tree

11 files changed

+892
-202
lines changed

11 files changed

+892
-202
lines changed

.changeset/rude-rabbits-bow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@vee-validate/valibot": patch
3+
---
4+
5+
feat: support valibot 0.31.0

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
"shiki": "^0.14.6",
3232
"tailwindcss": "^3.3.6",
3333
"unist-util-visit": "^5.0.0",
34-
"valibot": "^0.28.1",
34+
"valibot": "^0.31.0",
3535
"vee-validate": "^4.13.0",
3636
"vue": "^3.4.26",
3737
"yup": "^1.3.2",

docs/src/components/Repl.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ store.setImportMap({
3939
toposort: 'https://esm-repo.netlify.app/topsort.esm.js',
4040
yup: 'https://unpkg.com/[email protected]/index.esm.js',
4141
zod: 'https://unpkg.com/[email protected]/lib/index.mjs',
42-
valibot: 'https://unpkg.com/valibot@0.21.0/dist/index.js',
42+
valibot: 'https://unpkg.com/valibot@0.31.0/dist/index.js',
4343
'@vue/devtools-api': 'https://unpkg.com/@vue/[email protected]/lib/esm/index.js',
4444
vue: `https://unpkg.com/vue@${version}/dist/vue.esm-browser.prod.js`,
4545
},

docs/src/components/examples/CompositionInputFieldValibot.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
<script setup>
77
import { useField } from 'vee-validate';
88
import { toTypedSchema } from '@vee-validate/valibot';
9-
import { string, email, minLength } from 'valibot';
9+
import * as v from 'valibot';
1010
11-
const { value, errorMessage } = useField('email', toTypedSchema(string([minLength(1, 'Required'), email()])));
11+
const { value, errorMessage } = useField(
12+
'email',
13+
toTypedSchema(v.pipe(v.string(), v.email('Invalid email'), v.nonEmpty('Required'))),
14+
);
1215
</script>

docs/src/components/examples/CompositionValibotBasic.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<script setup>
22
import { useForm } from 'vee-validate';
33
import { toTypedSchema } from '@vee-validate/valibot';
4-
import { email as emailValidator, string, minLength, object } from 'valibot';
4+
import * as v from 'valibot';
55
66
const { errors, defineField } = useForm({
77
validationSchema: toTypedSchema(
8-
object({
9-
email: string([minLength(1, 'Email is required'), emailValidator()]),
10-
password: string([minLength(6, 'password too short')]),
8+
v.object({
9+
email: v.pipe(v.string(), v.email('Invalid Email'), v.nonEmpty('Email is required')),
10+
password: v.pipe(v.string(), v.minLength(6, 'password too short')),
1111
}),
1212
),
1313
});

docs/src/pages/guide/composition-api/getting-started.mdx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,13 +285,13 @@ Then you can wrap your Valibot schemas with `toTypedSchema` function which allow
285285

286286
```ts
287287
import { useForm } from 'vee-validate';
288-
import { string, object, email, minLength } from 'valibot';
288+
import * as v from 'valibot';
289289
import { toTypedSchema } from '@vee-validate/valibot';
290290

291291
// Creates a typed schema for vee-validate
292292
const schema = toTypedSchema(
293-
object({
294-
email: string([minLength(1, 'required'), email()]),
293+
v.object({
294+
email: v.pipe(v.string(), v.email('Invalid email'), v.nonEmpty('required')),
295295
}),
296296
);
297297

docs/src/pages/guide/composition-api/typed-schema.mdx

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -299,15 +299,15 @@ This makes the form values and submitted values typed automatically and caters f
299299

300300
```ts
301301
import { useForm } from 'vee-validate';
302-
import { object, string, minLength } from 'valibot';
302+
import * as v from 'valibot';
303303
import { toTypedSchema } from '@vee-validate/valibot';
304304

305305
const { values, handleSubmit } = useForm({
306306
validationSchema: toTypedSchema(
307-
object({
308-
email: string([minLength(1, 'required')]),
309-
password: string([minLength(1, 'required')]),
310-
name: string(),
307+
v.object({
308+
name: v.pipe(string()),
309+
email: v.pipe(v.string() v.nonEmpty('required')),
310+
password: v.pipe(string(), v.minLength(6, 'Must be at least 6 characters')),
311311
}),
312312
),
313313
});
@@ -331,14 +331,14 @@ You can also define default values on your schema directly and it will be picked
331331

332332
```ts
333333
import { useForm } from 'vee-validate';
334-
import { object, string, optional, minLength } from 'valibot';
334+
import * as v from 'valibot';
335335
import { toTypedSchema } from '@vee-validate/valibot';
336336

337337
const { values, handleSubmit } = useForm({
338338
validationSchema: toTypedSchema(
339-
object({
340-
email: optional(string([minLength(1, 'required')]), '[email protected]'),
341-
password: optional(string([minLength(1, 'required')]), ''),
339+
v.object({
340+
email: v.optional(v.pipe(string(), v.nonEmpty('required')), '[email protected]'),
341+
password: optional(v.pipe(v.string(), v.nonEmpty('required')), ''),
342342
name: optional(string(), ''),
343343
}),
344344
),
@@ -353,13 +353,16 @@ You can also define transforms to cast your fields before submission:
353353

354354
```ts
355355
import { useForm } from 'vee-validate';
356-
import { object, number, coerce, any } from 'valibot';
356+
import * as v from 'valibot';
357357
import { toTypedSchema } from '@vee-validate/valibot';
358358

359359
const { values, handleSubmit } = useForm({
360360
validationSchema: toTypedSchema(
361361
object({
362-
age: coerce(any(), arg => Number(arg)),
362+
age: v.pipe(
363+
v.unknown(),
364+
v.transform(v => Number(v)),
365+
),
363366
}),
364367
),
365368
});

packages/valibot/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
],
3030
"dependencies": {
3131
"type-fest": "^4.8.3",
32-
"valibot": "^0.30.0",
32+
"valibot": "^0.31.0",
3333
"vee-validate": "4.13.0"
3434
}
3535
}

packages/valibot/src/index.ts

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,38 @@
11
import { PartialDeep } from 'type-fest';
22
import { cleanupNonNestedPath, isNotNestedPath, type TypedSchema, type TypedSchemaError } from 'vee-validate';
33
import {
4-
Output,
5-
Input,
4+
InferOutput,
5+
InferInput,
66
BaseSchema,
77
BaseSchemaAsync,
88
safeParseAsync,
99
safeParse,
10-
SchemaIssue,
10+
BaseIssue,
1111
getDefault,
1212
optional,
1313
ArraySchema,
1414
ObjectSchema,
15+
ErrorMessage,
16+
ArrayIssue,
17+
ObjectEntries,
18+
LooseObjectIssue,
19+
ObjectIssue,
20+
ObjectWithRestSchema,
21+
ObjectWithRestIssue,
22+
StrictObjectIssue,
23+
StrictObjectSchema,
24+
LooseObjectSchema,
25+
getDotPath,
1526
} from 'valibot';
1627
import { isIndex, isObject, merge, normalizeFormPath } from '../../shared';
1728

1829
export function toTypedSchema<
19-
TSchema extends BaseSchema | BaseSchemaAsync,
20-
TOutput = Output<TSchema>,
21-
TInput = PartialDeep<Input<TSchema>>,
22-
>(valibotSchema: TSchema): TypedSchema<TInput, TOutput> {
30+
TSchema extends
31+
| BaseSchema<unknown, unknown, BaseIssue<unknown>>
32+
| BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>,
33+
TInferOutput = InferOutput<TSchema>,
34+
TInferInput = PartialDeep<InferInput<TSchema>>,
35+
>(valibotSchema: TSchema): TypedSchema<TInferInput, TInferOutput> {
2336
const schema: TypedSchema = {
2437
__type: 'VVTypedSchema',
2538
async parse(value) {
@@ -92,10 +105,10 @@ export function toTypedSchema<
92105
return schema;
93106
}
94107

95-
function processIssues(issues: SchemaIssue[], errors: Record<string, TypedSchemaError>): void {
108+
function processIssues(issues: BaseIssue<unknown>[], errors: Record<string, TypedSchemaError>): void {
96109
issues.forEach(issue => {
97-
const path = normalizeFormPath((issue.path || []).map(p => p.key).join('.'));
98-
if (issue.issues?.length) {
110+
const path = normalizeFormPath(getDotPath(issue) || '');
111+
if (issue.issues) {
99112
processIssues(
100113
issue.issues.flatMap(ue => ue.issues || []),
101114
errors,
@@ -114,7 +127,10 @@ function processIssues(issues: SchemaIssue[], errors: Record<string, TypedSchema
114127
});
115128
}
116129

117-
function getSchemaForPath(path: string, schema: any): BaseSchema | null {
130+
function getSchemaForPath(
131+
path: string,
132+
schema: BaseSchema<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>,
133+
): BaseSchema<unknown, unknown, BaseIssue<unknown>> | null {
118134
if (!isObjectSchema(schema)) {
119135
return null;
120136
}
@@ -125,7 +141,7 @@ function getSchemaForPath(path: string, schema: any): BaseSchema | null {
125141

126142
const paths = (path || '').split(/\.|\[(\d+)\]/).filter(Boolean);
127143

128-
let currentSchema: BaseSchema = schema;
144+
let currentSchema: BaseSchema<unknown, unknown, BaseIssue<unknown>> = schema;
129145
for (let i = 0; i <= paths.length; i++) {
130146
const p = paths[i];
131147
if (!p || !currentSchema) {
@@ -145,14 +161,28 @@ function getSchemaForPath(path: string, schema: any): BaseSchema | null {
145161
return null;
146162
}
147163

148-
function queryOptional(schema: BaseSchema | BaseSchemaAsync): boolean {
149-
return (schema as any).type === 'optional';
164+
function queryOptional(
165+
schema: BaseSchema<unknown, unknown, BaseIssue<unknown>> | BaseSchemaAsync<unknown, unknown, BaseIssue<unknown>>,
166+
): boolean {
167+
return schema.type === 'optional';
150168
}
151169

152-
function isArraySchema(schema: unknown): schema is ArraySchema<any> {
153-
return isObject(schema) && schema.type === 'array';
170+
function isArraySchema(
171+
schema: unknown,
172+
): schema is ArraySchema<BaseSchema<unknown, unknown, BaseIssue<unknown>>, ErrorMessage<ArrayIssue> | undefined> {
173+
return isObject(schema) && 'item' in schema;
154174
}
155175

156-
function isObjectSchema(schema: unknown): schema is ObjectSchema<any> {
157-
return isObject(schema) && schema.type === 'object';
176+
function isObjectSchema(
177+
schema: unknown,
178+
): schema is
179+
| LooseObjectSchema<ObjectEntries, ErrorMessage<LooseObjectIssue> | undefined>
180+
| ObjectSchema<ObjectEntries, ErrorMessage<ObjectIssue> | undefined>
181+
| ObjectWithRestSchema<
182+
ObjectEntries,
183+
BaseSchema<unknown, unknown, BaseIssue<unknown>>,
184+
ErrorMessage<ObjectWithRestIssue> | undefined
185+
>
186+
| StrictObjectSchema<ObjectEntries, ErrorMessage<StrictObjectIssue> | undefined> {
187+
return isObject(schema) && 'entries' in schema;
158188
}

0 commit comments

Comments
 (0)