Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/modules / ___dist / chain-able / src / deps / validators / validatorBuilder.js
Size: Mime:
"use strict";

/**
 * @since 4.0.0 <- moved out of the store, into scoped
 * @since 1.0.0
 * @desc library of validators to use by name
 *       @modifies this.validators
 * @param  {Object} validators
 */
const ChainedMap = require("../../ChainedMapBase");

const ENV_DEBUG = require("../env/debug");

const is = require("../is/_core");

const isString = require("../is/string");

const isFunction = require("../is/function");

const dopemerge = require("../dopemerge");

const camelCase = require("../string/camelCase");

const not = require("../conditional/not");

const or = require("../conditional/or");

const isArrayOf = require("../is/arrayOf");

const isNotRealOrIsEmpty = require("../is/notRealOrIsEmpty");

const replace = require("../fp/replace");

let validators = new ChainedMap(); // eslint-disable-next-line

const stripArithmeticSymbols = replace(/[?\[\]!\|]/g, '');

const escapedKey = x => camelCase('is-' + x);

const enummy = enums => x => enums === x || enums.includes(x); // @TODO: .remap!!!
// @TODO: can use these to return noops with error logging on development


const get = key => validators.get(key) || validators.get(escapedKey(key)) || enummy(key);

const has = key => validators.has(key) || validators.get(escapedKey(key));

const set = (key, value) => validators.set(key, value);

const doesNotHave = not(has);
/**
 * @desc add custom types for validation
 * @category types
 * @category schema
 * @types schema
 *
 * @since 4.0.0 <- used with schema, used in method chain
 * @since 3.0.0 <- took out
 * @since 1.0.0
 *
 * @param  {Object} types custom Types
 *
 * @see deps/validators/validatorFactory
 *
 * @example
 *
 *   addTypes({yaya: x => typeof x === 'string'})
 *
 *   const chain = new Chain().methods('eh').type('yaya').build()
 *
 *   chain.eh('good')
 *   //=> chain
 *
 *   chain.eh(!!'throws')
 *   //=> TypeError(false != {yaya: x => typeof x === 'string'})
 *
 * @example
 *
 *   const custom = {}
 *   custom.enums = enums => x => enums.includes(x)
 *   custom['*'] = x => true
 *   addTypes(custom)
 *   //-> void
 *
 *   new Chain().methods('eh').type('*').build().eh
 *   //=> validateType(custom['*'])
 *
 */

const addTypes = types => validators.from(dopemerge(validators.entries(), types));

addTypes(is);

const includesAndOr = x => x.includes('|') || x.includes('&');
/**
 * @memberOf schema
 * @category types
 *
 * @param  {string} fullKey a key with `|` and/or '&'
 * @return {Function} validator
 *
 * @example
 *
 *    const isStringOrNumber = typeListFactory('string|number')
 *
 *    isStringOrNumber(1)
 *    //=> true
 *    isStringOrNumber('one')
 *    //=> true
 *    isStringOrNumber(Object)
 *    //=> false
 *
 */


function typeListFactory(fullKey) {
  // already have it
  if (has(fullKey)) {
    return get(fullKey);
  } // get all types


  let orTypes = fullKey.split('|');
  let andTypes = fullKey.split('&'); // ensure we have all validators - sets up conditionals

  for (let v = 0; v < orTypes.length; v++) {
    builder(orTypes[v]);
  } // go through all valid options, if any are true, good to go


  set(fullKey, x => {
    for (let v = 0; v < orTypes.length; v++) {
      if (get(orTypes[v])(x)) {
        return true;
      }
    }

    return false;
  });
  return get(fullKey);
} // @TODO how to iterate properly with the bitwise fn + AND
//       add another param? ignore overly complex |& things? just allow 1?
//       just show how to use these shorthand fn builders

/**
 * @desc transform arithmetic strings into types
 * @since 4.0.0-alpha.1
 * @category types
 *
 * @param  {Matchable} fullKey arithmetic type key
 * @return {Matchable} function to match with, with .inspect for easy debugging
 *
 * @types schema
 * @test typed
 * @test schema
 * @see is
 * @todo coercing values to certain types: arithmeticTypeFactory('<value>')
 *
 * @example
 *
 *   arithmeticTypeFactory('?string')
 *   //=> x => !isReal(x) || isString(x)
 *
 * @example
 *
 *   arithmeticTypeFactory('?string|string[]')
 *   //=> x => isString(x) || isArrayOf(isString)(x)
 *
 * @example
 *
 *   arithmeticTypeFactory('!string')
 *   //=> x => not(isString)(x)
 *
 * @example
 *
 *   types.addTypes({star: x => true})
 *   arithmeticTypeFactory('object|function|star')
 *   //=> x => isObj(x) || isFunction(x) || isStar(x)
 *
 * @example
 *
 *   arithmeticTypeFactory('===')
 *   //=> x => (['===']).includes(x)
 */


function arithmeticTypeFactory(fullKey) {
  const key = stripArithmeticSymbols(fullKey);
  let fn = get(key);
  const optionalType = `?${key}`;
  const typeOrArrayOrType = `${key}[]`;
  const notType = `!${key}`;
  const isValidOrNotRealOrEmptyStr = or(fn, isNotRealOrIsEmpty);
  const isValidOrArrayOfValid = or(fn, isArrayOf(fn));

  if (doesNotHave(optionalType)) {
    set(optionalType, isValidOrNotRealOrEmptyStr);
  }

  if (doesNotHave(typeOrArrayOrType)) {
    set(typeOrArrayOrType, isValidOrArrayOfValid);
  }

  if (doesNotHave(notType)) {
    set(notType, not(fn));
  }

  return get(fullKey);
} // ----
// ; function split
// ----
// v- annoying on comments with ifs

/* prettier-ignore */

/**
 * @desc @pattern @builder -> builds using multiple factories depending on conditons
 *       or abstractFactory whatever
 *       opinionated: if it's a function, it's a validator...
 *
 * @category types
 * @since 4.0.0
 * @param  {string | Function | Primitive} fullKey arithmetic key to the validator
 * @return {Function} validator
 *
 * @see https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions/Default_parameters
 * @NOTE if/else is for uglifying ternaries, even though else if is not needed
 * @NOTE if key is number, iterating the array
 *
 * @example
 *
 *    // functionType
 *    const isString = x => typeof x === 'string'
 *    builder(isString)
 *    //=> isString
 *
 * @example
 *
 *    // stringType (built in, or custom-keyed validator, or eqeqeq)
 *    builder('string')
 *    //=> isString
 *
 *    const enummy = builder('enum')
 *    //=> x => ['enum'].includes(x)
 *
 * @example
 *
 *    // arithmeticType
 *    builder('string|string[]')
 *    //=> isString || isArrayOf(isString)
 *
 */


function builder(fullKey) {
  if (isFunction(fullKey)) {
    /* istanbul ignore next: dev */
    if (ENV_DEBUG) {
      console.log('functionType', {
        fullKey
      });
    }

    return fullKey;
  } else if (isString(fullKey) && includesAndOr(fullKey)) {
    /* istanbul ignore next: dev */
    if (ENV_DEBUG) {
      console.log('andOrType', {
        fullKey
      });
    }

    return typeListFactory(fullKey);
  } else {
    /* istanbul ignore next: dev */
    if (ENV_DEBUG) {
      console.log('arithmeticType', {
        fullKey
      }, arithmeticTypeFactory(fullKey));
    }

    return arithmeticTypeFactory(fullKey);
  }
}

builder.has = has;
builder.get = get;
builder.set = set;
builder.addTypes = addTypes; // was merge

builder.map = validators;
module.exports = builder;