Skip to content

Commit f6892ed

Browse files
hannoeruubugeeei
andauthored
refactor: replace zod with valibot (#912)
* refactor: replace zod with valibot * refactor: validatedBody * chore: cspell config * chore: format --------- Co-authored-by: ubugeeei <[email protected]>
1 parent fc016e0 commit f6892ed

File tree

7 files changed

+237
-228
lines changed

7 files changed

+237
-228
lines changed

app/components/form/VFForm.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<script lang="ts">
2-
import type { ZodSchema } from "zod";
2+
import type * as v from "valibot";
33
import {
44
type FormFieldState,
55
type FormSubmitEvent,
66
Form as PForm,
77
} from "@primevue/forms";
8-
import { zodResolver } from "@primevue/forms/resolvers/zod";
8+
import { valibotResolver } from "@primevue/forms/resolvers/valibot";
99
import { useTemplateRef } from "#imports";
1010
1111
export { type FormSubmitEvent, type FormFieldState } from "@primevue/forms";
@@ -14,7 +14,8 @@ export interface FormProps<State extends object> {
1414
initialValues: State;
1515
/** @default "blur" */
1616
validateOn?: "blur" | "submit" | "immediate";
17-
schema?: ZodSchema<State>;
17+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
18+
schema?: v.ObjectSchema<any, any> | v.ObjectSchemaAsync<any, any>;
1819
}
1920
2021
export interface FormEmits {
@@ -55,7 +56,7 @@ defineExpose({
5556
v-slot="$form"
5657
ref="form"
5758
:initial-values
58-
:resolver="schema ? zodResolver(schema) : undefined"
59+
:resolver="schema ? valibotResolver(schema) : undefined"
5960
:validate-on-blur="validateOn === 'blur'"
6061
:validate-on-submit="validateOn === 'submit'"
6162
:validate-on-mount="validateOn === 'immediate'"

app/pages/_components/SectionContact.vue

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { z } from "zod";
2+
import * as v from "valibot";
33
import { HOME_HEADING_ID } from "~/constant";
44
import { reactive, useI18n, useRuntimeConfig } from "#imports";
55
import { VFSection, VFButton } from "#components";
@@ -12,20 +12,23 @@ const config = useRuntimeConfig();
1212
1313
const toast = useToast();
1414
15-
const schema = z.object({
16-
name: z
17-
.string()
18-
.nonempty(t("validation.required", { target: t("contactForm.formFields.name.label") })),
19-
email: z
20-
.string()
21-
.nonempty(t("validation.required", { target: t("contactForm.formFields.email.label") }))
22-
.email(t("validation.email")),
23-
content: z
24-
.string()
25-
.nonempty(t("validation.required", { target: t("contactForm.formFields.content.label") })),
15+
const schema = v.object({
16+
name: v.pipe(
17+
v.string(),
18+
v.nonEmpty(t("validation.required", { target: t("contactForm.formFields.name.label") })),
19+
),
20+
email: v.pipe(
21+
v.string(),
22+
v.nonEmpty(t("validation.required", { target: t("contactForm.formFields.email.label") })),
23+
v.email(t("validation.email")),
24+
),
25+
content: v.pipe(
26+
v.string(),
27+
v.nonEmpty(t("validation.required", { target: t("contactForm.formFields.content.label") })),
28+
),
2629
});
2730
28-
const state = reactive<z.infer<typeof schema>>({
31+
const state = reactive<v.InferOutput<typeof schema>>({
2932
name: "",
3033
email: "",
3134
content: "",

app/pages/ticket/[userId]/edit/index.vue

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script setup lang="ts">
2-
import { z } from "zod";
2+
import * as v from "valibot";
33
import emojiRegex from "emoji-regex";
44
55
import { useLocaleRoute } from "@typed-router";
@@ -51,52 +51,51 @@ const sizeInMB = (sizeInBytes: number, decimalsNum = 2) => {
5151
return +result.toFixed(decimalsNum);
5252
};
5353
54-
const schema = z.object({
55-
name: z
56-
.string()
57-
.min(1, t("nameBadge.form.name.error.required"))
58-
.refine(
54+
const schema = v.objectAsync({
55+
name: v.pipe(
56+
v.string(),
57+
v.minLength(1, t("nameBadge.form.name.error.required")),
58+
v.check(
5959
(value) => {
6060
const len = [...value].reduce(
6161
(len, char) => len + (/[\u2E80-\u9FFF\uF900-\uFAFF\uFF00-\uFFEF]/.test(char) ? 2 : 1),
6262
0,
6363
);
6464
return len <= 24;
6565
},
66-
{ message: t("nameBadge.form.name.error.tooLong") },
67-
)
68-
.refine(
66+
t("nameBadge.form.name.error.tooLong"),
67+
),
68+
v.check(
6969
value => !emojiRegex().test(value),
70-
{ message: t("nameBadge.form.name.error.emoji") },
70+
t("nameBadge.form.name.error.emoji"),
7171
),
72+
),
73+
74+
salesId: v.pipe(
75+
v.string(),
76+
v.minLength(1, t("nameBadge.form.receipt.error.required")),
77+
),
7278
73-
salesId: z
74-
.string()
75-
.min(1, t("nameBadge.form.receipt.error.required")),
76-
77-
avatarImage: z
78-
.custom<VFFile>()
79-
.refine(
80-
file => !!file,
81-
{ message: t("nameBadge.form.avatarImage.error.required") },
82-
)
83-
.refine(
84-
async (file) => {
79+
avatarImage: v.pipeAsync(
80+
v.custom<VFFile>((input: unknown): input is VFFile => !!input),
81+
v.checkAsync(
82+
async (file: VFFile) => {
8583
const { size } = await fetch(file.objectURL).then(r => r.blob());
8684
return sizeInMB(size) <= 5;
8785
},
88-
{ message: t("nameBadge.form.avatarImage.error.size") },
89-
)
90-
.refine(
91-
file => ["image/jpg", "image/jpeg", "image/png"].includes(file.type),
92-
{ message: t("nameBadge.form.avatarImage.error.type") },
86+
t("nameBadge.form.avatarImage.error.size"),
87+
),
88+
v.check(
89+
(file: VFFile) => ["image/jpg", "image/jpeg", "image/png"].includes(file.type),
90+
t("nameBadge.form.avatarImage.error.type"),
9391
),
92+
),
9493
});
9594
9695
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9796
const form = useTemplateRef<any>("form");
98-
const currentStates = computed(() => form.value?.currentState?.() as FormFieldStates<z.infer<typeof schema>> | undefined);
99-
const initialValues = ref<Partial<z.infer<typeof schema>>>({
97+
const currentStates = computed(() => form.value?.currentState?.() as FormFieldStates<v.InferOutput<typeof schema>> | undefined);
98+
const initialValues = ref<Partial<v.InferOutput<typeof schema>>>({
10099
name: "",
101100
salesId: "",
102101
avatarImage: undefined,

cspell.config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ words:
151151
- uniquevision
152152
- unplugin
153153
- vados
154+
- valibot
154155
- Vashishtha
155156
- vejhszh
156157
- visasq

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@
112112
"vue-router": "latest",
113113
"vue-tsc": "^2.2.8",
114114
"wrangler": "^4.23.0",
115-
"zod": "^3.24.2"
115+
"valibot": "^1.1.0"
116116
},
117117
"eslintConfig": {
118118
"extends": [

0 commit comments

Comments
 (0)