@hookform/resolvers 6.0.1 → 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -21
- package/package.json +7 -7
- package/typebox/dist/typebox.d.ts +8 -9
- package/typebox/dist/typebox.js +1 -1
- package/typebox/dist/typebox.js.map +1 -1
- package/typebox/dist/typebox.mjs +1 -1
- package/typebox/dist/typebox.modern.mjs +1 -1
- package/typebox/dist/typebox.modern.mjs.map +1 -1
- package/typebox/dist/typebox.module.js +1 -1
- package/typebox/dist/typebox.module.js.map +1 -1
- package/typebox/dist/typebox.umd.js +1 -1
- package/typebox/dist/typebox.umd.js.map +1 -1
- package/typebox/package.json +1 -1
- package/typebox/src/__tests__/Form-compiler.tsx +7 -5
- package/typebox/src/__tests__/Form-native-validation-compiler.tsx +5 -5
- package/typebox/src/__tests__/Form-native-validation.tsx +3 -3
- package/typebox/src/__tests__/Form.tsx +2 -2
- package/typebox/src/__tests__/__fixtures__/data.ts +4 -8
- package/typebox/src/__tests__/__snapshots__/typebox-compiler.ts.snap +56 -63
- package/typebox/src/__tests__/__snapshots__/typebox.ts.snap +56 -63
- package/typebox/src/__tests__/special-path-names.ts +137 -6
- package/typebox/src/__tests__/typebox-compiler.ts +2 -2
- package/typebox/src/__tests__/typebox.ts +36 -8
- package/typebox/src/typebox.ts +59 -29
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { TypeCompiler } from '@sinclair/typebox/compiler';
|
|
3
1
|
import { renderHook } from '@testing-library/react';
|
|
4
2
|
import { Resolver, SubmitHandler, useForm } from 'react-hook-form';
|
|
3
|
+
import { Type } from 'typebox';
|
|
4
|
+
import { Compile } from 'typebox/compile';
|
|
5
5
|
import { typeboxResolver } from '..';
|
|
6
6
|
import { fields, invalidData, schema, validData } from './__fixtures__/data';
|
|
7
7
|
|
|
@@ -47,8 +47,8 @@ describe('typeboxResolver', () => {
|
|
|
47
47
|
>();
|
|
48
48
|
});
|
|
49
49
|
|
|
50
|
-
it('should correctly infer the output type from a typebox schema with
|
|
51
|
-
const typecheck =
|
|
50
|
+
it('should correctly infer the output type from a typebox schema with Compile', () => {
|
|
51
|
+
const typecheck = Compile(Type.Object({ id: Type.Number() }));
|
|
52
52
|
const resolver = typeboxResolver(typecheck);
|
|
53
53
|
|
|
54
54
|
expectTypeOf(resolver).toEqualTypeOf<
|
|
@@ -56,10 +56,38 @@ describe('typeboxResolver', () => {
|
|
|
56
56
|
>();
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
it('should correctly infer the output type from a typebox schema with Compile and a reference', () => {
|
|
60
|
+
const typecheck = Compile(
|
|
61
|
+
{ Name: Type.String() },
|
|
62
|
+
Type.Object({ name: Type.Ref('Name') }),
|
|
63
|
+
);
|
|
64
|
+
const resolver = typeboxResolver(typecheck);
|
|
65
|
+
|
|
66
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
67
|
+
Resolver<{ name: string }, unknown, { name: string }>
|
|
68
|
+
>();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should correctly infer the output type from a typebox schema with Compile and a reference using a transform', () => {
|
|
72
|
+
const typecheck = Compile(
|
|
73
|
+
{
|
|
74
|
+
Id: Type.Codec(Type.Number())
|
|
75
|
+
.Decode((v) => String(v))
|
|
76
|
+
.Encode((v) => Number.parseInt(v)),
|
|
77
|
+
},
|
|
78
|
+
Type.Object({ id: Type.Ref('Id') }),
|
|
79
|
+
);
|
|
80
|
+
const resolver = typeboxResolver(typecheck);
|
|
81
|
+
|
|
82
|
+
expectTypeOf(resolver).toEqualTypeOf<
|
|
83
|
+
Resolver<{ id: number }, unknown, { id: string }>
|
|
84
|
+
>();
|
|
85
|
+
});
|
|
86
|
+
|
|
59
87
|
it('should correctly infer the output type from a typebox schema using a transform', () => {
|
|
60
88
|
const resolver = typeboxResolver(
|
|
61
89
|
Type.Object({
|
|
62
|
-
id: Type.
|
|
90
|
+
id: Type.Codec(Type.Number())
|
|
63
91
|
.Decode((v) => String(v))
|
|
64
92
|
.Encode((v) => Number.parseInt(v)),
|
|
65
93
|
}),
|
|
@@ -93,8 +121,8 @@ describe('typeboxResolver', () => {
|
|
|
93
121
|
>();
|
|
94
122
|
});
|
|
95
123
|
|
|
96
|
-
it('should correctly infer the output type from a typebox schema with
|
|
97
|
-
const typecheck =
|
|
124
|
+
it('should correctly infer the output type from a typebox schema with Compile for the handleSubmit function in useForm', () => {
|
|
125
|
+
const typecheck = Compile(Type.Object({ id: Type.Number() }));
|
|
98
126
|
|
|
99
127
|
const {
|
|
100
128
|
result: { current: form },
|
|
@@ -118,7 +146,7 @@ describe('typeboxResolver', () => {
|
|
|
118
146
|
|
|
119
147
|
it('should correctly infer the output type from a typebox schema with a transform for the handleSubmit function in useForm', () => {
|
|
120
148
|
const schema = Type.Object({
|
|
121
|
-
id: Type.
|
|
149
|
+
id: Type.Codec(Type.Number())
|
|
122
150
|
.Decode((v) => String(v))
|
|
123
151
|
.Encode((v) => Number.parseInt(v)),
|
|
124
152
|
});
|
package/typebox/src/typebox.ts
CHANGED
|
@@ -1,40 +1,63 @@
|
|
|
1
1
|
import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
|
|
2
|
-
import { Static, StaticDecode, TObject } from '@sinclair/typebox';
|
|
3
|
-
import { TypeCheck } from '@sinclair/typebox/compiler';
|
|
4
|
-
import { Value, type ValueError } from '@sinclair/typebox/value';
|
|
5
2
|
import { FieldError, Resolver, appendErrors } from 'react-hook-form';
|
|
3
|
+
import type { Static, StaticDecode, TObject, TProperties } from 'typebox';
|
|
4
|
+
import { Validator } from 'typebox/compile';
|
|
5
|
+
import type { TLocalizedValidationError } from 'typebox/error';
|
|
6
|
+
import { Locale } from 'typebox/system';
|
|
7
|
+
import { Value } from 'typebox/value';
|
|
8
|
+
|
|
9
|
+
// TypeBox reports a single `required` error on the parent object listing every
|
|
10
|
+
// missing property; split it into one error per missing field so each one lands
|
|
11
|
+
// on its own path, re-localizing the message for that single property.
|
|
12
|
+
function expandRequiredErrors(
|
|
13
|
+
errors: TLocalizedValidationError[],
|
|
14
|
+
): TLocalizedValidationError[] {
|
|
15
|
+
return errors.flatMap<TLocalizedValidationError>((error) =>
|
|
16
|
+
error.keyword === 'required'
|
|
17
|
+
? error.params.requiredProperties.map((property) => {
|
|
18
|
+
const single = {
|
|
19
|
+
...error,
|
|
20
|
+
instancePath: `${error.instancePath}/${property.replace(/~/g, '~0').replace(/\//g, '~1')}`,
|
|
21
|
+
params: { requiredProperties: [property] },
|
|
22
|
+
};
|
|
23
|
+
return { ...single, message: Locale.Get()(single) };
|
|
24
|
+
})
|
|
25
|
+
: [error],
|
|
26
|
+
);
|
|
27
|
+
}
|
|
6
28
|
|
|
7
29
|
function parseErrorSchema(
|
|
8
|
-
_errors:
|
|
30
|
+
_errors: TLocalizedValidationError[],
|
|
9
31
|
validateAllFieldCriteria: boolean,
|
|
10
32
|
) {
|
|
11
33
|
// See the matching comment in the zod resolver's `parseZod3Issues`.
|
|
12
34
|
const errors: Record<string, FieldError> = Object.create(null);
|
|
13
|
-
for (
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
35
|
+
for (const error of _errors) {
|
|
36
|
+
const { keyword: type, message, instancePath } = error;
|
|
37
|
+
const _path = instancePath
|
|
38
|
+
.split('/')
|
|
39
|
+
.slice(1)
|
|
40
|
+
.map((segment) => segment.replace(/~1/g, '/').replace(/~0/g, '~'))
|
|
41
|
+
.join('.');
|
|
17
42
|
|
|
18
43
|
if (!errors[_path]) {
|
|
19
|
-
errors[_path] = { message, type
|
|
44
|
+
errors[_path] = { message, type };
|
|
20
45
|
}
|
|
21
46
|
|
|
22
47
|
if (validateAllFieldCriteria) {
|
|
23
48
|
const types = errors[_path].types;
|
|
24
|
-
const messages = types && types[
|
|
49
|
+
const messages = types && types[type];
|
|
25
50
|
|
|
26
51
|
errors[_path] = appendErrors(
|
|
27
52
|
_path,
|
|
28
53
|
validateAllFieldCriteria,
|
|
29
54
|
errors,
|
|
30
|
-
|
|
55
|
+
type,
|
|
31
56
|
messages
|
|
32
|
-
? ([] as string[]).concat(messages as string[],
|
|
33
|
-
:
|
|
57
|
+
? ([] as string[]).concat(messages as string[], message)
|
|
58
|
+
: message,
|
|
34
59
|
) as FieldError;
|
|
35
60
|
}
|
|
36
|
-
|
|
37
|
-
_errors.shift();
|
|
38
61
|
}
|
|
39
62
|
|
|
40
63
|
return errors;
|
|
@@ -42,10 +65,10 @@ function parseErrorSchema(
|
|
|
42
65
|
|
|
43
66
|
/**
|
|
44
67
|
* Creates a resolver for react-hook-form using Typebox schema validation
|
|
45
|
-
* @param {Schema |
|
|
68
|
+
* @param {Schema | Validator<TypeContext, Schema>} schema - The Typebox schema to validate against
|
|
46
69
|
* @param {Object} options - Additional resolver configuration
|
|
47
70
|
* @param {string} [options.mode='async'] - Validation mode
|
|
48
|
-
* @returns {Resolver<Static<Schema>>} A resolver function compatible with react-hook-form
|
|
71
|
+
* @returns {Resolver<Static<Schema, TypeContext>>} A resolver function compatible with react-hook-form
|
|
49
72
|
* @example
|
|
50
73
|
* const schema = Type.Object({
|
|
51
74
|
* name: Type.String(),
|
|
@@ -56,18 +79,25 @@ function parseErrorSchema(
|
|
|
56
79
|
* resolver: typeboxResolver(schema)
|
|
57
80
|
* });
|
|
58
81
|
*
|
|
59
|
-
* //
|
|
60
|
-
* //
|
|
61
|
-
* //
|
|
62
|
-
* // TypeRegistry.Set('Files', (schema, value) => Array.isArray(value));
|
|
82
|
+
* // Custom string formats must be registered with TypeBox's own `Format` registry
|
|
83
|
+
* // before validating, since this resolver validates via `typebox`'s `Value.Errors`:
|
|
84
|
+
* // Format.Set('slug', (value) => /^[a-z0-9-]+$/.test(value));
|
|
63
85
|
*/
|
|
64
|
-
export function typeboxResolver<
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
86
|
+
export function typeboxResolver<
|
|
87
|
+
Schema extends TObject,
|
|
88
|
+
Context,
|
|
89
|
+
TypeContext extends TProperties = Record<never, never>,
|
|
90
|
+
>(
|
|
91
|
+
schema: Schema | Validator<TypeContext, Schema>,
|
|
92
|
+
): Resolver<
|
|
93
|
+
Static<Schema, TypeContext>,
|
|
94
|
+
Context,
|
|
95
|
+
StaticDecode<Schema, TypeContext>
|
|
96
|
+
> {
|
|
97
|
+
return async (values: Static<Schema, TypeContext>, _, options) => {
|
|
98
|
+
const errors = expandRequiredErrors(
|
|
99
|
+
schema instanceof Validator
|
|
100
|
+
? (schema as Validator<TypeContext, Schema>).Errors(values)
|
|
71
101
|
: Value.Errors(schema, values),
|
|
72
102
|
);
|
|
73
103
|
|
|
@@ -76,7 +106,7 @@ export function typeboxResolver<Schema extends TObject, Context>(
|
|
|
76
106
|
if (!errors.length) {
|
|
77
107
|
return {
|
|
78
108
|
errors: {},
|
|
79
|
-
values,
|
|
109
|
+
values: values as StaticDecode<Schema, TypeContext>,
|
|
80
110
|
};
|
|
81
111
|
}
|
|
82
112
|
|