Skip to content

Commit c0d528b

Browse files
authored
feat: add VineJS resolver (#677)
1 parent a3e50c6 commit c0d528b

File tree

13 files changed

+651
-3
lines changed

13 files changed

+651
-3
lines changed

README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
- [ArkType](#arktype)
4747
- [Valibot](#valibot)
4848
- [effect-ts](#effect-ts)
49+
- [VineJS](#vinejs)
4950
- [Backers](#backers)
5051
- [Sponsors](#sponsors)
5152
- [Contributors](#contributors)
@@ -629,6 +630,41 @@ function TestComponent({ onSubmit }: Props) {
629630
}
630631
```
631632

633+
### [VineJS](https://github.com/vinejs/vine)
634+
635+
VineJS is a form data validation library for Node.js
636+
637+
[![npm](https://img.shields.io/bundlephobia/minzip/@vinejs/vine?style=for-the-badge)](https://bundlephobia.com/result?p=@vinejs/vine)
638+
639+
```typescript jsx
640+
import { useForm } from 'react-hook-form';
641+
import { vineResolver } from '@hookform/resolvers/vine';
642+
import vine from '@vinejs/vine';
643+
644+
const schema = vine.compile(
645+
vine.object({
646+
username: vine.string().minLength(1),
647+
password: vine.string().minLength(1),
648+
}),
649+
);
650+
651+
const App = () => {
652+
const { register, handleSubmit } = useForm({
653+
resolver: vineResolver(schema),
654+
});
655+
656+
return (
657+
<form onSubmit={handleSubmit((d) => console.log(d))}>
658+
<input {...register('username')} />
659+
{errors.username && <span role="alert">{errors.username.message}</span>}
660+
<input {...register('password')} />
661+
{errors.password && <span role="alert">{errors.password.message}</span>}
662+
<button type="submit">submit</button>
663+
</form>
664+
);
665+
};
666+
```
667+
632668
## Backers
633669

634670
Thanks go to all our backers! [[Become a backer](https://opencollective.com/react-hook-form#backer)].

config/node-13-exports.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const subRepositories = [
1717
'arktype',
1818
'valibot',
1919
'effect-ts',
20+
'vine',
2021
];
2122

2223
const copySrc = () => {

package.json

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@hookform/resolvers",
33
"amdName": "hookformResolvers",
44
"version": "2.9.1",
5-
"description": "React Hook Form validation resolvers: Yup, Joi, Superstruct, Zod, Vest, Class Validator, io-ts, Nope, computed-types, TypeBox, arktype, Typanion and Effect-TS",
5+
"description": "React Hook Form validation resolvers: Yup, Joi, Superstruct, Zod, Vest, Class Validator, io-ts, Nope, computed-types, TypeBox, arktype, Typanion, Effect-TS and VineJS",
66
"main": "dist/resolvers.js",
77
"module": "dist/resolvers.module.js",
88
"umd:main": "dist/resolvers.umd.js",
@@ -105,6 +105,12 @@
105105
"import": "./effect-ts/dist/effect-ts.mjs",
106106
"require": "./effect-ts/dist/effect-ts.js"
107107
},
108+
"./vine": {
109+
"types": "./vine/dist/index.d.ts",
110+
"umd": "./vine/dist/vine.umd.js",
111+
"import": "./vine/dist/vine.mjs",
112+
"require": "./vine/dist/vine.js"
113+
},
108114
"./package.json": "./package.json",
109115
"./*": "./*"
110116
},
@@ -154,7 +160,10 @@
154160
"valibot/dist",
155161
"effect-ts/package.json",
156162
"effect-ts/src",
157-
"effect-ts/dist"
163+
"effect-ts/dist",
164+
"vine/package.json",
165+
"vine/src",
166+
"vine/dist"
158167
],
159168
"publishConfig": {
160169
"access": "public"
@@ -178,6 +187,7 @@
178187
"build:arktype": "microbundle --cwd arktype --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm",
179188
"build:valibot": "microbundle --cwd valibot --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm",
180189
"build:effect-ts": "microbundle --cwd effect-ts --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,@effect/schema=EffectSchema,@effect/schema/AST=EffectSchemaAST,@effect/schema/ArrayFormatter=EffectSchemaArrayFormatter",
190+
"build:vine": "microbundle --cwd vine --globals @hookform/resolvers=hookformResolvers,react-hook-form=ReactHookForm,@vinejs/vine=vine",
181191
"postbuild": "node ./config/node-13-exports.js",
182192
"lint": "eslint . --ext .ts,.js --ignore-path .gitignore",
183193
"lint:types": "tsc",
@@ -207,7 +217,8 @@
207217
"typanion",
208218
"ajv",
209219
"TypeBox",
210-
"arktype"
220+
"arktype",
221+
"vine"
211222
],
212223
"repository": {
213224
"type": "git",
@@ -230,6 +241,7 @@
230241
"@types/react": "^18.2.20",
231242
"@typescript-eslint/eslint-plugin": "^6.4.1",
232243
"@typescript-eslint/parser": "^6.4.1",
244+
"@vinejs/vine": "^2.0.0",
233245
"@vitejs/plugin-react": "^4.0.4",
234246
"ajv": "^8.12.0",
235247
"ajv-errors": "^3.0.0",

pnpm-lock.yaml

Lines changed: 57 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

vine/package.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"name": "@hookform/resolvers/vine",
3+
"amdName": "hookformResolversVine",
4+
"version": "1.0.0",
5+
"private": true,
6+
"description": "React Hook Form validation resolver: vine",
7+
"main": "dist/vine.js",
8+
"module": "dist/vine.module.js",
9+
"umd:main": "dist/vine.umd.js",
10+
"source": "src/index.ts",
11+
"types": "dist/index.d.ts",
12+
"license": "MIT",
13+
"peerDependencies": {
14+
"react-hook-form": "^7.0.0",
15+
"@hookform/resolvers": "^2.0.0",
16+
"@vinejs/vine": "^2.0.0"
17+
}
18+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import React from 'react';
2+
import { useForm } from 'react-hook-form';
3+
import { render, screen } from '@testing-library/react';
4+
import user from '@testing-library/user-event';
5+
import { vineResolver } from '..';
6+
import vine from '@vinejs/vine';
7+
import { Infer } from '@vinejs/vine/build/src/types';
8+
9+
const schema = vine.compile(
10+
vine.object({
11+
username: vine.string().minLength(1),
12+
password: vine.string().minLength(1),
13+
}),
14+
);
15+
16+
type FormData = Infer<typeof schema> & { unusedProperty: string };
17+
18+
interface Props {
19+
onSubmit: (data: FormData) => void;
20+
}
21+
22+
function TestComponent({ onSubmit }: Props) {
23+
const { register, handleSubmit } = useForm<FormData>({
24+
resolver: vineResolver(schema),
25+
shouldUseNativeValidation: true,
26+
});
27+
28+
return (
29+
<form onSubmit={handleSubmit(onSubmit)}>
30+
<input {...register('username')} placeholder="username" />
31+
32+
<input {...register('password')} placeholder="password" />
33+
34+
<button type="submit">submit</button>
35+
</form>
36+
);
37+
}
38+
39+
test("form's native validation with Zod", async () => {
40+
const handleSubmit = vi.fn();
41+
render(<TestComponent onSubmit={handleSubmit} />);
42+
43+
// username
44+
let usernameField = screen.getByPlaceholderText(
45+
/username/i,
46+
) as HTMLInputElement;
47+
expect(usernameField.validity.valid).toBe(true);
48+
expect(usernameField.validationMessage).toBe('');
49+
50+
// password
51+
let passwordField = screen.getByPlaceholderText(
52+
/password/i,
53+
) as HTMLInputElement;
54+
expect(passwordField.validity.valid).toBe(true);
55+
expect(passwordField.validationMessage).toBe('');
56+
57+
await user.click(screen.getByText(/submit/i));
58+
59+
// username
60+
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
61+
expect(usernameField.validity.valid).toBe(false);
62+
expect(usernameField.validationMessage).toBe('The username field must have at least 1 characters');
63+
64+
// password
65+
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
66+
expect(passwordField.validity.valid).toBe(false);
67+
expect(passwordField.validationMessage).toBe('The password field must have at least 1 characters');
68+
69+
await user.type(screen.getByPlaceholderText(/username/i), 'joe');
70+
await user.type(screen.getByPlaceholderText(/password/i), 'password');
71+
72+
// username
73+
usernameField = screen.getByPlaceholderText(/username/i) as HTMLInputElement;
74+
expect(usernameField.validity.valid).toBe(true);
75+
expect(usernameField.validationMessage).toBe('');
76+
77+
// password
78+
passwordField = screen.getByPlaceholderText(/password/i) as HTMLInputElement;
79+
expect(passwordField.validity.valid).toBe(true);
80+
expect(passwordField.validationMessage).toBe('');
81+
});

0 commit comments

Comments
 (0)