import lodashMemoize from 'lodash/memoize' ; const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\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,}))$/ ; const isEmpty = ( value ) => typeof value === 'undefined' || value === null || value === '' || ( Array . isArray ( value ) && value . length === 0 ) ; export interface ValidationErrorMessageWithArgs { message : string ; args : { [ key ] : ValidationErrorMessageWithArgs | any ; } ; } export type ValidationErrorMessage = string | ValidationErrorMessageWithArgs ; export type Validator = ( value : any , values : any , props : any ) => ValidationErrorMessage | null | undefined | Promise < ValidationErrorMessage | null | undefined > ; function isValidationErrorMessageWithArgs ( error ) { return error . hasOwnProperty ( 'message' ) ; } interface MessageFuncParams { args : any ; value : any ; values : any ; } type MessageFunc = ( params : MessageFuncParams ) => ValidationErrorMessage ; const getMessage = ( message , messageArgs , value , values ) => typeof message === 'function' ? message ( { args : messageArgs , value , values , } ) : messageArgs ? { message , args : messageArgs , } : message ; type Memoize = < T extends ( ... args : any [ ] ) => any > ( func : T , resolver ? : ( ... args : any [ ] ) => any ) => T ; const memoize = ( fn ) => lodashMemoize ( fn , ( ... args ) => JSON . stringify ( args ) ) ; const isFunction = value => typeof value === 'function' ; export const combine2Validators = ( validator1 , validator2 ) : Validator => { return ( value , values , meta ) => { const result1 = validator1 ( value , values , meta ) ; if ( ! result1 ) { return validator2 ( value , values , meta ) ; } if ( typeof result1 === 'string' || isValidationErrorMessageWithArgs ( result1 ) ) { return result1 ; } return result1 . then ( resolvedResult1 => { if ( ! resolvedResult1 ) { return validator2 ( value , values , meta ) ; } return resolvedResult1 ; } ) ; } ; } ; export const composeValidators = ( ... validators ) => { const allValidators = ( Array . isArray ( validators [ 0 ] ) ? validators [ 0 ] : validators ) . filter ( isFunction ) as Validator [ ] ; return allValidators . reduce ( combine2Validators , ( ) => null ) ; } ; export const composeSyncValidators = ( ... validators ) => ( value , values , meta ) => { const allValidators = ( Array . isArray ( validators [ 0 ] ) ? validators [ 0 ] : validators ) . filter ( isFunction ) as Validator [ ] ; for ( const validator of allValidators ) { const error = validator ( value , values , meta ) ; if ( error ) { return error ; } } } ; param { string | Function } message example export const required = memoize ( ( message = 'ra.validation.required' ) => Object . assign ( ( value , values ) => isEmpty ( value ) ? getMessage ( message , undefined , value , values ) : undefined , { isRequired : true } ) ) ; param { integer } min param { string | Function } message example export const minLength = memoize ( ( min , message = 'ra.validation.minLength' ) => ( value , values ) => ! isEmpty ( value ) && value . length < min ? getMessage ( message , { min } , value , values ) : undefined ) ; param { integer } max param { string | Function } message example export const maxLength = memoize ( ( max , message = 'ra.validation.maxLength' ) => ( value , values ) => ! isEmpty ( value ) && value . length > max ? getMessage ( message , { max } , value , values ) : undefined ) ; param { integer } min param { string | Function } message example export const minValue = memoize ( ( min , message = 'ra.validation.minValue' ) => ( value , values ) => ! isEmpty ( value ) && value < min ? getMessage ( message , { min } , value , values ) : undefined ) ; param { integer } max param { string | Function } message example export const maxValue = memoize ( ( max , message = 'ra.validation.maxValue' ) => ( value , values ) => ! isEmpty ( value ) && value > max ? getMessage ( message , { max } , value , values ) : undefined ) ; param { string | Function } message example export const number = memoize ( ( message = 'ra.validation.number' ) => ( value , values ) => ! isEmpty ( value ) && isNaN ( Number ( value ) ) ? getMessage ( message , undefined , value , values ) : undefined ) ; param { RegExp } pattern param { string | Function } message example export const regex = lodashMemoize ( ( pattern , message = 'ra.validation.regex' ) => ( value , values ) => ! isEmpty ( value ) && typeof value === 'string' && ! pattern . test ( value ) ? getMessage ( message , { pattern } , value , values ) : undefined , ( pattern , message ) => { return pattern . toString ( ) + message ; } ) ; param { string | Function } message example export const email = memoize ( ( message = 'ra.validation.email' ) => regex ( EMAIL_REGEX , message ) ) ; const oneOfTypeMessage = ( { args } ) => ( { message : 'ra.validation.oneOf' , args , } ) ; param { array } list param { string | Function } message example export const choices = memoize ( ( list , message = oneOfTypeMessage ) => ( value , values ) => ! isEmpty ( value ) && list . indexOf ( value ) === - 1 ? getMessage ( message , { list } , value , values ) : undefined ) ;