Skip to content

Commit b50325a

Browse files
author
JB AUBREE
committed
refactor: optimize bundle size
1 parent 097b745 commit b50325a

21 files changed

+264
-290
lines changed

src/errors.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import type { FieldErrors, Form, GetErrorsFn, InputSchema } from './types'
2-
import { getJoiErrors, isJoiSchema } from './joi'
3-
import { getSuperStructErrors, isSuperStructSchema } from './superstruct'
4-
import { getValibotErrors, isValibotSchema } from './valibot'
5-
import { getYupErrors, isYupSchema } from './yup'
6-
import { getZodErrors, isZodSchema } from './zod'
2+
import { validators } from './validators'
73

84
export async function getErrors<S extends InputSchema<F>, F extends Form>(
95
schema: S,
@@ -19,23 +15,12 @@ export async function getErrors<S, F extends Form>(
1915
form: F,
2016
transformFn?: GetErrorsFn<S, F>,
2117
): Promise<FieldErrors<F>> {
22-
if (transformFn) {
18+
if (transformFn)
2319
return await transformFn(schema, form)
24-
}
25-
if (isZodSchema(schema)) {
26-
return await getZodErrors<F>(schema, form)
27-
}
28-
if (isYupSchema<F>(schema)) {
29-
return await getYupErrors<F>(schema, form)
30-
}
31-
if (isJoiSchema(schema)) {
32-
return await getJoiErrors<F>(schema, form)
33-
}
34-
if (isValibotSchema(schema)) {
35-
return getValibotErrors<F>(schema, form)
36-
}
37-
if (isSuperStructSchema<S, F>(schema)) {
38-
return getSuperStructErrors<S, F>(schema, form)
20+
for (const validator of Object.values(validators)) {
21+
if (validator.check(schema)) {
22+
return await validator.getErrors(schema, form)
23+
}
3924
}
4025
return {}
4126
}

src/joi.ts

Lines changed: 0 additions & 30 deletions
This file was deleted.

src/polyfill.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
export function polyfillGroupBy<T>(): void {
22
if (!Object.groupBy) {
33
Object.defineProperty(Object, 'groupBy', {
4-
value(array: T[], keyGetter: (item: T) => string): Record<string, T[]> {
5-
return array.reduce((result, currentItem) => {
6-
const key = keyGetter(currentItem)
7-
if (!result[key]) {
8-
result[key] = []
9-
}
10-
result[key].push(currentItem)
11-
return result
12-
}, {} as Record<string, T[]>)
13-
},
4+
value: (array: T[], keyGetter: (item: T) => string): Record<string, T[]> => array.reduce((result: Record<string, T[]>, item: T) => {
5+
const key = keyGetter(item);
6+
(result[key] ??= []).push(item)
7+
return result
8+
}, {}),
149
writable: true,
1510
configurable: true,
1611
})

src/superstruct.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/types.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,11 @@ interface YupSchema<F> extends AnyObject {
1212
interface ValibotSchema<F> extends AnyObject {
1313
entries: Record<keyof F, unknown>
1414
}
15-
// interface JoiTerms<F> extends AnyObject {
16-
// keys: Array<{ key: keyof F, schema: unknown }>
17-
// }
18-
// interface JoiSchema<F> extends AnyObject {
19-
// $_terms: JoiTerms<F>
20-
// }
2115
interface SuperstructSchema<F> extends AnyObject {
2216
schema: Record<keyof F, unknown>
2317
}
2418

19+
export type Validator = 'Joi' | 'SuperStruct' | 'Valibot' | 'Yup' | 'Zod'
2520
export type Awaitable<T> = T | PromiseLike<T>
2621
export type FieldErrors<F> = Partial<Record<keyof F, string>>
2722
export type Form = Record<string, unknown>

src/useFormValidation.ts

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function useFormValidation<S extends InputSchema<F>, F extends Form>(
1919
options?: { mode?: 'eager' | 'lazy', transformFn?: GetErrorsFn<S, F> },
2020
): ReturnType<F> {
2121
polyfillGroupBy()
22-
const opts = Object.assign({}, { mode: 'lazy', transformFn: undefined }, options)
22+
const opts = { mode: 'lazy', transformFn: null, ...options }
2323

2424
const errors = shallowRef<FieldErrors<F>>({})
2525

@@ -34,36 +34,22 @@ export function useFormValidation<S extends InputSchema<F>, F extends Form>(
3434
}
3535
const getErrorMessage = (path: keyof F): string | undefined => errors.value[path]
3636

37-
let unwatch: null | (() => void) = null
38-
const validationWatch: () => void = () => {
39-
if (unwatch !== null)
40-
return
41-
unwatch = watch(
42-
() => toValue(form),
43-
async () => {
44-
// eslint-disable-next-line ts/no-use-before-define
45-
await validate()
46-
},
47-
{ deep: true },
48-
)
49-
}
50-
5137
const validate = async (): Promise<FieldErrors<F>> => {
5238
isLoading.value = true
5339
clearErrors()
5440
errors.value = opts.transformFn
5541
? await getErrors<S, F>(toValue(schema), toValue(form), opts.transformFn)
5642
: await getErrors<S, F>(toValue(schema), toValue(form))
57-
if (hasError.value) {
58-
validationWatch()
59-
}
43+
44+
if (hasError.value)
45+
// eslint-disable-next-line ts/no-use-before-define
46+
watchFormChanges()
6047
isLoading.value = false
6148
return errors.value
6249
}
6350

6451
const focusInput = ({ inputName }: { inputName: keyof F }): void => {
65-
const element: HTMLInputElement | null = document.querySelector(`input[name="${inputName.toString()}"]`)
66-
element?.focus()
52+
(document.querySelector(`input[name="${inputName.toString()}"]`) as HTMLInputElement | null)?.focus()
6753
}
6854
const focusFirstErroredInput = (): void => {
6955
for (const key in toValue(form)) {
@@ -74,10 +60,15 @@ export function useFormValidation<S extends InputSchema<F>, F extends Form>(
7460
}
7561
}
7662

77-
if (opts.mode === 'eager') {
78-
validationWatch()
63+
let unwatch: null | (() => void)
64+
const watchFormChanges = (): void | (() => void) => {
65+
if (!unwatch)
66+
unwatch = watch(() => toValue(form), validate, { deep: true })
7967
}
8068

69+
if (opts.mode === 'eager')
70+
watchFormChanges()
71+
8172
return {
8273
validate,
8374
errors,

src/valibot.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/validators/index.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { Awaitable, FieldErrors, Form, Validator } from '../types'
2+
import { Joi } from './joi'
3+
import { SuperStruct } from './superstruct'
4+
import { Valibot } from './valibot'
5+
import { Yup } from './yup'
6+
import { Zod } from './zod'
7+
8+
export const validators: Record<Validator, {
9+
check: (schema: unknown) => boolean
10+
errorCheck?: (error: unknown) => boolean
11+
getErrors: <F extends Form>(schema: any, form: F) => Awaitable<FieldErrors<F>>
12+
}> = {
13+
Joi,
14+
SuperStruct,
15+
Valibot,
16+
Yup,
17+
Zod,
18+
}

src/validators/joi.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import type { ValidationError as JoiError, Schema as JoiSchema } from 'joi'
2+
import type { FieldErrors, Form } from '../types'
3+
import { isNonNullObject } from '../utils'
4+
5+
export const Joi = {
6+
check: (schema: unknown): schema is JoiSchema => isNonNullObject(schema) && 'validateAsync' in schema,
7+
errorCheck: (error: unknown): error is JoiError => isNonNullObject(error) && error.isJoi === true,
8+
async getErrors<F extends Form>(schema: JoiSchema, form: F): Promise<FieldErrors<F>> {
9+
const errors: FieldErrors<F> = {}
10+
try {
11+
await schema.validateAsync(form, { abortEarly: false })
12+
}
13+
catch (error) {
14+
if (Joi.errorCheck(error)) {
15+
error.details.forEach(i => errors[i.path[0] as keyof F] = i.message)
16+
}
17+
}
18+
return errors
19+
},
20+
}

src/validators/superstruct.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type { Struct } from 'superstruct'
2+
import type { FieldErrors, Form } from '../types'
3+
import { isNonNullObject } from '../utils'
4+
5+
export const SuperStruct = {
6+
check: (schema: unknown) => isNonNullObject(schema) && 'validator' in schema,
7+
getErrors<S, F extends Form>(schema: Struct<F, S>, form: F): FieldErrors<F> {
8+
const errors: FieldErrors<F> = {}
9+
const [structError] = schema.validate(form)
10+
structError?.failures().forEach(i => errors[i.path[0] as keyof F] = i.message)
11+
return errors
12+
},
13+
}

0 commit comments

Comments
 (0)