@coxy/react-validator 4.0.0 → 5.0.1

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 CHANGED
@@ -71,41 +71,17 @@ Create your own rules easily, or use the built-in ones:
71
71
 
72
72
  ```javascript
73
73
  const rules = {
74
- email: [
75
- { rule: value => value.trim() !== '', message: 'Value is required' },
76
- { rule: value => value.includes('@'), message: '@ is required' },
77
- ],
78
- password: [
79
- { rule: value => value.length >= 6, message: 'Password must be at least 6 characters' },
80
- ],
81
- notEmpty: [
82
- { rule: value => !!value && value.trim() !== '', message: 'Field cannot be empty' },
83
- ],
84
- boolean: [
85
- { rule: value => typeof value === 'boolean', message: 'Must be a boolean value' },
86
- ],
87
- min: (min) => ([
88
- { rule: value => Number(value) >= min, message: `Must be at least ${min}` },
89
- ]),
90
- max: (max) => ([
91
- { rule: value => Number(value) <= max, message: `Must be at most ${max}` },
92
- ]),
93
- length: (min, max) => ([
94
- { rule: value => value.length >= min, message: `Must be at least ${min} characters` },
95
- ...(max ? [{ rule: value => value.length <= max, message: `Must be at most ${max} characters` }] : [])
96
- ]),
97
- };
74
+ notEmpty: [z.string().min(1, { error: 'Field is required' })],
75
+ isTrue: [z.boolean({ error: 'Value is required' }).and(z.literal(true))],
76
+ email: [z.string().min(1, { error: 'Email is required' }), z.email({ message: 'Email is invalid' })],
77
+ }
98
78
  ```
99
79
 
100
- | Name | Type | Description |
101
- |-------------|----------|-----------------------------------------------------------|
102
- | email | Array | Validate non-empty email with regex check. |
103
- | password | Array | Validate non-empty password with min length. |
104
- | notEmpty | Array | Check if a string is not empty. |
105
- | boolean | Array | Ensure a boolean value is present. |
106
- | min(n) | Function | Validate number is >= `n`. |
107
- | max(n) | Function | Validate number is <= `n`. |
108
- | length(a,b) | Function | Validate string length is between `a` and `b` (optional). |
80
+ | Name | Type | Description |
81
+ |----------|-------|--------------------------------------------|
82
+ | email | Array | Validate non-empty email with regex check. |
83
+ | notEmpty | Array | Check if a string is not empty. |
84
+ | isTrue | Array | Ensure a boolean value is present. |
109
85
 
110
86
  ---
111
87
 
@@ -160,7 +136,7 @@ Use it server-side or in custom flows.
160
136
  const validator = new Validator({ stopAtFirstError: true });
161
137
 
162
138
  const field = validator.addField({
163
- rules: rules.password,
139
+ rules: rules.email,
164
140
  value: '12345',
165
141
  });
166
142
  ```
@@ -200,8 +176,8 @@ validator.addField({
200
176
 
201
177
  validator.addField({
202
178
  id: 'password',
203
- rules: rules.password,
204
- value: '1234567',
179
+ rules: rules.isTrue,
180
+ value: true,
205
181
  });
206
182
 
207
183
  const result = validator.validate();
package/dist/index.js CHANGED
@@ -1,2 +1,206 @@
1
- 'use strict';var react=require('react'),jsxRuntime=require('react/jsx-runtime');var h=/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,x={notEmpty:[{rule:r=>r!==""&&r.length>0,message:"Value is required"}],bool:[{rule:r=>!!r,message:"Value is required"}],password:[{rule:r=>r.length>0,message:"Password field cannot be empty"},{rule:r=>r.length>5,message:"Password field can not be less than 6 characters"}],email:[{rule:r=>!!r&&r!==""&&r.length!==0,message:"Email is required"},{rule:r=>h.test(String(r).toLowerCase()),message:"Email is invalid"}],min:r=>[{rule:e=>Number.parseFloat(e)>r,message:`The value must be greater than ${r}`}],max:r=>[{rule:e=>Number.parseFloat(e)<r,message:`The value must be smaller ${r}`}],length:(r,e)=>[{rule:t=>String(t).length>=r,message:`No less than ${r} symbols`},{rule:t=>e!==void 0?String(t).length<=e:true,message:`No more than ${e} symbols`}]};var V=class{rules;required;value;id;constructor({rules:e,required:t,value:s,id:i}){this.rules=e,this.required=t,this.value=s,this.id=i;}validate(){let e=true,t="",{rules:s,value:i,required:d,id:u}=this,n=!i&&Number.parseFloat(i)!==0;if(!s.length||n&&d===false)return {isValid:e,message:t,id:u};for(let a of s)e&&(e=a.rule(i),e||(typeof a.message=="function"?t=a.message(i):t=a.message));return {isValid:e,message:t,id:u}}},c=class{fields;params;constructor(e){this.params=e||null,this.fields=[];}addField(e){let t=new V(e);return this.fields.push(t),t}removeField(e){let t=this.fields.indexOf(e);t>-1&&this.fields.splice(t,1);}getField(e){return this.fields.find(t=>t.id===e)||null}validate(){let e,s=this.fields.map(i=>this.params?.stopAtFirstError&&e&&e.isValid===false?null:(e=i.validate(),e)).filter(i=>i&&i.isValid===false);if(s.length){let{isValid:i,message:d}=s[0];return {isValid:i,message:d,errors:s}}return {isValid:true,message:""}}};function b(r,e){let t=new c;t.addField({value:r,rules:e});let{isValid:s,...i}=t.validate();return [s,i]}var v=react.createContext(null);var w=react.forwardRef(function(e,t){let{children:s,value:i}=e,{customErrors:d,registerField:u,unregisterField:n}=react.useContext(v),a=react.useRef(e);a.current=e;let g=react.useRef(d);g.current=d;let p=react.useRef(null);p.current||(p.current={get props(){return a.current},validate:()=>{let m=a.current,y=g.current.find(o=>o.id===m.id);return y||new V({rules:m.rules,required:m.required,value:m.value,id:m.id}).validate()}}),react.useEffect(()=>(u(p.current),()=>{n(p.current);}),[u,n]);let F=p.current.validate();return typeof s=="function"?s(F,i):s});var S=react.forwardRef(function({children:e,stopAtFirstError:t},s){let i=react.useRef([]),[d,u]=react.useState([]),n=react.useCallback(l=>{l&&!i.current.includes(l)&&i.current.push(l);},[]),a=react.useCallback(l=>{let o=i.current.indexOf(l);o>-1&&i.current.splice(o,1);},[]),g=react.useCallback(l=>i.current.find(o=>o?.props?.id===l)||null,[]),p=react.useCallback(l=>{u(o=>[...o,l]);},[]),F=react.useCallback(()=>{u([]);},[]),m=react.useCallback(()=>{let l=new c({stopAtFirstError:t});for(let o of i.current)l.addField(o.props);return l.validate()},[t]);react.useImperativeHandle(s,()=>({validate:m,getField:g,registerField:n,unregisterField:a,setCustomError:p,clearCustomErrors:F}),[m,g,n,a,p,F]);let y=react.useMemo(()=>({customErrors:d,registerField:n,unregisterField:a}),[d,n,a]);return jsxRuntime.jsx(v.Provider,{value:y,children:e})});exports.Validator=c;exports.ValidatorField=w;exports.ValidatorWrapper=S;exports.rules=x;exports.useValidator=b;//# sourceMappingURL=index.js.map
2
- //# sourceMappingURL=index.js.map
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __export = (target, all) => {
6
+ for (var name in all)
7
+ __defProp(target, name, { get: all[name], enumerable: true });
8
+ };
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+
19
+ // src/index.ts
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ Validator: () => Validator,
23
+ ValidatorField: () => ValidatorField,
24
+ ValidatorWrapper: () => ValidatorWrapper,
25
+ rules: () => rules,
26
+ useValidator: () => useValidator
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/rules.ts
31
+ var import_zod = require("zod");
32
+ var rules = {
33
+ notEmpty: [import_zod.z.string().min(1, { error: "Field is required" })],
34
+ isTrue: [import_zod.z.boolean({ error: "Value is required" }).and(import_zod.z.literal(true))],
35
+ email: [import_zod.z.string().min(1, { error: "Email is required" }), import_zod.z.email({ message: "Email is invalid" })]
36
+ };
37
+
38
+ // src/validator.ts
39
+ var Field = class {
40
+ rules;
41
+ required;
42
+ value;
43
+ id;
44
+ constructor({ rules: rules2, required, value, id }) {
45
+ this.rules = rules2;
46
+ this.required = required;
47
+ this.value = value;
48
+ this.id = id;
49
+ }
50
+ validate() {
51
+ let isValid = true;
52
+ let message = "";
53
+ const { value, required, id } = this;
54
+ let result = { success: true, data: value };
55
+ const isEmptyValue = !value && Number.parseFloat(value) !== 0;
56
+ const rules2 = Array.isArray(this.rules) ? this.rules : [this.rules];
57
+ if (!rules2.length || isEmptyValue && required === false) {
58
+ return {
59
+ isValid,
60
+ message,
61
+ id,
62
+ result: { success: true, data: value }
63
+ };
64
+ }
65
+ for (const ruleItem of rules2) {
66
+ if (isValid && ruleItem && "safeParse" in ruleItem) {
67
+ result = ruleItem.safeParse(value);
68
+ isValid = result.success;
69
+ if (!isValid && result.error) {
70
+ message = result.error.issues[0]?.message || "Validation error";
71
+ }
72
+ }
73
+ }
74
+ return { isValid, message, id, result };
75
+ }
76
+ };
77
+ var Validator = class {
78
+ fields;
79
+ params;
80
+ constructor(params) {
81
+ this.params = params || null;
82
+ this.fields = [];
83
+ }
84
+ addField(params) {
85
+ const field = new Field(params);
86
+ this.fields.push(field);
87
+ return field;
88
+ }
89
+ removeField(field) {
90
+ const index = this.fields.indexOf(field);
91
+ if (index > -1) this.fields.splice(index, 1);
92
+ }
93
+ getField(id) {
94
+ return this.fields.find((field) => field.id === id) || null;
95
+ }
96
+ validate() {
97
+ let prevResult;
98
+ const statuses = this.fields.map((field) => {
99
+ if (this.params?.stopAtFirstError && prevResult && prevResult.isValid === false) {
100
+ return null;
101
+ }
102
+ prevResult = field.validate();
103
+ return prevResult;
104
+ });
105
+ const results = statuses.filter((inst) => inst && inst.isValid === false);
106
+ if (results.length) {
107
+ return results[0];
108
+ }
109
+ return { isValid: true, message: "", result: results[0]?.result };
110
+ }
111
+ };
112
+
113
+ // src/use-validator.ts
114
+ function useValidator(value, rules2) {
115
+ const validator = new Validator();
116
+ validator.addField({ value, rules: rules2 });
117
+ const { isValid, ...validateObject } = validator.validate();
118
+ return [isValid, validateObject];
119
+ }
120
+
121
+ // src/validator-field.tsx
122
+ var import_react2 = require("react");
123
+
124
+ // src/context.ts
125
+ var import_react = require("react");
126
+ var Context = (0, import_react.createContext)(null);
127
+
128
+ // src/validator-field.tsx
129
+ var ValidatorField = (0, import_react2.forwardRef)(function ValidatorField2(props, _ref) {
130
+ const { children, value } = props;
131
+ const { registerField, unregisterField } = (0, import_react2.useContext)(Context);
132
+ const propsRef = (0, import_react2.useRef)(props);
133
+ propsRef.current = props;
134
+ const handleRef = (0, import_react2.useRef)(null);
135
+ if (!handleRef.current) {
136
+ handleRef.current = {
137
+ get props() {
138
+ return propsRef.current;
139
+ },
140
+ validate: () => {
141
+ const curr = propsRef.current;
142
+ const field = new Field({
143
+ rules: curr.rules,
144
+ required: curr.required,
145
+ value: curr.value,
146
+ id: curr.id
147
+ });
148
+ return field.validate();
149
+ }
150
+ };
151
+ }
152
+ (0, import_react2.useEffect)(() => {
153
+ registerField(handleRef.current);
154
+ return () => {
155
+ unregisterField(handleRef.current);
156
+ };
157
+ }, [registerField, unregisterField]);
158
+ const validity = handleRef.current.validate();
159
+ return typeof children === "function" ? children(validity, value) : children;
160
+ });
161
+
162
+ // src/validator-wrapper.tsx
163
+ var import_react3 = require("react");
164
+ var import_jsx_runtime = require("react/jsx-runtime");
165
+ var ValidatorWrapper = (0, import_react3.forwardRef)(function ValidatorWrapper2({ children, stopAtFirstError }, ref) {
166
+ const fieldsRef = (0, import_react3.useRef)([]);
167
+ const registerField = (0, import_react3.useCallback)((field) => {
168
+ if (field && !fieldsRef.current.includes(field)) {
169
+ fieldsRef.current.push(field);
170
+ }
171
+ }, []);
172
+ const unregisterField = (0, import_react3.useCallback)((field) => {
173
+ const index = fieldsRef.current.indexOf(field);
174
+ if (index > -1) fieldsRef.current.splice(index, 1);
175
+ }, []);
176
+ const getField = (0, import_react3.useCallback)((id) => {
177
+ return fieldsRef.current.find((field) => field?.props?.id === id) || null;
178
+ }, []);
179
+ const validate = (0, import_react3.useCallback)(() => {
180
+ const validator = new Validator({ stopAtFirstError });
181
+ for (const comp of fieldsRef.current) {
182
+ validator.addField(comp.props);
183
+ }
184
+ return validator.validate();
185
+ }, [stopAtFirstError]);
186
+ (0, import_react3.useImperativeHandle)(
187
+ ref,
188
+ () => ({
189
+ validate,
190
+ getField,
191
+ registerField,
192
+ unregisterField
193
+ }),
194
+ [validate, getField, registerField, unregisterField]
195
+ );
196
+ const contextValue = (0, import_react3.useMemo)(() => ({ registerField, unregisterField }), [registerField, unregisterField]);
197
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Context.Provider, { value: contextValue, children });
198
+ });
199
+ // Annotate the CommonJS export names for ESM import in node:
200
+ 0 && (module.exports = {
201
+ Validator,
202
+ ValidatorField,
203
+ ValidatorWrapper,
204
+ rules,
205
+ useValidator
206
+ });
@@ -1,6 +1,7 @@
1
+ // biome-ignore lint/correctness/noUnusedImports: <need>
1
2
  import React, { createRef, useState } from 'react'
2
3
  import ReactDOM from 'react-dom/client'
3
- import { rules, Validator, ValidatorField, ValidatorWrapper } from '../dist/index'
4
+ import { rules, ValidatorField, ValidatorWrapper } from '../dist/index'
4
5
 
5
6
  function App() {
6
7
  const [email, setEmail] = useState('')
@@ -15,32 +16,6 @@ function App() {
15
16
  console.log('success')
16
17
  }
17
18
 
18
- function handleValidateCustomFields() {
19
- const validator = new Validator({ stopAtFirstError: true })
20
- const fieldPassword = validator.addField({
21
- rules: rules.password,
22
- value: '',
23
- id: 'for-remove',
24
- })
25
-
26
- const fieldSearchPassword = validator.getField('for-remove')
27
- console.log(fieldPassword === fieldSearchPassword)
28
-
29
- validator.removeField(fieldPassword)
30
-
31
- validator.addField({
32
- rules: rules.password,
33
- value: 'testpassword',
34
- })
35
-
36
- const { isValid, message, errors } = validator.validate()
37
- if (!isValid) {
38
- console.log(isValid, message, errors)
39
- return
40
- }
41
- console.log('success')
42
- }
43
-
44
19
  return (
45
20
  <ValidatorWrapper ref={jsxValidator}>
46
21
  <ValidatorField rules={rules.email} value={email}>
@@ -55,9 +30,6 @@ function App() {
55
30
  </>
56
31
  )}
57
32
  </ValidatorField>
58
- <button onClick={handleValidateCustomFields} type="button">
59
- Validate custom fields
60
- </button>
61
33
  </ValidatorWrapper>
62
34
  )
63
35
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coxy/react-validator",
3
- "version": "4.0.0",
4
- "description": "🚀 Simple validation form for React or NodeJS apps. useValidator are included ;)",
3
+ "version": "5.0.1",
4
+ "description": "🚀 Simple validation form for React or NodeJS apps with zod. useValidator are included ;)",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
@@ -22,8 +22,8 @@
22
22
  "author": "dsshard",
23
23
  "scripts": {
24
24
  "start": "tsup ./src/index.ts --watch",
25
- "build": "tsup",
26
- "lint": "biome lint --fix",
25
+ "build": "tsup ./src/index.ts",
26
+ "lint": "npx biome lint --fix",
27
27
  "example": "NODE_ENV=production npx esbuild example/example.tsx --bundle --outfile=example/dist/example.js --minify --format=esm --target=es2020",
28
28
  "coverage": "npx codecov",
29
29
  "coveralls": "npx coveralls .",
@@ -33,11 +33,10 @@
33
33
  "peerDependencies": {
34
34
  "react": ">=18.x.x"
35
35
  },
36
- "dependencies": {},
36
+ "dependencies": {
37
+ "zod": "^3.25.0 || ^4.0.0"
38
+ },
37
39
  "devDependencies": {
38
- "react": ">=18.x.x",
39
- "react-dom": ">=18.x.x",
40
- "@biomejs/biome": "2.2.2",
41
40
  "@testing-library/dom": "10.4.1",
42
41
  "@testing-library/react": "16.3.0",
43
42
  "@types/jest": "30.0.0",
@@ -46,10 +45,12 @@
46
45
  "@types/react-dom": "19.1.7",
47
46
  "jest": "30.0.5",
48
47
  "jest-environment-jsdom": "30.0.5",
49
- "tsup": "8.5.0",
48
+ "react": ">=18.x.x",
49
+ "react-dom": ">=18.x.x",
50
50
  "react-test-renderer": "19.1.1",
51
51
  "ts-jest": "29.4.0",
52
52
  "ts-node": "10.9.2",
53
+ "tsup": "8.5.0",
53
54
  "typescript": "5.8.3"
54
55
  }
55
56
  }
package/src/context.ts CHANGED
@@ -10,5 +10,4 @@ export interface RegisteredFieldHandle {
10
10
  export const Context = createContext<{
11
11
  registerField: (field: RegisteredFieldHandle) => void
12
12
  unregisterField: (field: RegisteredFieldHandle) => void
13
- customErrors: Array<Validity>
14
13
  }>(null)
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export { rules } from './rules'
2
2
 
3
- export type { ErrorMessage, FieldParams, ValidatorRule, Validity } from './types'
3
+ export type { ErrorMessage, FieldParams, Validity } from './types'
4
4
  export { useValidator } from './use-validator'
5
5
  export { Validator } from './validator'
6
6
  export { ValidatorField } from './validator-field'
package/src/rules.test.ts CHANGED
@@ -1,132 +1,37 @@
1
+ import type { ZodSafeParseResult } from 'zod'
1
2
  import { rules } from './rules'
2
3
 
3
4
  it('check rule email', () => {
4
5
  expect(rules.email.length).toBe(2)
5
6
 
6
- let result: boolean
7
+ let result: ZodSafeParseResult<string>
7
8
  // email.length > 0
8
- result = rules.email[0].rule('test')
9
- expect(result).toBe(true)
9
+ result = rules.email[0].safeParse('test')
10
+ expect(result.success).toBe(true)
10
11
 
11
12
  // email check regexp
12
- result = rules.email[1].rule('test')
13
- expect(result).toBe(false)
13
+ result = rules.email[1].safeParse('test')
14
+ expect(result.success).toBe(false)
14
15
 
15
16
  // email check regexp
16
- result = rules.email[1].rule('test@gmail.com')
17
- expect(result).toBe(true)
18
- })
19
-
20
- it('check rule password', () => {
21
- expect(rules.password.length).toBe(2)
22
-
23
- let result: boolean
24
- // password.length > 0
25
- result = rules.password[0].rule('test')
26
- expect(result).toBe(true)
27
-
28
- // password.length > 5
29
- result = rules.password[1].rule('test')
30
- expect(result).toBe(false)
17
+ result = rules.email[1].safeParse('test@gmail.com')
18
+ expect(result.success).toBe(true)
31
19
  })
32
20
 
33
21
  it('check rule bool', () => {
34
- expect(rules.bool.length).toBe(1)
22
+ expect(rules.isTrue.length).toBe(1)
35
23
 
36
- // @ts-expect-error
37
- const result = rules.bool[0].rule(true)
38
- expect(result).toBe(true)
24
+ const result = rules.isTrue[0].safeParse(true)
25
+ expect(result.success).toBe(true)
39
26
  })
40
27
 
41
28
  it('check rule notEmpty', () => {
42
29
  expect(rules.notEmpty.length).toBe(1)
43
30
 
44
- let result: boolean
45
- result = rules.notEmpty[0].rule('')
46
- expect(result).toBe(false)
47
-
48
- result = rules.notEmpty[0].rule('test')
49
- expect(result).toBe(true)
50
- })
51
-
52
- it('check rule min', () => {
53
- expect(rules.min(1).length).toBe(1)
54
- expect(typeof rules.min).toBe('function')
55
-
56
- let result: boolean
57
- result = rules.min(10)[0].rule('')
58
- expect(result).toBe(false)
59
-
60
- result = rules.min(9)[0].rule('test-test-test')
61
- expect(result).toBe(false)
62
-
63
- result = rules.min(9)[0].rule('11')
64
- expect(result).toBe(true)
65
-
66
- // @ts-expect-error
67
- result = rules.min(9)[0].rule(10)
68
- expect(result).toBe(true)
69
-
70
- result = rules.min(9)[0].rule('8')
71
- expect(result).toBe(false)
72
-
73
- // @ts-expect-error
74
- result = rules.min(9)[0].rule(7)
75
- expect(result).toBe(false)
76
-
77
- result = rules.min(-1)[0].rule('')
78
- expect(result).toBe(false)
79
- })
80
-
81
- it('check rule max', () => {
82
- let result: boolean
83
- result = rules.max(10)[0].rule('')
84
- expect(result).toBe(false)
85
-
86
- result = rules.max(9)[0].rule('test-test-test')
87
- expect(result).toBe(false)
88
-
89
- result = rules.max(9)[0].rule('11')
90
- expect(result).toBe(false)
91
-
92
- // @ts-expect-error
93
- result = rules.max(9)[0].rule(10)
94
- expect(result).toBe(false)
95
-
96
- result = rules.max(9)[0].rule('5')
97
- expect(result).toBe(true)
98
-
99
- // @ts-expect-error
100
- result = rules.max(9)[0].rule(5)
101
- expect(result).toBe(true)
102
-
103
- result = rules.max(-1)[0].rule('')
104
- expect(result).toBe(false)
105
- })
106
-
107
- it('check rule length', () => {
108
- let result: boolean
109
- result = rules.length(1)[0].rule('')
110
- expect(result).toBe(false)
111
-
112
- result = rules.length(1)[0].rule('1')
113
- expect(result).toBe(true)
114
-
115
- result = rules.length(1, 10)[0].rule('test-test-test')
116
- expect(result).toBe(true)
117
-
118
- result = rules.length(1, 10)[1].rule('test-test-test')
119
- expect(result).toBe(false)
120
-
121
- result = rules.length(1, 10)[0].rule('lol')
122
- expect(result).toBe(true)
123
-
124
- result = rules.length(1, 10)[1].rule('lol')
125
- expect(result).toBe(true)
126
-
127
- result = rules.length(1)[1].rule('test undefined 2 param')
128
- expect(result).toBe(true)
31
+ let result: ZodSafeParseResult<string>
32
+ result = rules.notEmpty[0].safeParse('')
33
+ expect(result.success).toBe(false)
129
34
 
130
- result = rules.length(10)[0].rule('tes')
131
- expect(result).toBe(false)
35
+ result = rules.notEmpty[0].safeParse('test')
36
+ expect(result.success).toBe(true)
132
37
  })
package/src/rules.ts CHANGED
@@ -1,70 +1,9 @@
1
- import type { ValidatorRule } from './types'
1
+ import { z } from 'zod'
2
2
 
3
- // eslint-disable-next-line
4
- const emailReg =
5
- /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
6
-
7
- export type ValidatorRules = ValidatorRule[]
3
+ export type ValidatorRules = z.ZodType[] | z.ZodType
8
4
 
9
5
  export const rules = {
10
- notEmpty: [
11
- {
12
- rule: (value: string) => value !== '' && value.length > 0,
13
- message: 'Value is required',
14
- },
15
- ],
16
-
17
- bool: [
18
- {
19
- rule: (value: string) => !!value,
20
- message: 'Value is required',
21
- },
22
- ],
23
-
24
- password: [
25
- {
26
- rule: (value: string) => value.length > 0,
27
- message: 'Password field cannot be empty',
28
- },
29
- {
30
- rule: (value: string) => value.length > 5,
31
- message: 'Password field can not be less than 6 characters',
32
- },
33
- ],
34
-
35
- email: [
36
- {
37
- rule: (value: string) => !!value && value !== '' && value.length !== 0,
38
- message: 'Email is required',
39
- },
40
- {
41
- rule: (value: string) => emailReg.test(String(value).toLowerCase()),
42
- message: 'Email is invalid',
43
- },
44
- ],
45
-
46
- min: (min: number) => [
47
- {
48
- rule: (value: string) => Number.parseFloat(value) > min,
49
- message: `The value must be greater than ${min}`,
50
- },
51
- ],
52
-
53
- max: (max: number) => [
54
- {
55
- rule: (value: string) => Number.parseFloat(value) < max,
56
- message: `The value must be smaller ${max}`,
57
- },
58
- ],
59
-
60
- length: (min: number, max?: number) => [
61
- {
62
- rule: (value: string) => String(value).length >= min,
63
- message: `No less than ${min} symbols`,
64
- },
65
- {
66
- rule: (value: string) => (max !== undefined ? String(value).length <= max : true),
67
- message: `No more than ${max} symbols`,
68
- },
69
- ],
6
+ notEmpty: [z.string().min(1, { error: 'Field is required' })],
7
+ isTrue: [z.boolean({ error: 'Value is required' }).and(z.literal(true))],
8
+ email: [z.string().min(1, { error: 'Email is required' }), z.email({ message: 'Email is invalid' })],
70
9
  }
package/src/types.ts CHANGED
@@ -1,13 +1,7 @@
1
+ import type { ZodSafeParseResult } from 'zod/index'
1
2
  import type { ValidatorRules } from './rules'
2
3
  import type { Value } from './validator-field'
3
4
 
4
- type Fn = (value: Value) => string
5
-
6
- export interface ValidatorRule {
7
- rule: (value: Value) => boolean
8
- message: string | Fn
9
- }
10
-
11
5
  export interface ErrorMessage {
12
6
  message: string
13
7
  isValid: boolean
@@ -16,7 +10,7 @@ export interface ErrorMessage {
16
10
  export interface Validity {
17
11
  message: string
18
12
  isValid: boolean
19
- errors?: ErrorMessage[]
13
+ result: ZodSafeParseResult<unknown>
20
14
  id?: string | number
21
15
  }
22
16
 
@@ -24,14 +24,14 @@ jest.useFakeTimers()
24
24
  it('check state change and hide field', () => {
25
25
  function Comp() {
26
26
  const [value, setValue] = useState(false)
27
- const [isValid, validateObject] = useValidator(value, rules.bool)
27
+ const [isValid, validateObject] = useValidator(value, rules.isTrue)
28
28
 
29
29
  useEffect(() => {
30
30
  setTimeout(() => {
31
31
  act(() => {
32
32
  setValue(true)
33
33
  })
34
- }, 100)
34
+ }, 200)
35
35
  }, [])
36
36
 
37
37
  return (
@@ -46,7 +46,7 @@ it('check state change and hide field', () => {
46
46
  })
47
47
 
48
48
  expect(screen.getByTestId('test1').textContent).toContain('false')
49
- expect(screen.getByTestId('test2').textContent).toContain('Value is required')
49
+ expect(screen.getByTestId('test2').textContent).toContain('Invalid input: expected true')
50
50
 
51
51
  jest.runAllTimers()
52
52