@coxy/react-validator 2.0.7 → 4.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 +145 -120
- package/biome.json +34 -0
- package/dist/index.d.mts +106 -0
- package/dist/index.d.ts +106 -6
- package/dist/index.js +2 -28
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/example/example.tsx +26 -26
- package/example/index.html +2 -7
- package/example/tsconfig.json +6 -11
- package/package.json +26 -36
- package/src/context.ts +8 -3
- package/src/custom-errors.test.tsx +73 -0
- package/src/index.test.ts +19 -4
- package/src/index.ts +4 -3
- package/src/rules.test.ts +15 -10
- package/src/rules.ts +15 -15
- package/src/types.ts +3 -2
- package/src/use-validator.test.tsx +4 -7
- package/src/use-validator.ts +3 -3
- package/src/validator-field.test.tsx +45 -18
- package/src/validator-field.tsx +44 -53
- package/src/validator-wrapper.test.tsx +41 -27
- package/src/validator-wrapper.tsx +60 -57
- package/src/validator.test.tsx +10 -0
- package/src/validator.ts +10 -10
- package/tsconfig.json +6 -16
- package/tsup.config.ts +14 -0
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -5
- package/.prettierrc.js +0 -10
- package/CHANGELOG.md +0 -49
- package/dist/context.d.ts +0 -7
- package/dist/context.js +0 -5
- package/dist/rules.d.ts +0 -32
- package/dist/rules.js +0 -60
- package/dist/types.d.ts +0 -24
- package/dist/types.js +0 -2
- package/dist/use-validator.d.ts +0 -4
- package/dist/use-validator.js +0 -22
- package/dist/validator-field.d.ts +0 -17
- package/dist/validator-field.js +0 -38
- package/dist/validator-wrapper.d.ts +0 -24
- package/dist/validator-wrapper.js +0 -57
- package/dist/validator.d.ts +0 -21
- package/dist/validator.js +0 -72
- package/src/jest.d.ts +0 -1
- package/tsconfig.build.json +0 -12
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rules.ts","../src/validator.ts","../src/use-validator.ts","../src/context.ts","../src/validator-field.tsx","../src/validator-wrapper.tsx"],"names":["emailReg","rules","value","min","max","Field","required","id","isValid","message","isEmptyValue","instance","Validator","params","field","index","prevResult","errors","inst","useValidator","validator","validateObject","Context","createContext","ValidatorField","forwardRef","props","_ref","children","customErrors","registerField","unregisterField","useContext","propsRef","useRef","customErrorsRef","handleRef","curr","customError","item","useEffect","validity","ValidatorWrapper","stopAtFirstError","ref","fieldsRef","setCustomErrors","useState","useCallback","getField","setCustomError","prev","clearCustomErrors","validate","comp","useImperativeHandle","contextValue","useMemo","jsx"],"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: string) => value !== '' && value.length > 0,\n message: 'Value is required',\n },\n ],\n\n bool: [\n {\n rule: (value: string) => !!value,\n message: 'Value is required',\n },\n ],\n\n password: [\n {\n rule: (value: string) => value.length > 0,\n message: 'Password field cannot be empty',\n },\n {\n rule: (value: string) => value.length > 5,\n message: 'Password field can not be less than 6 characters',\n },\n ],\n\n email: [\n {\n rule: (value: string) => !!value && value !== '' && value.length !== 0,\n message: 'Email is required',\n },\n {\n rule: (value: string) => emailReg.test(String(value).toLowerCase()),\n message: 'Email is invalid',\n },\n ],\n\n min: (min: number) => [\n {\n rule: (value: string) => Number.parseFloat(value) > min,\n message: `The value must be greater than ${min}`,\n },\n ],\n\n max: (max: number) => [\n {\n rule: (value: string) => Number.parseFloat(value) < max,\n message: `The value must be smaller ${max}`,\n },\n ],\n\n length: (min: number, max?: number) => [\n {\n rule: (value: string) => String(value).length >= min,\n message: `No less than ${min} symbols`,\n },\n {\n rule: (value: string) => (max !== undefined ? String(value).length <= max : true),\n message: `No more than ${max} symbols`,\n },\n ],\n}\n","import type { ValidatorRules } from './rules'\nimport type { FieldParams, Validity } from './types'\nimport type { Value } from './validator-field'\n\nexport class Field {\n protected rules: ValidatorRules\n protected required: boolean\n protected 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 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","import { createContext } from 'react'\n\nimport type { FieldParams, Validity } from './types'\n\nexport interface RegisteredFieldHandle {\n props: FieldParams\n validate: () => Validity\n}\n\nexport const Context = createContext<{\n registerField: (field: RegisteredFieldHandle) => void\n unregisterField: (field: RegisteredFieldHandle) => void\n customErrors: Array<Validity>\n}>(null)\n","import { forwardRef, type ReactNode, useContext, useEffect, useRef } from 'react'\nimport type { FieldParams, Validity } from 'types'\n\nimport { Context, type RegisteredFieldHandle } from './context'\nimport { Field } from './validator'\n\n// biome-ignore lint/suspicious/noExplicitAny: <need>\nexport type Value = any\n\ntype Fn = (validity: Validity, value: Value) => ReactNode\n\ntype Props = FieldParams & {\n children?: ReactNode | Fn\n}\n\nexport const ValidatorField = forwardRef<unknown, Props>(function ValidatorField(props: Props, _ref) {\n const { children, value } = props\n const { customErrors, registerField, unregisterField } = useContext(Context)\n\n const propsRef = useRef(props)\n propsRef.current = props\n\n const customErrorsRef = useRef(customErrors)\n customErrorsRef.current = customErrors\n\n const handleRef = useRef<RegisteredFieldHandle | null>(null)\n if (!handleRef.current) {\n handleRef.current = {\n get props() {\n return propsRef.current\n },\n validate: () => {\n const curr = propsRef.current\n const customError = customErrorsRef.current.find((item) => item.id === curr.id)\n if (customError) {\n return customError\n }\n const field = new Field({\n rules: curr.rules,\n required: curr.required,\n value: curr.value,\n id: curr.id,\n })\n return field.validate()\n },\n }\n }\n\n useEffect(() => {\n registerField(handleRef.current as RegisteredFieldHandle)\n return () => {\n unregisterField(handleRef.current as RegisteredFieldHandle)\n }\n }, [registerField, unregisterField])\n\n const validity = handleRef.current.validate()\n\n return typeof children === 'function' ? (children as Fn)(validity, value) : (children as ReactNode)\n})\n","import { forwardRef, type ReactNode, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'\n\nimport { Context, type RegisteredFieldHandle } from './context'\nimport type { Validity } from './types'\nimport { Validator } from './validator'\n\ninterface ComponentProps {\n children?: ReactNode\n stopAtFirstError?: boolean\n}\n\nexport interface ValidatorWrapper {\n validate: () => Validity\n getField: (id: string | number) => RegisteredFieldHandle | null\n registerField: (field: RegisteredFieldHandle) => void\n unregisterField: (field: RegisteredFieldHandle) => void\n setCustomError: (customError: Validity) => void\n clearCustomErrors: () => void\n}\n\nexport const ValidatorWrapper = forwardRef<ValidatorWrapper, ComponentProps>(function ValidatorWrapper(\n { children, stopAtFirstError },\n ref,\n) {\n const fieldsRef = useRef<RegisteredFieldHandle[]>([])\n const [customErrors, setCustomErrors] = useState<Validity[]>([])\n\n const registerField = useCallback((field: RegisteredFieldHandle) => {\n if (field && !fieldsRef.current.includes(field)) {\n fieldsRef.current.push(field)\n }\n }, [])\n\n const unregisterField = useCallback((field: RegisteredFieldHandle) => {\n const index = fieldsRef.current.indexOf(field)\n if (index > -1) fieldsRef.current.splice(index, 1)\n }, [])\n\n const getField = useCallback<ValidatorWrapper['getField']>((id) => {\n return fieldsRef.current.find((field) => field?.props?.id === id) || null\n }, [])\n\n const setCustomError = useCallback<ValidatorWrapper['setCustomError']>((customError) => {\n setCustomErrors((prev) => [...prev, customError])\n }, [])\n\n const clearCustomErrors = useCallback<ValidatorWrapper['clearCustomErrors']>(() => {\n setCustomErrors([])\n }, [])\n\n const validate = useCallback<ValidatorWrapper['validate']>(() => {\n const validator = new Validator({ stopAtFirstError })\n for (const comp of fieldsRef.current) {\n validator.addField(comp.props)\n }\n return validator.validate()\n }, [stopAtFirstError])\n\n useImperativeHandle(\n ref,\n () => ({\n validate,\n getField,\n registerField,\n unregisterField,\n setCustomError,\n clearCustomErrors,\n }),\n [validate, getField, registerField, unregisterField, setCustomError, clearCustomErrors],\n )\n\n const contextValue = useMemo(\n () => ({ customErrors, registerField, unregisterField }),\n [customErrors, registerField, unregisterField],\n )\n\n return <Context.Provider value={contextValue}>{children}</Context.Provider>\n})\n"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {createContext,forwardRef,useContext,useRef,useEffect,useState,useCallback,useImperativeHandle,useMemo}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,}))$/,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=createContext(null);var w=forwardRef(function(e,t){let{children:s,value:i}=e,{customErrors:d,registerField:u,unregisterField:n}=useContext(v),a=useRef(e);a.current=e;let g=useRef(d);g.current=d;let p=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()}}),useEffect(()=>(u(p.current),()=>{n(p.current);}),[u,n]);let F=p.current.validate();return typeof s=="function"?s(F,i):s});var S=forwardRef(function({children:e,stopAtFirstError:t},s){let i=useRef([]),[d,u]=useState([]),n=useCallback(l=>{l&&!i.current.includes(l)&&i.current.push(l);},[]),a=useCallback(l=>{let o=i.current.indexOf(l);o>-1&&i.current.splice(o,1);},[]),g=useCallback(l=>i.current.find(o=>o?.props?.id===l)||null,[]),p=useCallback(l=>{u(o=>[...o,l]);},[]),F=useCallback(()=>{u([]);},[]),m=useCallback(()=>{let l=new c({stopAtFirstError:t});for(let o of i.current)l.addField(o.props);return l.validate()},[t]);useImperativeHandle(s,()=>({validate:m,getField:g,registerField:n,unregisterField:a,setCustomError:p,clearCustomErrors:F}),[m,g,n,a,p,F]);let y=useMemo(()=>({customErrors:d,registerField:n,unregisterField:a}),[d,n,a]);return jsx(v.Provider,{value:y,children:e})});export{c as Validator,w as ValidatorField,S as ValidatorWrapper,x as rules,b as useValidator};//# sourceMappingURL=index.mjs.map
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/rules.ts","../src/validator.ts","../src/use-validator.ts","../src/context.ts","../src/validator-field.tsx","../src/validator-wrapper.tsx"],"names":["emailReg","rules","value","min","max","Field","required","id","isValid","message","isEmptyValue","instance","Validator","params","field","index","prevResult","errors","inst","useValidator","validator","validateObject","Context","createContext","ValidatorField","forwardRef","props","_ref","children","customErrors","registerField","unregisterField","useContext","propsRef","useRef","customErrorsRef","handleRef","curr","customError","item","useEffect","validity","ValidatorWrapper","stopAtFirstError","ref","fieldsRef","setCustomErrors","useState","useCallback","getField","setCustomError","prev","clearCustomErrors","validate","comp","useImperativeHandle","contextValue","useMemo","jsx"],"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: string) => value !== '' && value.length > 0,\n message: 'Value is required',\n },\n ],\n\n bool: [\n {\n rule: (value: string) => !!value,\n message: 'Value is required',\n },\n ],\n\n password: [\n {\n rule: (value: string) => value.length > 0,\n message: 'Password field cannot be empty',\n },\n {\n rule: (value: string) => value.length > 5,\n message: 'Password field can not be less than 6 characters',\n },\n ],\n\n email: [\n {\n rule: (value: string) => !!value && value !== '' && value.length !== 0,\n message: 'Email is required',\n },\n {\n rule: (value: string) => emailReg.test(String(value).toLowerCase()),\n message: 'Email is invalid',\n },\n ],\n\n min: (min: number) => [\n {\n rule: (value: string) => Number.parseFloat(value) > min,\n message: `The value must be greater than ${min}`,\n },\n ],\n\n max: (max: number) => [\n {\n rule: (value: string) => Number.parseFloat(value) < max,\n message: `The value must be smaller ${max}`,\n },\n ],\n\n length: (min: number, max?: number) => [\n {\n rule: (value: string) => String(value).length >= min,\n message: `No less than ${min} symbols`,\n },\n {\n rule: (value: string) => (max !== undefined ? String(value).length <= max : true),\n message: `No more than ${max} symbols`,\n },\n ],\n}\n","import type { ValidatorRules } from './rules'\nimport type { FieldParams, Validity } from './types'\nimport type { Value } from './validator-field'\n\nexport class Field {\n protected rules: ValidatorRules\n protected required: boolean\n protected 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 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","import { createContext } from 'react'\n\nimport type { FieldParams, Validity } from './types'\n\nexport interface RegisteredFieldHandle {\n props: FieldParams\n validate: () => Validity\n}\n\nexport const Context = createContext<{\n registerField: (field: RegisteredFieldHandle) => void\n unregisterField: (field: RegisteredFieldHandle) => void\n customErrors: Array<Validity>\n}>(null)\n","import { forwardRef, type ReactNode, useContext, useEffect, useRef } from 'react'\nimport type { FieldParams, Validity } from 'types'\n\nimport { Context, type RegisteredFieldHandle } from './context'\nimport { Field } from './validator'\n\n// biome-ignore lint/suspicious/noExplicitAny: <need>\nexport type Value = any\n\ntype Fn = (validity: Validity, value: Value) => ReactNode\n\ntype Props = FieldParams & {\n children?: ReactNode | Fn\n}\n\nexport const ValidatorField = forwardRef<unknown, Props>(function ValidatorField(props: Props, _ref) {\n const { children, value } = props\n const { customErrors, registerField, unregisterField } = useContext(Context)\n\n const propsRef = useRef(props)\n propsRef.current = props\n\n const customErrorsRef = useRef(customErrors)\n customErrorsRef.current = customErrors\n\n const handleRef = useRef<RegisteredFieldHandle | null>(null)\n if (!handleRef.current) {\n handleRef.current = {\n get props() {\n return propsRef.current\n },\n validate: () => {\n const curr = propsRef.current\n const customError = customErrorsRef.current.find((item) => item.id === curr.id)\n if (customError) {\n return customError\n }\n const field = new Field({\n rules: curr.rules,\n required: curr.required,\n value: curr.value,\n id: curr.id,\n })\n return field.validate()\n },\n }\n }\n\n useEffect(() => {\n registerField(handleRef.current as RegisteredFieldHandle)\n return () => {\n unregisterField(handleRef.current as RegisteredFieldHandle)\n }\n }, [registerField, unregisterField])\n\n const validity = handleRef.current.validate()\n\n return typeof children === 'function' ? (children as Fn)(validity, value) : (children as ReactNode)\n})\n","import { forwardRef, type ReactNode, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'\n\nimport { Context, type RegisteredFieldHandle } from './context'\nimport type { Validity } from './types'\nimport { Validator } from './validator'\n\ninterface ComponentProps {\n children?: ReactNode\n stopAtFirstError?: boolean\n}\n\nexport interface ValidatorWrapper {\n validate: () => Validity\n getField: (id: string | number) => RegisteredFieldHandle | null\n registerField: (field: RegisteredFieldHandle) => void\n unregisterField: (field: RegisteredFieldHandle) => void\n setCustomError: (customError: Validity) => void\n clearCustomErrors: () => void\n}\n\nexport const ValidatorWrapper = forwardRef<ValidatorWrapper, ComponentProps>(function ValidatorWrapper(\n { children, stopAtFirstError },\n ref,\n) {\n const fieldsRef = useRef<RegisteredFieldHandle[]>([])\n const [customErrors, setCustomErrors] = useState<Validity[]>([])\n\n const registerField = useCallback((field: RegisteredFieldHandle) => {\n if (field && !fieldsRef.current.includes(field)) {\n fieldsRef.current.push(field)\n }\n }, [])\n\n const unregisterField = useCallback((field: RegisteredFieldHandle) => {\n const index = fieldsRef.current.indexOf(field)\n if (index > -1) fieldsRef.current.splice(index, 1)\n }, [])\n\n const getField = useCallback<ValidatorWrapper['getField']>((id) => {\n return fieldsRef.current.find((field) => field?.props?.id === id) || null\n }, [])\n\n const setCustomError = useCallback<ValidatorWrapper['setCustomError']>((customError) => {\n setCustomErrors((prev) => [...prev, customError])\n }, [])\n\n const clearCustomErrors = useCallback<ValidatorWrapper['clearCustomErrors']>(() => {\n setCustomErrors([])\n }, [])\n\n const validate = useCallback<ValidatorWrapper['validate']>(() => {\n const validator = new Validator({ stopAtFirstError })\n for (const comp of fieldsRef.current) {\n validator.addField(comp.props)\n }\n return validator.validate()\n }, [stopAtFirstError])\n\n useImperativeHandle(\n ref,\n () => ({\n validate,\n getField,\n registerField,\n unregisterField,\n setCustomError,\n clearCustomErrors,\n }),\n [validate, getField, registerField, unregisterField, setCustomError, clearCustomErrors],\n )\n\n const contextValue = useMemo(\n () => ({ customErrors, registerField, unregisterField }),\n [customErrors, registerField, unregisterField],\n )\n\n return <Context.Provider value={contextValue}>{children}</Context.Provider>\n})\n"]}
|
package/example/example.tsx
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import React, { createRef, useState } from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom/client'
|
|
3
|
+
import { rules, Validator, ValidatorField, ValidatorWrapper } from '../dist/index'
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
function App () {
|
|
5
|
+
function App() {
|
|
8
6
|
const [email, setEmail] = useState('')
|
|
9
7
|
const jsxValidator = createRef<ValidatorWrapper>()
|
|
10
8
|
|
|
11
|
-
function handleValidateEmail
|
|
9
|
+
function handleValidateEmail() {
|
|
12
10
|
const { isValid, message, errors } = jsxValidator.current.validate()
|
|
13
11
|
if (!isValid) {
|
|
14
12
|
console.log(isValid, message, errors)
|
|
@@ -17,12 +15,12 @@ function App () {
|
|
|
17
15
|
console.log('success')
|
|
18
16
|
}
|
|
19
17
|
|
|
20
|
-
function handleValidateCustomFields
|
|
18
|
+
function handleValidateCustomFields() {
|
|
21
19
|
const validator = new Validator({ stopAtFirstError: true })
|
|
22
20
|
const fieldPassword = validator.addField({
|
|
23
21
|
rules: rules.password,
|
|
24
22
|
value: '',
|
|
25
|
-
id: 'for-remove'
|
|
23
|
+
id: 'for-remove',
|
|
26
24
|
})
|
|
27
25
|
|
|
28
26
|
const fieldSearchPassword = validator.getField('for-remove')
|
|
@@ -32,7 +30,7 @@ function App () {
|
|
|
32
30
|
|
|
33
31
|
validator.addField({
|
|
34
32
|
rules: rules.password,
|
|
35
|
-
value: 'testpassword'
|
|
33
|
+
value: 'testpassword',
|
|
36
34
|
})
|
|
37
35
|
|
|
38
36
|
const { isValid, message, errors } = validator.validate()
|
|
@@ -44,24 +42,26 @@ function App () {
|
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
return (
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
<ValidatorWrapper ref={jsxValidator}>
|
|
46
|
+
<ValidatorField rules={rules.email} value={email}>
|
|
47
|
+
{({ isValid, message }) => (
|
|
48
|
+
<>
|
|
49
|
+
<input onChange={({ target: { value } }) => setEmail(value)} />
|
|
50
|
+
<div>{isValid ? 'valid' : 'invalid'}</div>
|
|
51
|
+
<div>{message || ''}</div>
|
|
52
|
+
<button onClick={handleValidateEmail} type="button">
|
|
53
|
+
Validate email
|
|
54
|
+
</button>
|
|
55
|
+
</>
|
|
56
|
+
)}
|
|
57
|
+
</ValidatorField>
|
|
58
|
+
<button onClick={handleValidateCustomFields} type="button">
|
|
59
|
+
Validate custom fields
|
|
60
|
+
</button>
|
|
61
|
+
</ValidatorWrapper>
|
|
62
62
|
)
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
const root = createRoot(document.getElementById('root'))
|
|
65
|
+
const root = ReactDOM.createRoot(document.getElementById('root'))
|
|
66
66
|
|
|
67
67
|
root.render(<App />)
|
package/example/index.html
CHANGED
|
@@ -3,14 +3,9 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<title>Test template</title>
|
|
6
|
-
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
|
|
7
|
-
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
|
|
8
6
|
</head>
|
|
9
7
|
<body>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
<script src="./dist/example.js"></script>
|
|
14
|
-
|
|
8
|
+
<div id="root"></div>
|
|
9
|
+
<script type="module" src="./dist/example.js"></script>
|
|
15
10
|
</body>
|
|
16
11
|
</html>
|
package/example/tsconfig.json
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "../tsconfig.json",
|
|
3
2
|
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"jsx": "react",
|
|
4
6
|
"outDir": "./dist",
|
|
5
|
-
"
|
|
6
|
-
"baseUrl": "./",
|
|
7
|
+
"allowSyntheticDefaultImports": true
|
|
7
8
|
},
|
|
8
|
-
"include": [
|
|
9
|
-
|
|
10
|
-
],
|
|
11
|
-
"exclude": [
|
|
12
|
-
"node_modules",
|
|
13
|
-
"dist"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
9
|
+
"include": ["example.tsx"]
|
|
10
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coxy/react-validator",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.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,47 +21,35 @@
|
|
|
19
21
|
],
|
|
20
22
|
"author": "dsshard",
|
|
21
23
|
"scripts": {
|
|
22
|
-
"start": "
|
|
23
|
-
"build": "
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
24
|
+
"start": "tsup ./src/index.ts --watch",
|
|
25
|
+
"build": "tsup",
|
|
26
|
+
"lint": "biome lint --fix",
|
|
27
|
+
"example": "NODE_ENV=production npx esbuild example/example.tsx --bundle --outfile=example/dist/example.js --minify --format=esm --target=es2020",
|
|
28
|
+
"coverage": "npx codecov",
|
|
29
|
+
"coveralls": "npx coveralls .",
|
|
27
30
|
"test": "jest"
|
|
28
31
|
},
|
|
29
32
|
"license": "MIT",
|
|
30
33
|
"peerDependencies": {
|
|
31
34
|
"react": ">=18.x.x"
|
|
32
35
|
},
|
|
33
|
-
"dependencies": {
|
|
34
|
-
"react": ">=18.x.x"
|
|
35
|
-
},
|
|
36
|
+
"dependencies": {},
|
|
36
37
|
"devDependencies": {
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"@
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
42
|
-
"@
|
|
43
|
-
"@
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
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"
|
|
38
|
+
"react": ">=18.x.x",
|
|
39
|
+
"react-dom": ">=18.x.x",
|
|
40
|
+
"@biomejs/biome": "2.2.2",
|
|
41
|
+
"@testing-library/dom": "10.4.1",
|
|
42
|
+
"@testing-library/react": "16.3.0",
|
|
43
|
+
"@types/jest": "30.0.0",
|
|
44
|
+
"@types/node": "24.1.0",
|
|
45
|
+
"@types/react": "19.1.11",
|
|
46
|
+
"@types/react-dom": "19.1.7",
|
|
47
|
+
"jest": "30.0.5",
|
|
48
|
+
"jest-environment-jsdom": "30.0.5",
|
|
49
|
+
"tsup": "8.5.0",
|
|
50
|
+
"react-test-renderer": "19.1.1",
|
|
51
|
+
"ts-jest": "29.4.0",
|
|
52
|
+
"ts-node": "10.9.2",
|
|
53
|
+
"typescript": "5.8.3"
|
|
64
54
|
}
|
|
65
55
|
}
|
package/src/context.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { createContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import { Validity } from './types'
|
|
3
|
+
import type { FieldParams, Validity } from './types'
|
|
4
|
+
|
|
5
|
+
export interface RegisteredFieldHandle {
|
|
6
|
+
props: FieldParams
|
|
7
|
+
validate: () => Validity
|
|
8
|
+
}
|
|
4
9
|
|
|
5
10
|
export const Context = createContext<{
|
|
6
|
-
registerField: (field:
|
|
7
|
-
unregisterField: (field:
|
|
11
|
+
registerField: (field: RegisteredFieldHandle) => void
|
|
12
|
+
unregisterField: (field: RegisteredFieldHandle) => void
|
|
8
13
|
customErrors: Array<Validity>
|
|
9
14
|
}>(null)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @jest-environment jsdom
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { act, render } from '@testing-library/react'
|
|
6
|
+
import { createRef } from 'react'
|
|
7
|
+
|
|
8
|
+
import { rules } from './rules'
|
|
9
|
+
import { ValidatorField } from './validator-field'
|
|
10
|
+
import { ValidatorWrapper, type ValidatorWrapper as ValidatorWrapperHandle } from './validator-wrapper'
|
|
11
|
+
|
|
12
|
+
it('setCustomError overrides field validation result and clearCustomErrors restores it', () => {
|
|
13
|
+
const validator = createRef<ValidatorWrapperHandle>()
|
|
14
|
+
|
|
15
|
+
render(
|
|
16
|
+
<ValidatorWrapper ref={validator}>
|
|
17
|
+
<ValidatorField id="email-field" rules={rules.email} value="user@example.com" />
|
|
18
|
+
</ValidatorWrapper>,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// Initially valid
|
|
22
|
+
const fieldBefore = validator.current.getField('email-field')
|
|
23
|
+
const validityBefore = fieldBefore.validate()
|
|
24
|
+
expect(validityBefore.isValid).toBe(true)
|
|
25
|
+
expect(validityBefore.message).toBe('')
|
|
26
|
+
|
|
27
|
+
// Set a custom error
|
|
28
|
+
act(() => {
|
|
29
|
+
validator.current.setCustomError({ id: 'email-field', isValid: false, message: 'Custom error' })
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const fieldWithCustom = validator.current.getField('email-field')
|
|
33
|
+
const validityWithCustom = fieldWithCustom.validate()
|
|
34
|
+
expect(validityWithCustom.isValid).toBe(false)
|
|
35
|
+
expect(validityWithCustom.message).toBe('Custom error')
|
|
36
|
+
|
|
37
|
+
// Clear custom errors
|
|
38
|
+
act(() => {
|
|
39
|
+
validator.current.clearCustomErrors()
|
|
40
|
+
})
|
|
41
|
+
const fieldAfter = validator.current.getField('email-field')
|
|
42
|
+
const validityAfter = fieldAfter.validate()
|
|
43
|
+
expect(validityAfter.isValid).toBe(true)
|
|
44
|
+
expect(validityAfter.message).toBe('')
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('custom error is used inside ValidatorField render-prop child', () => {
|
|
48
|
+
const validator = createRef<ValidatorWrapperHandle>()
|
|
49
|
+
const messages: string[] = []
|
|
50
|
+
|
|
51
|
+
render(
|
|
52
|
+
<ValidatorWrapper ref={validator}>
|
|
53
|
+
<ValidatorField id="field-x" rules={rules.password} value="strongpassword">
|
|
54
|
+
{({ message }) => {
|
|
55
|
+
messages.push(message)
|
|
56
|
+
return null
|
|
57
|
+
}}
|
|
58
|
+
</ValidatorField>
|
|
59
|
+
</ValidatorWrapper>,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
// Initially valid → message pushed should be ''
|
|
63
|
+
expect(messages[messages.length - 1]).toBe('')
|
|
64
|
+
|
|
65
|
+
// After setting custom error, the render-prop should see the custom message.
|
|
66
|
+
act(() => {
|
|
67
|
+
validator.current.setCustomError({ id: 'field-x', isValid: false, message: 'Injected' })
|
|
68
|
+
})
|
|
69
|
+
const field = validator.current.getField('field-x')
|
|
70
|
+
const res = field.validate()
|
|
71
|
+
expect(res.isValid).toBe(false)
|
|
72
|
+
expect(res.message).toBe('Injected')
|
|
73
|
+
})
|
package/src/index.test.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { rules, useValidator, Validator, ValidatorField, ValidatorWrapper } from './index'
|
|
2
2
|
|
|
3
|
-
it('
|
|
4
|
-
expect(typeof ValidatorWrapper).toBe('
|
|
5
|
-
expect(typeof ValidatorField).toBe('
|
|
3
|
+
it('exports surface is available', () => {
|
|
4
|
+
expect(typeof ValidatorWrapper).toBe('object')
|
|
5
|
+
expect(typeof ValidatorField).toBe('object')
|
|
6
6
|
expect(typeof rules).toBe('object')
|
|
7
|
+
expect(typeof Validator).toBe('function')
|
|
8
|
+
expect(typeof useValidator).toBe('function')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('use exports to execute a basic validation flow', () => {
|
|
12
|
+
// use Validator (class)
|
|
13
|
+
const validator = new Validator()
|
|
14
|
+
validator.addField({ value: 'test@example.com', rules: rules.email })
|
|
15
|
+
const res = validator.validate()
|
|
16
|
+
expect(res.isValid).toBe(true)
|
|
17
|
+
|
|
18
|
+
// use useValidator (hook-like util function)
|
|
19
|
+
const [isValid, { message }] = useValidator('bad-email', rules.email)
|
|
20
|
+
expect(isValid).toBe(false)
|
|
21
|
+
expect(message).toBe('Email is invalid')
|
|
7
22
|
})
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { rules } from './rules'
|
|
2
|
+
|
|
3
|
+
export type { ErrorMessage, FieldParams, ValidatorRule, Validity } from './types'
|
|
4
|
+
export { useValidator } from './use-validator'
|
|
5
|
+
export { Validator } from './validator'
|
|
2
6
|
export { ValidatorField } from './validator-field'
|
|
3
7
|
export { ValidatorWrapper } from './validator-wrapper'
|
|
4
|
-
export { Validator } from './validator'
|
|
5
|
-
export { useValidator } from './use-validator'
|
|
6
|
-
export * from './types'
|
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)
|
|
@@ -33,6 +33,7 @@ it('check rule password', () => {
|
|
|
33
33
|
it('check rule bool', () => {
|
|
34
34
|
expect(rules.bool.length).toBe(1)
|
|
35
35
|
|
|
36
|
+
// @ts-expect-error
|
|
36
37
|
const result = rules.bool[0].rule(true)
|
|
37
38
|
expect(result).toBe(true)
|
|
38
39
|
})
|
|
@@ -40,7 +41,7 @@ it('check rule bool', () => {
|
|
|
40
41
|
it('check rule notEmpty', () => {
|
|
41
42
|
expect(rules.notEmpty.length).toBe(1)
|
|
42
43
|
|
|
43
|
-
let result
|
|
44
|
+
let result: boolean
|
|
44
45
|
result = rules.notEmpty[0].rule('')
|
|
45
46
|
expect(result).toBe(false)
|
|
46
47
|
|
|
@@ -52,22 +53,24 @@ it('check rule min', () => {
|
|
|
52
53
|
expect(rules.min(1).length).toBe(1)
|
|
53
54
|
expect(typeof rules.min).toBe('function')
|
|
54
55
|
|
|
55
|
-
let result
|
|
56
|
+
let result: boolean
|
|
56
57
|
result = rules.min(10)[0].rule('')
|
|
57
58
|
expect(result).toBe(false)
|
|
58
59
|
|
|
59
|
-
result = rules.min(9)[0].rule('
|
|
60
|
+
result = rules.min(9)[0].rule('test-test-test')
|
|
60
61
|
expect(result).toBe(false)
|
|
61
62
|
|
|
62
63
|
result = rules.min(9)[0].rule('11')
|
|
63
64
|
expect(result).toBe(true)
|
|
64
65
|
|
|
66
|
+
// @ts-expect-error
|
|
65
67
|
result = rules.min(9)[0].rule(10)
|
|
66
68
|
expect(result).toBe(true)
|
|
67
69
|
|
|
68
70
|
result = rules.min(9)[0].rule('8')
|
|
69
71
|
expect(result).toBe(false)
|
|
70
72
|
|
|
73
|
+
// @ts-expect-error
|
|
71
74
|
result = rules.min(9)[0].rule(7)
|
|
72
75
|
expect(result).toBe(false)
|
|
73
76
|
|
|
@@ -76,22 +79,24 @@ it('check rule min', () => {
|
|
|
76
79
|
})
|
|
77
80
|
|
|
78
81
|
it('check rule max', () => {
|
|
79
|
-
let result
|
|
82
|
+
let result: boolean
|
|
80
83
|
result = rules.max(10)[0].rule('')
|
|
81
84
|
expect(result).toBe(false)
|
|
82
85
|
|
|
83
|
-
result = rules.max(9)[0].rule('
|
|
86
|
+
result = rules.max(9)[0].rule('test-test-test')
|
|
84
87
|
expect(result).toBe(false)
|
|
85
88
|
|
|
86
89
|
result = rules.max(9)[0].rule('11')
|
|
87
90
|
expect(result).toBe(false)
|
|
88
91
|
|
|
92
|
+
// @ts-expect-error
|
|
89
93
|
result = rules.max(9)[0].rule(10)
|
|
90
94
|
expect(result).toBe(false)
|
|
91
95
|
|
|
92
96
|
result = rules.max(9)[0].rule('5')
|
|
93
97
|
expect(result).toBe(true)
|
|
94
98
|
|
|
99
|
+
// @ts-expect-error
|
|
95
100
|
result = rules.max(9)[0].rule(5)
|
|
96
101
|
expect(result).toBe(true)
|
|
97
102
|
|
|
@@ -100,17 +105,17 @@ it('check rule max', () => {
|
|
|
100
105
|
})
|
|
101
106
|
|
|
102
107
|
it('check rule length', () => {
|
|
103
|
-
let result
|
|
108
|
+
let result: boolean
|
|
104
109
|
result = rules.length(1)[0].rule('')
|
|
105
110
|
expect(result).toBe(false)
|
|
106
111
|
|
|
107
112
|
result = rules.length(1)[0].rule('1')
|
|
108
113
|
expect(result).toBe(true)
|
|
109
114
|
|
|
110
|
-
result = rules.length(1, 10)[0].rule('
|
|
115
|
+
result = rules.length(1, 10)[0].rule('test-test-test')
|
|
111
116
|
expect(result).toBe(true)
|
|
112
117
|
|
|
113
|
-
result = rules.length(1, 10)[1].rule('
|
|
118
|
+
result = rules.length(1, 10)[1].rule('test-test-test')
|
|
114
119
|
expect(result).toBe(false)
|
|
115
120
|
|
|
116
121
|
result = rules.length(1, 10)[0].rule('lol')
|
package/src/rules.ts
CHANGED
|
@@ -1,69 +1,69 @@
|
|
|
1
|
-
import { ValidatorRule } from './types'
|
|
1
|
+
import type { ValidatorRule } from './types'
|
|
2
2
|
|
|
3
3
|
// eslint-disable-next-line
|
|
4
4
|
const emailReg =
|
|
5
|
-
/^(([^<>()
|
|
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
6
|
|
|
7
7
|
export type ValidatorRules = ValidatorRule[]
|
|
8
8
|
|
|
9
9
|
export const rules = {
|
|
10
10
|
notEmpty: [
|
|
11
11
|
{
|
|
12
|
-
rule: (value) => value !== '' && value.length > 0,
|
|
12
|
+
rule: (value: string) => value !== '' && value.length > 0,
|
|
13
13
|
message: 'Value is required',
|
|
14
14
|
},
|
|
15
15
|
],
|
|
16
16
|
|
|
17
17
|
bool: [
|
|
18
18
|
{
|
|
19
|
-
rule: (value) => !!value,
|
|
19
|
+
rule: (value: string) => !!value,
|
|
20
20
|
message: 'Value is required',
|
|
21
21
|
},
|
|
22
22
|
],
|
|
23
23
|
|
|
24
24
|
password: [
|
|
25
25
|
{
|
|
26
|
-
rule: (value) => value.length > 0,
|
|
26
|
+
rule: (value: string) => value.length > 0,
|
|
27
27
|
message: 'Password field cannot be empty',
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
|
-
rule: (value) => value.length > 5,
|
|
30
|
+
rule: (value: string) => value.length > 5,
|
|
31
31
|
message: 'Password field can not be less than 6 characters',
|
|
32
32
|
},
|
|
33
33
|
],
|
|
34
34
|
|
|
35
35
|
email: [
|
|
36
36
|
{
|
|
37
|
-
rule: (value) => !!value && value !== '' && value.length !== 0,
|
|
37
|
+
rule: (value: string) => !!value && value !== '' && value.length !== 0,
|
|
38
38
|
message: 'Email is required',
|
|
39
39
|
},
|
|
40
40
|
{
|
|
41
|
-
rule: (value) => emailReg.test(String(value).toLowerCase()),
|
|
41
|
+
rule: (value: string) => emailReg.test(String(value).toLowerCase()),
|
|
42
42
|
message: 'Email is invalid',
|
|
43
43
|
},
|
|
44
44
|
],
|
|
45
45
|
|
|
46
|
-
min: (min) => [
|
|
46
|
+
min: (min: number) => [
|
|
47
47
|
{
|
|
48
|
-
rule: (value) => parseFloat(value) > min,
|
|
48
|
+
rule: (value: string) => Number.parseFloat(value) > min,
|
|
49
49
|
message: `The value must be greater than ${min}`,
|
|
50
50
|
},
|
|
51
51
|
],
|
|
52
52
|
|
|
53
|
-
max: (max) => [
|
|
53
|
+
max: (max: number) => [
|
|
54
54
|
{
|
|
55
|
-
rule: (value) => parseFloat(value) < max,
|
|
55
|
+
rule: (value: string) => Number.parseFloat(value) < max,
|
|
56
56
|
message: `The value must be smaller ${max}`,
|
|
57
57
|
},
|
|
58
58
|
],
|
|
59
59
|
|
|
60
|
-
length: (min, max
|
|
60
|
+
length: (min: number, max?: number) => [
|
|
61
61
|
{
|
|
62
|
-
rule: (value) => String(value).length >= min,
|
|
62
|
+
rule: (value: string) => String(value).length >= min,
|
|
63
63
|
message: `No less than ${min} symbols`,
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
|
-
rule: (value) => (max !== undefined ? String(value).length <= max : true),
|
|
66
|
+
rule: (value: string) => (max !== undefined ? String(value).length <= max : true),
|
|
67
67
|
message: `No more than ${max} symbols`,
|
|
68
68
|
},
|
|
69
69
|
],
|
package/src/types.ts
CHANGED