rambdax
Version:
Extended version of Rambda - a lightweight, faster alternative to Ramda
188 lines (166 loc) • 5.03 kB
JavaScript
import { _isArray } from './_internals/_isArray'
import { all } from './all'
import { any } from './any'
import { includes } from './includes'
import { init } from './init'
import { test } from './test'
import { toLower } from './toLower'
import { type } from './type'
export function isPrototype(input){
const currentPrototype = input.prototype
const list = [ Number, String, Boolean, Promise ]
let toReturn = false
let counter = -1
while (++counter < list.length && !toReturn){
if (currentPrototype === list[ counter ].prototype) toReturn = true
}
return toReturn
}
export function prototypeToString(input){
const currentPrototype = input.prototype
const list = [ Number, String, Boolean, Promise ]
const translatedList = [ 'Number', 'String', 'Boolean', 'Promise' ]
let found
let counter = -1
while (++counter < list.length){
if (currentPrototype === list[ counter ].prototype) found = counter
}
return translatedList[ found ]
}
const typesWithoutPrototype = [ 'any', 'promise', 'async', 'function' ]
export function fromPrototypeToString(rule){
if (
_isArray(rule) ||
rule === undefined ||
rule === null ||
rule.prototype === undefined ||
typesWithoutPrototype.includes(rule)
){
return {
rule,
parsed : false,
}
}
if (String.prototype === rule.prototype){
return {
rule : 'string',
parsed : true,
}
}
if (Boolean.prototype === rule.prototype){
return {
rule : 'boolean',
parsed : true,
}
}
if (Number.prototype === rule.prototype){
return {
rule : 'number',
parsed : true,
}
}
return {
rule : type(rule.prototype).toLowerCase(),
parsed : true,
}
}
function getRuleAndType(schema, requirementRaw){
const ruleRaw = schema[ requirementRaw ]
const typeIs = type(ruleRaw)
const { rule, parsed } = fromPrototypeToString(ruleRaw)
return {
rule : rule,
ruleType : parsed ? 'String' : typeIs,
}
}
export function isValid({ input, schema }){
if (input === undefined || schema === undefined) return false
let flag = true
const boom = boomFlag => {
if (!boomFlag){
flag = false
}
}
for (const requirementRaw in schema){
if (flag){
const isOptional = requirementRaw.endsWith('?')
const requirement = isOptional ? init(requirementRaw) : requirementRaw
const { rule, ruleType } = getRuleAndType(schema, requirementRaw)
const inputProp = input[ requirement ]
const inputPropType = type(input[ requirement ])
const ok = isOptional && inputProp !== undefined || !isOptional
if (!ok || rule === 'any' && inputProp != null || rule === inputProp)
continue
if (ruleType === 'Object'){
/**
* This rule is standalone schema, so we recursevly call `isValid`
*/
const isValidResult = isValid({
input : inputProp,
schema : rule,
})
boom(isValidResult)
} else if (ruleType === 'String'){
/**
* Rule is actual rule such as 'number', so the two types are compared
*/
boom(toLower(inputPropType) === rule)
} else if (typeof rule === 'function'){
/**
* Rule is function so we pass to it the input
*/
boom(rule(inputProp))
} else if (ruleType === 'Array' && inputPropType === 'String'){
/**
* Enum case | rule is like a: ['foo', 'bar']
*/
boom(includes(inputProp, rule))
} else if (
ruleType === 'Array' &&
rule.length === 1 &&
inputPropType === 'Array'
){
/**
* 1. array of type | rule is like a: ['number']
* 2. rule is like a: [{foo: 'string', bar: 'number'}]
*/
const [ currentRule ] = rule
const currentRuleType = type(currentRule)
//Check if rule is invalid
boom(currentRuleType === 'String' ||
currentRuleType === 'Object' ||
isPrototype(currentRule))
if (currentRuleType === 'Object' && flag){
/**
* 2. rule is like a: [{from: 'string'}]
*/
const isValidResult = all(inputPropInstance =>
isValid({
input : inputPropInstance,
schema : currentRule,
}),
inputProp)
boom(isValidResult)
} else if (flag){
/**
* 1. array of type
*/
const actualRule =
currentRuleType === 'String' ?
currentRule :
prototypeToString(currentRule)
const isInvalidResult = any(inputPropInstance =>
type(inputPropInstance).toLowerCase() !==
actualRule.toLowerCase(),
inputProp)
boom(!isInvalidResult)
}
} else if (ruleType === 'RegExp' && inputPropType === 'String'){
boom(test(rule, inputProp))
} else {
boom(false)
}
}
}
return flag
}