@coxy/react-validator 2.0.6 → 3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules.ts","../src/context.ts","../src/validator.ts","../src/validator-field.tsx","../src/validator-wrapper.tsx","../src/use-validator.ts"],"names":["emailReg","rules","value","min","max","Context","createContext","Field","required","id","isValid","message","isEmptyValue","instance","Validator","params","field","index","prevResult","errors","inst","ValidationFieldWrapper","Component","props","customError","item","children","validity","ValidatorField","jsx","data","ValidatorWrapper","validator","comp","useValidator","validateObject"],"mappings":"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","file":"index.js","sourcesContent":["import type { ValidatorRule } from './types'\n\n// eslint-disable-next-line\nconst emailReg =\n /^(([^<>()\\[\\]\\\\.,;:\\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,}))$/\n\nexport type ValidatorRules = ValidatorRule[]\n\nexport const rules = {\n notEmpty: [\n {\n rule: (value) => value !== '' && value.length > 0,\n message: 'Value is required',\n },\n ],\n\n bool: [\n {\n rule: (value) => !!value,\n message: 'Value is required',\n },\n ],\n\n password: [\n {\n rule: (value) => value.length > 0,\n message: 'Password field cannot be empty',\n },\n {\n rule: (value) => value.length > 5,\n message: 'Password field can not be less than 6 characters',\n },\n ],\n\n email: [\n {\n rule: (value) => !!value && value !== '' && value.length !== 0,\n message: 'Email is required',\n },\n {\n rule: (value) => emailReg.test(String(value).toLowerCase()),\n message: 'Email is invalid',\n },\n ],\n\n min: (min) => [\n {\n rule: (value) => Number.parseFloat(value) > min,\n message: `The value must be greater than ${min}`,\n },\n ],\n\n max: (max) => [\n {\n rule: (value) => Number.parseFloat(value) < max,\n message: `The value must be smaller ${max}`,\n },\n ],\n\n length: (min, max?) => [\n {\n rule: (value) => String(value).length >= min,\n message: `No less than ${min} symbols`,\n },\n {\n rule: (value) => (max !== undefined ? String(value).length <= max : true),\n message: `No more than ${max} symbols`,\n },\n ],\n}\n","import { createContext } from 'react'\n\nimport type { Validity } from './types'\n\nexport const Context = createContext<{\n registerField: (field: string | number) => void\n unregisterField: (field: string | number) => void\n customErrors: Array<Validity>\n}>(null)\n","import type { ValidatorRules } from './rules'\nimport type { FieldParams, Validity } from './types'\nimport type { Value } from './validator-field'\n\nexport class Field {\n private rules: ValidatorRules\n private required: boolean\n private value: Value\n public id: string | number\n\n constructor({ rules, required, value, id }: FieldParams) {\n this.rules = rules\n this.required = required\n this.value = value\n this.id = id\n }\n\n validate(): Validity {\n let isValid = true\n let message = ''\n const { rules, value, required, id } = this\n\n const isEmptyValue = !value && Number.parseFloat(value) !== 0\n\n if (!rules.length || (isEmptyValue && required === false)) {\n return { isValid, message, id }\n }\n for (const instance of rules) {\n if (isValid) {\n isValid = instance.rule(value)\n if (!isValid) {\n if (typeof instance.message === 'function') {\n message = instance.message(value)\n } else {\n message = instance.message\n }\n }\n }\n }\n return { isValid, message, id }\n }\n}\n\nexport interface ValidatorParams {\n stopAtFirstError: boolean\n}\n\nexport class Validator {\n private fields: Field[]\n private params: ValidatorParams\n\n constructor(params?: ValidatorParams) {\n this.params = params || null\n this.fields = []\n }\n\n addField(params: FieldParams): Field {\n const field = new Field(params)\n this.fields.push(field)\n return field\n }\n\n removeField(field: Field): void {\n const index = this.fields.indexOf(field)\n if (index > -1) this.fields.splice(index, 1)\n }\n\n getField(id: Field['id']): Field {\n return this.fields.find((field) => field.id === id) || null\n }\n\n validate(): Validity {\n let prevResult: Validity | null\n const statuses = this.fields.map((field) => {\n if (this.params?.stopAtFirstError && prevResult && prevResult.isValid === false) {\n return null\n }\n prevResult = field.validate()\n return prevResult\n })\n\n const errors = statuses.filter((inst) => inst && inst.isValid === false)\n\n if (errors.length) {\n const { isValid, message } = errors[0]\n return { isValid, message, errors }\n }\n return { isValid: true, message: '' }\n }\n}\n","import { Component, type ReactNode } from 'react'\nimport type { Validity } from 'types'\n\nimport { Context } from './context'\nimport type { ValidatorRules } from './rules'\nimport { Field } from './validator'\n\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\nexport type Value = any\n\ntype Fn = (validity: Validity, value: Value) => ReactNode\n\ninterface Props {\n rules?: ValidatorRules\n required?: boolean\n value?: Value\n id?: string | number\n children?: ReactNode | Fn\n unregisterField: (val: Value) => void\n registerField: (val: Value) => void\n customErrors: Array<Validity>\n}\n\nclass ValidationFieldWrapper extends Component<Props> {\n componentWillUnmount() {\n this.props.unregisterField(this)\n }\n\n componentDidMount() {\n this.props.registerField(this)\n }\n\n validate(): Validity {\n const props = this.props\n const customError = props.customErrors.find((item) => item.id === props.id)\n if (customError) {\n return customError\n }\n\n const field = new Field({\n rules: props.rules,\n required: props.required,\n value: props.value,\n id: props.id,\n })\n return field.validate()\n }\n\n render() {\n const { children, value } = this.props\n const validity = this.validate()\n return typeof children === 'function' ? children(validity, value) : children\n }\n}\n\nexport function ValidatorField(props: Omit<Props, 'registerField' | 'unregisterField' | 'customErrors'>) {\n return (\n <Context.Consumer>\n {(data) => (\n <ValidationFieldWrapper\n {...props}\n customErrors={data.customErrors}\n registerField={data.registerField}\n unregisterField={data.unregisterField}\n />\n )}\n </Context.Consumer>\n )\n}\n","import { Component, type ReactNode, type RefObject } from 'react'\n\nimport { Context } from './context'\nimport type { Validity } from './types'\nimport { type Field, Validator } from './validator'\n\ninterface ComponentProps {\n children?: ReactNode\n stopAtFirstError?: boolean\n ref?: RefObject<ValidatorWrapper>\n}\n\nexport class ValidatorWrapper extends Component<ComponentProps> {\n fields = []\n state = {\n customErrors: [],\n }\n\n constructor(props) {\n super(props)\n this.registerField = this.registerField.bind(this)\n this.unregisterField = this.unregisterField.bind(this)\n }\n\n componentWillUnmount() {\n this.fields = []\n }\n\n registerField(field) {\n if (field && !this.fields.includes(field)) {\n this.fields.push(field)\n }\n }\n\n unregisterField(field) {\n const index = this.fields.indexOf(field)\n if (index > -1) this.fields.splice(index, 1)\n }\n\n getField(id): Field | null {\n return this.fields.find((field) => field.props.id === id) || null\n }\n\n setCustomError(customError: Validity) {\n this.setState({\n customErrors: [...this.state.customErrors, customError],\n })\n }\n\n clearCustomErrors() {\n this.setState({ customErrors: [] })\n }\n\n validate(): Validity {\n const validator = new Validator({ stopAtFirstError: this.props.stopAtFirstError })\n for (const comp of this.fields) {\n validator.addField(comp.props)\n }\n return validator.validate()\n }\n\n render(): ReactNode {\n return (\n <Context.Provider\n value={{\n customErrors: this.state.customErrors,\n registerField: this.registerField,\n unregisterField: this.unregisterField,\n }}\n >\n {this.props.children}\n </Context.Provider>\n )\n }\n}\n","import type { ValidatorRules } from './rules'\nimport type { Validity } from './types'\nimport { Validator } from './validator'\nimport type { Value } from './validator-field'\n\nexport function useValidator(value: Value, rules: ValidatorRules): [boolean, Pick<Validity, 'message' | 'errors'>] {\n const validator = new Validator()\n validator.addField({ value, rules })\n const { isValid, ...validateObject } = validator.validate()\n return [isValid, validateObject]\n}\n"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import {createContext,Component}from'react';import {jsx}from'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,}))$/,g={notEmpty:[{rule:t=>t!==""&&t.length>0,message:"Value is required"}],bool:[{rule:t=>!!t,message:"Value is required"}],password:[{rule:t=>t.length>0,message:"Password field cannot be empty"},{rule:t=>t.length>5,message:"Password field can not be less than 6 characters"}],email:[{rule:t=>!!t&&t!==""&&t.length!==0,message:"Email is required"},{rule:t=>h.test(String(t).toLowerCase()),message:"Email is invalid"}],min:t=>[{rule:e=>Number.parseFloat(e)>t,message:`The value must be greater than ${t}`}],max:t=>[{rule:e=>Number.parseFloat(e)<t,message:`The value must be smaller ${t}`}],length:(t,e)=>[{rule:r=>String(r).length>=t,message:`No less than ${t} symbols`},{rule:r=>e!==void 0?String(r).length<=e:true,message:`No more than ${e} symbols`}]};var d=createContext(null);var l=class{rules;required;value;id;constructor({rules:e,required:r,value:s,id:i}){this.rules=e,this.required=r,this.value=s,this.id=i;}validate(){let e=true,r="",{rules:s,value:i,required:n,id:m}=this,c=!i&&Number.parseFloat(i)!==0;if(!s.length||c&&n===false)return {isValid:e,message:r,id:m};for(let a of s)e&&(e=a.rule(i),e||(typeof a.message=="function"?r=a.message(i):r=a.message));return {isValid:e,message:r,id:m}}},o=class{fields;params;constructor(e){this.params=e||null,this.fields=[];}addField(e){let r=new l(e);return this.fields.push(r),r}removeField(e){let r=this.fields.indexOf(e);r>-1&&this.fields.splice(r,1);}getField(e){return this.fields.find(r=>r.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:n}=s[0];return {isValid:i,message:n,errors:s}}return {isValid:true,message:""}}};var u=class extends Component{componentWillUnmount(){this.props.unregisterField(this);}componentDidMount(){this.props.registerField(this);}validate(){let e=this.props,r=e.customErrors.find(i=>i.id===e.id);return r||new l({rules:e.rules,required:e.required,value:e.value,id:e.id}).validate()}render(){let{children:e,value:r}=this.props,s=this.validate();return typeof e=="function"?e(s,r):e}};function F(t){return jsx(d.Consumer,{children:e=>jsx(u,{...t,customErrors:e.customErrors,registerField:e.registerField,unregisterField:e.unregisterField})})}var p=class extends Component{fields=[];state={customErrors:[]};constructor(e){super(e),this.registerField=this.registerField.bind(this),this.unregisterField=this.unregisterField.bind(this);}componentWillUnmount(){this.fields=[];}registerField(e){e&&!this.fields.includes(e)&&this.fields.push(e);}unregisterField(e){let r=this.fields.indexOf(e);r>-1&&this.fields.splice(r,1);}getField(e){return this.fields.find(r=>r.props.id===e)||null}setCustomError(e){this.setState({customErrors:[...this.state.customErrors,e]});}clearCustomErrors(){this.setState({customErrors:[]});}validate(){let e=new o({stopAtFirstError:this.props.stopAtFirstError});for(let r of this.fields)e.addField(r.props);return e.validate()}render(){return jsx(d.Provider,{value:{customErrors:this.state.customErrors,registerField:this.registerField,unregisterField:this.unregisterField},children:this.props.children})}};function x(t,e){let r=new o;r.addField({value:t,rules:e});let{isValid:s,...i}=r.validate();return [s,i]}export{o as Validator,F as ValidatorField,p as ValidatorWrapper,g as rules,x as useValidator};//# sourceMappingURL=index.mjs.map
2
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules.ts","../src/context.ts","../src/validator.ts","../src/validator-field.tsx","../src/validator-wrapper.tsx","../src/use-validator.ts"],"names":["emailReg","rules","value","min","max","Context","createContext","Field","required","id","isValid","message","isEmptyValue","instance","Validator","params","field","index","prevResult","errors","inst","ValidationFieldWrapper","Component","props","customError","item","children","validity","ValidatorField","jsx","data","ValidatorWrapper","validator","comp","useValidator","validateObject"],"mappings":"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","file":"index.mjs","sourcesContent":["import type { ValidatorRule } from './types'\n\n// eslint-disable-next-line\nconst emailReg =\n /^(([^<>()\\[\\]\\\\.,;:\\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,}))$/\n\nexport type ValidatorRules = ValidatorRule[]\n\nexport const rules = {\n notEmpty: [\n {\n rule: (value) => value !== '' && value.length > 0,\n message: 'Value is required',\n },\n ],\n\n bool: [\n {\n rule: (value) => !!value,\n message: 'Value is required',\n },\n ],\n\n password: [\n {\n rule: (value) => value.length > 0,\n message: 'Password field cannot be empty',\n },\n {\n rule: (value) => value.length > 5,\n message: 'Password field can not be less than 6 characters',\n },\n ],\n\n email: [\n {\n rule: (value) => !!value && value !== '' && value.length !== 0,\n message: 'Email is required',\n },\n {\n rule: (value) => emailReg.test(String(value).toLowerCase()),\n message: 'Email is invalid',\n },\n ],\n\n min: (min) => [\n {\n rule: (value) => Number.parseFloat(value) > min,\n message: `The value must be greater than ${min}`,\n },\n ],\n\n max: (max) => [\n {\n rule: (value) => Number.parseFloat(value) < max,\n message: `The value must be smaller ${max}`,\n },\n ],\n\n length: (min, max?) => [\n {\n rule: (value) => String(value).length >= min,\n message: `No less than ${min} symbols`,\n },\n {\n rule: (value) => (max !== undefined ? String(value).length <= max : true),\n message: `No more than ${max} symbols`,\n },\n ],\n}\n","import { createContext } from 'react'\n\nimport type { Validity } from './types'\n\nexport const Context = createContext<{\n registerField: (field: string | number) => void\n unregisterField: (field: string | number) => void\n customErrors: Array<Validity>\n}>(null)\n","import type { ValidatorRules } from './rules'\nimport type { FieldParams, Validity } from './types'\nimport type { Value } from './validator-field'\n\nexport class Field {\n private rules: ValidatorRules\n private required: boolean\n private value: Value\n public id: string | number\n\n constructor({ rules, required, value, id }: FieldParams) {\n this.rules = rules\n this.required = required\n this.value = value\n this.id = id\n }\n\n validate(): Validity {\n let isValid = true\n let message = ''\n const { rules, value, required, id } = this\n\n const isEmptyValue = !value && Number.parseFloat(value) !== 0\n\n if (!rules.length || (isEmptyValue && required === false)) {\n return { isValid, message, id }\n }\n for (const instance of rules) {\n if (isValid) {\n isValid = instance.rule(value)\n if (!isValid) {\n if (typeof instance.message === 'function') {\n message = instance.message(value)\n } else {\n message = instance.message\n }\n }\n }\n }\n return { isValid, message, id }\n }\n}\n\nexport interface ValidatorParams {\n stopAtFirstError: boolean\n}\n\nexport class Validator {\n private fields: Field[]\n private params: ValidatorParams\n\n constructor(params?: ValidatorParams) {\n this.params = params || null\n this.fields = []\n }\n\n addField(params: FieldParams): Field {\n const field = new Field(params)\n this.fields.push(field)\n return field\n }\n\n removeField(field: Field): void {\n const index = this.fields.indexOf(field)\n if (index > -1) this.fields.splice(index, 1)\n }\n\n getField(id: Field['id']): Field {\n return this.fields.find((field) => field.id === id) || null\n }\n\n validate(): Validity {\n let prevResult: Validity | null\n const statuses = this.fields.map((field) => {\n if (this.params?.stopAtFirstError && prevResult && prevResult.isValid === false) {\n return null\n }\n prevResult = field.validate()\n return prevResult\n })\n\n const errors = statuses.filter((inst) => inst && inst.isValid === false)\n\n if (errors.length) {\n const { isValid, message } = errors[0]\n return { isValid, message, errors }\n }\n return { isValid: true, message: '' }\n }\n}\n","import { Component, type ReactNode } from 'react'\nimport type { Validity } from 'types'\n\nimport { Context } from './context'\nimport type { ValidatorRules } from './rules'\nimport { Field } from './validator'\n\n// biome-ignore lint/suspicious/noExplicitAny: <explanation>\nexport type Value = any\n\ntype Fn = (validity: Validity, value: Value) => ReactNode\n\ninterface Props {\n rules?: ValidatorRules\n required?: boolean\n value?: Value\n id?: string | number\n children?: ReactNode | Fn\n unregisterField: (val: Value) => void\n registerField: (val: Value) => void\n customErrors: Array<Validity>\n}\n\nclass ValidationFieldWrapper extends Component<Props> {\n componentWillUnmount() {\n this.props.unregisterField(this)\n }\n\n componentDidMount() {\n this.props.registerField(this)\n }\n\n validate(): Validity {\n const props = this.props\n const customError = props.customErrors.find((item) => item.id === props.id)\n if (customError) {\n return customError\n }\n\n const field = new Field({\n rules: props.rules,\n required: props.required,\n value: props.value,\n id: props.id,\n })\n return field.validate()\n }\n\n render() {\n const { children, value } = this.props\n const validity = this.validate()\n return typeof children === 'function' ? children(validity, value) : children\n }\n}\n\nexport function ValidatorField(props: Omit<Props, 'registerField' | 'unregisterField' | 'customErrors'>) {\n return (\n <Context.Consumer>\n {(data) => (\n <ValidationFieldWrapper\n {...props}\n customErrors={data.customErrors}\n registerField={data.registerField}\n unregisterField={data.unregisterField}\n />\n )}\n </Context.Consumer>\n )\n}\n","import { Component, type ReactNode, type RefObject } from 'react'\n\nimport { Context } from './context'\nimport type { Validity } from './types'\nimport { type Field, Validator } from './validator'\n\ninterface ComponentProps {\n children?: ReactNode\n stopAtFirstError?: boolean\n ref?: RefObject<ValidatorWrapper>\n}\n\nexport class ValidatorWrapper extends Component<ComponentProps> {\n fields = []\n state = {\n customErrors: [],\n }\n\n constructor(props) {\n super(props)\n this.registerField = this.registerField.bind(this)\n this.unregisterField = this.unregisterField.bind(this)\n }\n\n componentWillUnmount() {\n this.fields = []\n }\n\n registerField(field) {\n if (field && !this.fields.includes(field)) {\n this.fields.push(field)\n }\n }\n\n unregisterField(field) {\n const index = this.fields.indexOf(field)\n if (index > -1) this.fields.splice(index, 1)\n }\n\n getField(id): Field | null {\n return this.fields.find((field) => field.props.id === id) || null\n }\n\n setCustomError(customError: Validity) {\n this.setState({\n customErrors: [...this.state.customErrors, customError],\n })\n }\n\n clearCustomErrors() {\n this.setState({ customErrors: [] })\n }\n\n validate(): Validity {\n const validator = new Validator({ stopAtFirstError: this.props.stopAtFirstError })\n for (const comp of this.fields) {\n validator.addField(comp.props)\n }\n return validator.validate()\n }\n\n render(): ReactNode {\n return (\n <Context.Provider\n value={{\n customErrors: this.state.customErrors,\n registerField: this.registerField,\n unregisterField: this.unregisterField,\n }}\n >\n {this.props.children}\n </Context.Provider>\n )\n }\n}\n","import type { ValidatorRules } from './rules'\nimport type { Validity } from './types'\nimport { Validator } from './validator'\nimport type { Value } from './validator-field'\n\nexport function useValidator(value: Value, rules: ValidatorRules): [boolean, Pick<Validity, 'message' | 'errors'>] {\n const validator = new Validator()\n validator.addField({ value, rules })\n const { isValid, ...validateObject } = validator.validate()\n return [isValid, validateObject]\n}\n"]}
@@ -1,14 +1,14 @@
1
1
  /* eslint no-console: [0] */
2
- import { useState, createRef } from 'react'
2
+ import { createRef, useState } from 'react'
3
3
  import { createRoot } from 'react-dom/client'
4
4
 
5
- import { ValidatorWrapper, rules, ValidatorField, Validator } from '../dist/index'
5
+ import { Validator, ValidatorField, ValidatorWrapper, rules } from '../dist/index'
6
6
 
7
- function App () {
7
+ function App() {
8
8
  const [email, setEmail] = useState('')
9
9
  const jsxValidator = createRef<ValidatorWrapper>()
10
10
 
11
- function handleValidateEmail () {
11
+ function handleValidateEmail() {
12
12
  const { isValid, message, errors } = jsxValidator.current.validate()
13
13
  if (!isValid) {
14
14
  console.log(isValid, message, errors)
@@ -17,12 +17,12 @@ function App () {
17
17
  console.log('success')
18
18
  }
19
19
 
20
- function handleValidateCustomFields () {
20
+ function handleValidateCustomFields() {
21
21
  const validator = new Validator({ stopAtFirstError: true })
22
22
  const fieldPassword = validator.addField({
23
23
  rules: rules.password,
24
24
  value: '',
25
- id: 'for-remove'
25
+ id: 'for-remove',
26
26
  })
27
27
 
28
28
  const fieldSearchPassword = validator.getField('for-remove')
@@ -32,7 +32,7 @@ function App () {
32
32
 
33
33
  validator.addField({
34
34
  rules: rules.password,
35
- value: 'testpassword'
35
+ value: 'testpassword',
36
36
  })
37
37
 
38
38
  const { isValid, message, errors } = validator.validate()
@@ -52,11 +52,15 @@ function App () {
52
52
  <input onChange={({ target: { value } }) => setEmail(value)} />
53
53
  <div>{isValid ? 'valid' : 'invalid'}</div>
54
54
  <div>{message || ''}</div>
55
- <button onClick={handleValidateEmail} type="button">Validate email</button>
55
+ <button onClick={handleValidateEmail} type="button">
56
+ Validate email
57
+ </button>
56
58
  </>
57
59
  )}
58
60
  </ValidatorField>
59
- <button onClick={handleValidateCustomFields} type="button">Validate custom fields</button>
61
+ <button onClick={handleValidateCustomFields} type="button">
62
+ Validate custom fields
63
+ </button>
60
64
  </ValidatorWrapper>
61
65
  </>
62
66
  )
@@ -3,13 +3,8 @@
3
3
  "compilerOptions": {
4
4
  "outDir": "./dist",
5
5
  "rootDir": ".",
6
- "baseUrl": "./",
6
+ "baseUrl": "./"
7
7
  },
8
- "include": [
9
- "example.tsx"
10
- ],
11
- "exclude": [
12
- "node_modules",
13
- "dist"
14
- ]
8
+ "include": ["example.tsx"],
9
+ "exclude": ["node_modules", "dist"]
15
10
  }
package/package.json CHANGED
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "name": "@coxy/react-validator",
3
- "version": "2.0.6",
3
+ "version": "3.0.0",
4
4
  "description": "🚀 Simple validation form for React or NodeJS apps. useValidator are included ;)",
5
5
  "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
6
8
  "repository": {
7
9
  "type": "git",
8
10
  "url": "https://github.com/dsshard/react-validator.git"
@@ -19,8 +21,8 @@
19
21
  ],
20
22
  "author": "dsshard",
21
23
  "scripts": {
22
- "start": "ts-node . --watch",
23
- "build": "NODE_ENV=production tsc --project tsconfig.build.json",
24
+ "start": "tsup ./src/index.ts --watch",
25
+ "build": "tsup",
24
26
  "example": "tsc --project ./example/tsconfig.json",
25
27
  "coverage": "codecov",
26
28
  "coveralls": "coveralls .",
@@ -28,38 +30,27 @@
28
30
  },
29
31
  "license": "MIT",
30
32
  "peerDependencies": {
31
- "react": ">=18.x.x"
33
+ "react": ">=19.x.x"
32
34
  },
33
35
  "dependencies": {
34
- "react": ">=18.x.x"
36
+ "react": ">=19.x.x"
35
37
  },
36
38
  "devDependencies": {
37
- "@coxy/eslint-config": "1.0.6",
38
- "@testing-library/react": "13.4.0",
39
- "@types/jest": "29.1.2",
40
- "@types/node": "18.8.3",
41
- "@types/react": "18.0.21",
42
- "@typescript-eslint/eslint-plugin": "5.*",
43
- "@typescript-eslint/parser": "5.*",
39
+ "@biomejs/biome": "2.1.2",
40
+ "@testing-library/dom": "10.4.1",
41
+ "@testing-library/react": "16.3.0",
42
+ "@types/jest": "30.0.0",
43
+ "@types/node": "24.1.0",
44
+ "@types/react": "19.1.8",
44
45
  "codecov": "3.8.3",
45
46
  "coveralls": "3.1.1",
46
- "eslint": "8.25.0",
47
- "eslint-config-standard": "17.0.0",
48
- "eslint-config-prettier": "8.8.0",
49
- "eslint-plugin-import": "2.26.0",
50
- "eslint-plugin-jsx-a11y": "6.6.1",
51
- "eslint-plugin-n": "15.3.0",
52
- "eslint-plugin-promise": "6.0.1",
53
- "eslint-plugin-filename-rules": "1.2.0",
54
- "eslint-plugin-react": "7.31.8",
55
- "eslint-plugin-prettier": "4.2.1",
56
- "jest": "29.1.2",
57
- "jest-environment-jsdom": "29.1.2",
58
- "prettier": "2.8.7",
59
- "react-dom": "18.2.0",
60
- "react-test-renderer": "18.2.0",
61
- "ts-jest": "29.0.3",
62
- "ts-node": "10.9.1",
63
- "typescript": "4.8.4"
47
+ "jest": "30.0.5",
48
+ "jest-environment-jsdom": "30.0.5",
49
+ "react-dom": "19.1.1",
50
+ "tsup": "8.5.0",
51
+ "react-test-renderer": "19.1.1",
52
+ "ts-jest": "29.4.0",
53
+ "ts-node": "10.9.2",
54
+ "typescript": "5.8.3"
64
55
  }
65
56
  }
package/src/context.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { createContext } from 'react'
2
2
 
3
- import { Validity } from './types'
3
+ import type { Validity } from './types'
4
4
 
5
5
  export const Context = createContext<{
6
6
  registerField: (field: string | number) => void
package/src/jest.d.ts CHANGED
@@ -1 +1,2 @@
1
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
1
2
  declare let shallow: any
package/src/rules.test.ts CHANGED
@@ -3,7 +3,7 @@ import { rules } from './rules'
3
3
  it('check rule email', () => {
4
4
  expect(rules.email.length).toBe(2)
5
5
 
6
- let result
6
+ let result: boolean
7
7
  // email.length > 0
8
8
  result = rules.email[0].rule('test')
9
9
  expect(result).toBe(true)
@@ -20,7 +20,7 @@ it('check rule email', () => {
20
20
  it('check rule password', () => {
21
21
  expect(rules.password.length).toBe(2)
22
22
 
23
- let result
23
+ let result: boolean
24
24
  // password.length > 0
25
25
  result = rules.password[0].rule('test')
26
26
  expect(result).toBe(true)
@@ -40,7 +40,7 @@ it('check rule bool', () => {
40
40
  it('check rule notEmpty', () => {
41
41
  expect(rules.notEmpty.length).toBe(1)
42
42
 
43
- let result
43
+ let result: boolean
44
44
  result = rules.notEmpty[0].rule('')
45
45
  expect(result).toBe(false)
46
46
 
@@ -52,7 +52,7 @@ it('check rule min', () => {
52
52
  expect(rules.min(1).length).toBe(1)
53
53
  expect(typeof rules.min).toBe('function')
54
54
 
55
- let result
55
+ let result: boolean
56
56
  result = rules.min(10)[0].rule('')
57
57
  expect(result).toBe(false)
58
58
 
@@ -76,7 +76,7 @@ it('check rule min', () => {
76
76
  })
77
77
 
78
78
  it('check rule max', () => {
79
- let result
79
+ let result: boolean
80
80
  result = rules.max(10)[0].rule('')
81
81
  expect(result).toBe(false)
82
82
 
@@ -100,7 +100,7 @@ it('check rule max', () => {
100
100
  })
101
101
 
102
102
  it('check rule length', () => {
103
- let result
103
+ let result: boolean
104
104
  result = rules.length(1)[0].rule('')
105
105
  expect(result).toBe(false)
106
106
 
package/src/rules.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ValidatorRule } from './types'
1
+ import type { ValidatorRule } from './types'
2
2
 
3
3
  // eslint-disable-next-line
4
4
  const emailReg =
@@ -45,14 +45,14 @@ export const rules = {
45
45
 
46
46
  min: (min) => [
47
47
  {
48
- rule: (value) => parseFloat(value) > min,
48
+ rule: (value) => Number.parseFloat(value) > min,
49
49
  message: `The value must be greater than ${min}`,
50
50
  },
51
51
  ],
52
52
 
53
53
  max: (max) => [
54
54
  {
55
- rule: (value) => parseFloat(value) < max,
55
+ rule: (value) => Number.parseFloat(value) < max,
56
56
  message: `The value must be smaller ${max}`,
57
57
  },
58
58
  ],
package/src/types.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { ValidatorRules } from './rules'
2
- import { Value } from './validator-field'
1
+ import type { ValidatorRules } from './rules'
2
+ import type { Value } from './validator-field'
3
+
3
4
  type Fn = (value: Value) => string
4
5
 
5
6
  export interface ValidatorRule {
@@ -2,22 +2,20 @@
2
2
  * @jest-environment jsdom
3
3
  */
4
4
 
5
- import { unmountComponentAtNode } from 'react-dom'
6
5
  import { render, screen } from '@testing-library/react'
7
- import { act } from 'react-dom/test-utils'
6
+ import { act } from '@testing-library/react'
8
7
  import { useEffect, useState } from 'react'
9
8
 
10
9
  import { rules } from './rules'
11
10
  import { useValidator } from './use-validator'
12
11
 
13
- let container
12
+ let container: HTMLDivElement | null
14
13
  beforeEach(() => {
15
14
  container = document.createElement('div')
16
15
  document.body.appendChild(container)
17
16
  })
18
17
 
19
18
  afterEach(() => {
20
- unmountComponentAtNode(container)
21
19
  container.remove()
22
20
  container = null
23
21
  })
@@ -39,8 +37,8 @@ it('check state change and hide field', () => {
39
37
 
40
38
  return (
41
39
  <>
42
- <span data-testid='test1'>{isValid ? 'true' : 'false'}</span>
43
- <span data-testid='test2'>{validateObject.message || 'true'}</span>
40
+ <span data-testid="test1">{isValid ? 'true' : 'false'}</span>
41
+ <span data-testid="test2">{validateObject.message || 'true'}</span>
44
42
  </>
45
43
  )
46
44
  }
@@ -1,7 +1,7 @@
1
+ import type { ValidatorRules } from './rules'
2
+ import type { Validity } from './types'
1
3
  import { Validator } from './validator'
2
- import { ValidatorRules } from './rules'
3
- import { Value } from './validator-field'
4
- import { Validity } from './types'
4
+ import type { Value } from './validator-field'
5
5
 
6
6
  export function useValidator(value: Value, rules: ValidatorRules): [boolean, Pick<Validity, 'message' | 'errors'>] {
7
7
  const validator = new Validator()
@@ -2,21 +2,17 @@
2
2
  * @jest-environment jsdom
3
3
  */
4
4
 
5
+ import { act, render } from '@testing-library/react'
5
6
  import { createRef, useEffect, useState } from 'react'
6
- import { render, act } from '@testing-library/react'
7
7
 
8
- import { ValidatorWrapper } from './validator-wrapper'
9
- import { ValidatorField } from './validator-field'
10
8
  import { rules } from './rules'
11
-
12
- it('render without wrapper', () => {
13
- expect(() => shallow(<ValidatorField />)).toThrowError()
14
- })
9
+ import { ValidatorField } from './validator-field'
10
+ import { ValidatorWrapper } from './validator-wrapper'
15
11
 
16
12
  it('normal render', () => {
17
13
  render(
18
14
  <ValidatorWrapper>
19
- <ValidatorField rules={[]} value='' />
15
+ <ValidatorField rules={[]} value="" />
20
16
  </ValidatorWrapper>,
21
17
  )
22
18
  })
@@ -42,10 +38,10 @@ it('check failed validation', () => {
42
38
  render(
43
39
  <>
44
40
  <ValidatorWrapper ref={validator1}>
45
- <ValidatorField rules={rules.email} value='test' />
41
+ <ValidatorField rules={rules.email} value="test" />
46
42
  </ValidatorWrapper>
47
43
  <ValidatorWrapper ref={validator2}>
48
- <ValidatorField rules={rules.email} value='' />
44
+ <ValidatorField rules={rules.email} value="" />
49
45
  </ValidatorWrapper>
50
46
  </>,
51
47
  )
@@ -83,8 +79,8 @@ it('check state change and hide field', () => {
83
79
 
84
80
  return (
85
81
  <ValidatorWrapper ref={validator1}>
86
- <ValidatorField rules={rules.email} value='test' />
87
- {st && <ValidatorField rules={rules.email} value='' />}
82
+ <ValidatorField rules={rules.email} value="test" />
83
+ {st && <ValidatorField rules={rules.email} value="" />}
88
84
  </ValidatorWrapper>
89
85
  )
90
86
  }
@@ -104,7 +100,7 @@ it('check success validation', () => {
104
100
  const validator = createRef<ValidatorWrapper>()
105
101
  render(
106
102
  <ValidatorWrapper ref={validator}>
107
- <ValidatorField rules={rules.email} value='email@email.com' />
103
+ <ValidatorField rules={rules.email} value="email@email.com" />
108
104
  </ValidatorWrapper>,
109
105
  )
110
106
 
@@ -118,7 +114,7 @@ it('check success validation fot child function', () => {
118
114
  const validator = createRef<ValidatorWrapper>()
119
115
  render(
120
116
  <ValidatorWrapper ref={validator}>
121
- <ValidatorField rules={rules.email} value='email@email.com'>
117
+ <ValidatorField rules={rules.email} value="email@email.com">
122
118
  {({ isValid, message }) => <>{!isValid && <div>{message}</div>}</>}
123
119
  </ValidatorField>
124
120
  </ValidatorWrapper>,
@@ -140,7 +136,7 @@ it('check custom rule message function', () => {
140
136
  ]
141
137
  render(
142
138
  <ValidatorWrapper ref={validator}>
143
- <ValidatorField rules={rule} value='test'>
139
+ <ValidatorField rules={rule} value="test">
144
140
  {({ isValid, message }) => <>{!isValid && <div>{message}</div>}</>}
145
141
  </ValidatorField>
146
142
  </ValidatorWrapper>,
@@ -1,10 +1,11 @@
1
- import { Component, ReactNode } from 'react'
2
- import { Validity } from 'types'
1
+ import { Component, type ReactNode } from 'react'
2
+ import type { Validity } from 'types'
3
3
 
4
4
  import { Context } from './context'
5
+ import type { ValidatorRules } from './rules'
5
6
  import { Field } from './validator'
6
- import { ValidatorRules } from './rules'
7
7
 
8
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
8
9
  export type Value = any
9
10
 
10
11
  type Fn = (validity: Validity, value: Value) => ReactNode
@@ -15,8 +16,8 @@ interface Props {
15
16
  value?: Value
16
17
  id?: string | number
17
18
  children?: ReactNode | Fn
18
- unregisterField: (val: any) => void
19
- registerField: (val: any) => void
19
+ unregisterField: (val: Value) => void
20
+ registerField: (val: Value) => void
20
21
  customErrors: Array<Validity>
21
22
  }
22
23
 
@@ -31,7 +32,7 @@ class ValidationFieldWrapper extends Component<Props> {
31
32
 
32
33
  validate(): Validity {
33
34
  const props = this.props
34
- const customError = (props.customErrors || []).find((item) => item.id === props.id)
35
+ const customError = props.customErrors.find((item) => item.id === props.id)
35
36
  if (customError) {
36
37
  return customError
37
38
  }
@@ -2,16 +2,12 @@
2
2
  * @jest-environment jsdom
3
3
  */
4
4
 
5
- import { createRef } from 'react'
6
5
  import { render } from '@testing-library/react'
6
+ import { createRef } from 'react'
7
7
 
8
- import { ValidatorWrapper } from './validator-wrapper'
9
- import { ValidatorField } from './validator-field'
10
8
  import { rules } from './rules'
11
-
12
- it('render without child', () => {
13
- expect(() => shallow(<ValidatorWrapper />)).toThrowError()
14
- })
9
+ import { ValidatorField } from './validator-field'
10
+ import { ValidatorWrapper } from './validator-wrapper'
15
11
 
16
12
  it('check wrapper validator', () => {
17
13
  const validator = createRef<ValidatorWrapper>()
@@ -34,8 +30,8 @@ it('check getField validator', () => {
34
30
  const validator = createRef<ValidatorWrapper>()
35
31
  render(
36
32
  <ValidatorWrapper ref={validator}>
37
- <ValidatorField rules={[]} id='test' />
38
- <ValidatorField rules={[]} id='test-fields' />
33
+ <ValidatorField rules={[]} id="test" />
34
+ <ValidatorField rules={[]} id="test-fields" />
39
35
  </ValidatorWrapper>,
40
36
  )
41
37
  expect(typeof validator.current.getField).toBe('function')
@@ -52,7 +48,7 @@ it('check getField undefined field', () => {
52
48
  const validator = createRef<ValidatorWrapper>()
53
49
  render(
54
50
  <ValidatorWrapper ref={validator}>
55
- <ValidatorField rules={[]} id='test-empty-field' />
51
+ <ValidatorField rules={[]} id="test-empty-field" />
56
52
  </ValidatorWrapper>,
57
53
  )
58
54
 
@@ -64,9 +60,9 @@ it('check stopAtFirstError validator', () => {
64
60
  const validator = createRef<ValidatorWrapper>()
65
61
  render(
66
62
  <ValidatorWrapper ref={validator} stopAtFirstError>
67
- <ValidatorField rules={[]} value='test' />
68
- <ValidatorField rules={rules.email} value='test' />
69
- <ValidatorField rules={rules.password} value='' />
63
+ <ValidatorField rules={[]} value="test" />
64
+ <ValidatorField rules={rules.email} value="test" />
65
+ <ValidatorField rules={rules.password} value="" />
70
66
  </ValidatorWrapper>,
71
67
  )
72
68
  const fieldValidate = validator.current.validate()
@@ -79,7 +75,7 @@ it('check unregisterField, registerField', () => {
79
75
  const validator = createRef<ValidatorWrapper>()
80
76
  render(
81
77
  <ValidatorWrapper ref={validator}>
82
- <ValidatorField rules={[]} id='test-register-field' />
78
+ <ValidatorField rules={[]} id="test-register-field" />
83
79
  </ValidatorWrapper>,
84
80
  )
85
81
 
@@ -92,8 +88,8 @@ it('check filed in field', () => {
92
88
  render(
93
89
  <ValidatorWrapper ref={validator}>
94
90
  <ValidatorField rules={[]}>
95
- <ValidatorField rules={[]} id='check-validate-field-1' />
96
- <ValidatorField rules={[]} id='check-validate-field-2' />
91
+ <ValidatorField rules={[]} id="check-validate-field-1" />
92
+ <ValidatorField rules={[]} id="check-validate-field-2" />
97
93
  </ValidatorField>
98
94
  </ValidatorWrapper>,
99
95
  )
@@ -109,9 +105,9 @@ it('check wrapper in wrapper', () => {
109
105
  const validatorIn = createRef<ValidatorWrapper>()
110
106
  render(
111
107
  <ValidatorWrapper ref={validatorOut}>
112
- <ValidatorField rules={rules.email} value='' />
108
+ <ValidatorField rules={rules.email} value="" />
113
109
  <ValidatorWrapper ref={validatorIn}>
114
- <ValidatorField rules={rules.password} value='successpasswword' />
110
+ <ValidatorField rules={rules.password} value="successpasswword" />
115
111
  </ValidatorWrapper>
116
112
  </ValidatorWrapper>,
117
113
  )
@@ -125,10 +121,10 @@ it('check two validators', () => {
125
121
  render(
126
122
  <>
127
123
  <ValidatorWrapper ref={validatorSuccess}>
128
- <ValidatorField rules={rules.password} value='successpasswword' />
124
+ <ValidatorField rules={rules.password} value="successpasswword" />
129
125
  </ValidatorWrapper>
130
126
  <ValidatorWrapper ref={validatorFailed}>
131
- <ValidatorField rules={rules.email} value='' />
127
+ <ValidatorField rules={rules.email} value="" />
132
128
  </ValidatorWrapper>
133
129
  </>,
134
130
  )
@@ -1,13 +1,13 @@
1
- import { Component, ReactNode, RefObject } from 'react'
1
+ import { Component, type ReactNode, type RefObject } from 'react'
2
2
 
3
- import { Validity } from './types'
4
3
  import { Context } from './context'
5
- import { Field, Validator } from './validator'
4
+ import type { Validity } from './types'
5
+ import { type Field, Validator } from './validator'
6
6
 
7
7
  interface ComponentProps {
8
8
  children?: ReactNode
9
9
  stopAtFirstError?: boolean
10
- ref?: RefObject<any>
10
+ ref?: RefObject<ValidatorWrapper>
11
11
  }
12
12
 
13
13
  export class ValidatorWrapper extends Component<ComponentProps> {
@@ -16,8 +16,8 @@ export class ValidatorWrapper extends Component<ComponentProps> {
16
16
  customErrors: [],
17
17
  }
18
18
 
19
- constructor(props, ctx) {
20
- super(props, ctx)
19
+ constructor(props) {
20
+ super(props)
21
21
  this.registerField = this.registerField.bind(this)
22
22
  this.unregisterField = this.unregisterField.bind(this)
23
23
  }
@@ -53,13 +53,13 @@ export class ValidatorWrapper extends Component<ComponentProps> {
53
53
 
54
54
  validate(): Validity {
55
55
  const validator = new Validator({ stopAtFirstError: this.props.stopAtFirstError })
56
- this.fields.forEach((comp) => {
56
+ for (const comp of this.fields) {
57
57
  validator.addField(comp.props)
58
- })
58
+ }
59
59
  return validator.validate()
60
60
  }
61
61
 
62
- render() {
62
+ render(): ReactNode {
63
63
  return (
64
64
  <Context.Provider
65
65
  value={{