node-vitals
Version:
Do more with less. A simple, high-performing, functional JavaScript library.
973 lines (858 loc) • 24.2 kB
JavaScript
/**
* -----------------------------------------------------------------------------
* VITALS METHOD: is
* -----------------------------------------------------------------------------
* @section base
* @version 4.1.3
* @see [vitals.is]{@link https://github.com/imaginate/vitals/wiki/vitals.is}
*
* @author Adam Smith <adam@imaginate.life> (https://github.com/imaginate)
* @copyright 2017 Adam A Smith <adam@imaginate.life> (https://github.com/imaginate)
*
* Annotations:
* @see [JSDoc3](http://usejsdoc.org)
* @see [Closure Compiler JSDoc Syntax](https://developers.google.com/closure/compiler/docs/js-for-compiler)
*/
'use strict';
var newErrorMaker = require('./helpers/new-error-maker.js');
var own = require('./helpers/own.js');
var _is = require('./helpers/is.js');
////////////////////////////////////////////////////////////////////////////////
// VITALS METHOD: is
////////////////////////////////////////////////////////////////////////////////
var is = (function isPrivateScope() {
//////////////////////////////////////////////////////////
// PUBLIC METHODS
// - is
// - is.null (is.nil)
// - is.undefined
// - is.boolean (is.bool)
// - is.string (is.str)
// - is._string (is._str)
// - is.number (is.num)
// - is._number (is._num)
// - is.nan
// - is.object (is.obj)
// - is._object (is._obj)
// - is.func (is.function|is.fn)
// - is.array (is.arr)
// - is._array (is._arr)
// - is.regexp (is.regex|is.re)
// - is.date
// - is.error (is.err)
// - is.args
// - is.document (is.doc)
// - is.element (is.elem)
// - is.empty
// - is.frozen
// - is.whole
// - is.odd
// - is.even
//////////////////////////////////////////////////////////
/**
* Checks if a value(s) is one of the provided types. See the [type docs](https://github.com/imaginate/vitals/wiki/vitals.is-types)
* for all available options. Note that all object types are nullable by
* default (i.e. `null` will return `true`).
*
* @public
* @param {string} types - The valid data types. See the [type docs](https://github.com/imaginate/vitals/wiki/vitals.is-types)
* for all options.
* @param {...*} val - The value to evaluate. If multiple values are
* provided all must pass the type check to return true.
* @return {boolean} The evaluation result.
*/
function is(types, val) {
/** @type {string} */
var nullable;
/** @type {Array<function>} */
var checks;
if (arguments.length < 2) throw _error('No type or val');
if ( !_is._str(types) ) throw _error.type('types');
if ( _hasSpecial('*', types) ) return true;
checks = _getChecks(types);
if (!checks) throw _error.range('types', DOCS);
nullable = _getNullable(types);
return arguments.length > 2
? _checkVals(checks, arguments, nullable)
: _checkVal(checks, val, nullable);
}
/**
* Checks if a value(s) is `null`.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is['null'] = function isNull(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'null');
case 1: return _is.nil(val);
default: return _are(arguments, _is.nil);
}
};
// define shorthand
is.nil = is['null'];
/**
* Checks if a value(s) is `undefined`.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.undefined = function isUndefined(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'undefined');
case 1: return _is.undefined(val);
default: return _are(arguments, _is.undefined);
}
};
/**
* Checks if a value(s) is a boolean.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is['boolean'] = function isBoolean(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'boolean');
case 1: return _is.bool(val);
default: return _are(arguments, _is.bool);
}
};
// define shorthand
is.bool = is['boolean'];
/**
* Checks if a value(s) is a string.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.string = function isString(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'string');
case 1: return _is.str(val);
default: return _are(arguments, _is.str);
}
};
// define shorthand
is.str = is.string;
/**
* Checks if a value(s) is a non-empty string.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is._string = function isNonEmptyString(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', '_string');
case 1: return _is._str(val);
default: return _are(arguments, _is._str);
}
};
// define shorthand
is._str = is._string;
/**
* Checks if a value(s) is a number.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.number = function isNumber(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'number');
case 1: return _is.num(val);
default: return _are(arguments, _is.num);
}
};
// define shorthand
is.num = is.number;
/**
* Checks if a value(s) is a number and not `0`.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is._number = function isNonZeroNumber(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', '_number');
case 1: return _is._num(val);
default: return _are(arguments, _is._num);
}
};
// define shorthand
is._num = is._number;
/**
* Checks if a value(s) is `NaN`.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.nan = function isNan(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'nan');
case 1: return _is.nan(val);
default: return _are(arguments, _is.nan);
}
};
/**
* Checks if a value(s) is an object.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.object = function isObject(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'object');
case 1: return _is.obj(val);
default: return _are(arguments, _is.obj);
}
};
// define shorthand
is.obj = is.object;
/**
* Checks if a value(s) is an object or function.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is._object = function isObjectOrFunction(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', '_object');
case 1: return _is._obj(val);
default: return _are(arguments, _is._obj);
}
};
// define shorthand
is._obj = is._object;
/**
* Checks if a value(s) is a function. Note that `vitals.is.function` is not
* valid in ES3 and some ES5 browser environments. Use `vitals.is.func` for
* browser safety.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.func = function isFunction(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'function');
case 1: return _is.func(val);
default: return _are(arguments, _is.func);
}
};
// define shorthand
is.fn = is.func;
try {
is['function'] = is.func;
}
catch (error) {}
/**
* Checks if a value(s) is an `Array` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.array = function isArray(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'array');
case 1: return _is.arr(val);
default: return _are(arguments, _is.arr);
}
};
// define shorthand
is.arr = is.array;
/**
* Checks if a value(s) is an `Array` or `Arguments` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is._array = function isArrayOrArguments(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', '_array');
case 1: return _is._arr(val);
default: return _are(arguments, _is._arr);
}
};
// define shorthand
is._arr = is._array;
/**
* Checks if a value(s) is a `RegExp` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.regexp = function isRegExp(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'regexp');
case 1: return _is.regex(val);
default: return _are(arguments, _is.regex);
}
};
// define shorthand
is.regex = is.regexp;
is.re = is.regexp;
/**
* Checks if a value(s) is a `Date` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.date = function isDate(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'date');
case 1: return _is.date(val);
default: return _are(arguments, _is.date);
}
};
/**
* Checks if a value(s) is an `Error` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.error = function isError(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'error');
case 1: return _is.err(val);
default: return _are(arguments, _is.err);
}
};
// define shorthand
is.err = is.error;
/**
* Checks if a value(s) is an `Arguments` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.args = function isArguments(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'args');
case 1: return _is.args(val);
default: return _are(arguments, _is.args);
}
};
/**
* Checks if a value(s) is a DOM `Document` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.document = function isDocument(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'document');
case 1: return _is.doc(val);
default: return _are(arguments, _is.doc);
}
};
// define shorthand
is.doc = is.document;
/**
* Checks if a value(s) is a DOM `Element` instance.
*
* @public
* @param {...*} val
* @return {boolean}
*/
is.element = function isElement(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'element');
case 1: return _is.elem(val);
default: return _are(arguments, _is.elem);
}
};
// define shorthand
is.elem = is.element;
/**
* Checks if a value(s) is considered empty.
*
* @public
* @param {...*} val
* @return {boolean} Returns `false` if value is one of the following:
* ```
* 0, "", {}, [], null, undefined, false, NaN, function(){...}
* ```
* Note that for functions this method checks whether it has any defined
* params:
* ```
* function empty(){}
* function notEmpty(param){}
* ```
*/
is.empty = function isEmpty(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'empty');
case 1: return _is.empty(val);
default: return _are(arguments, _is.empty);
}
};
/**
* Checks if a value(s) is [frozen](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/isFrozen).
*
* @public
* @param {...(Object|?function)} val
* @return {boolean}
*/
is.frozen = function isFrozen(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'frozen');
case 1: return _isFrozen(val);
default: return _are(arguments, _isFrozen);
}
};
/**
* Checks if a number(s) is whole (i.e. has no decimal). Zero and non-decimal
* negative numbers will return `true`.
*
* @public
* @param {...number} val
* @return {boolean}
*/
is.whole = function isWholeNumber(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'whole');
case 1: return _isWhole(val);
default: return _are(arguments, _isWhole);
}
};
/**
* Checks if a number(s) is odd.
*
* @public
* @param {...number} val - Each val must be a whole number.
* @return {boolean}
*/
is.odd = function isOddNumber(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'odd');
case 1: return _isOdd(val);
default: return _are(arguments, _isOdd);
}
};
/**
* Checks if a number(s) is even.
*
* @public
* @param {...number} val - Each val must be a whole number.
* @return {boolean}
*/
is.even = function isEvenNumber(val) {
switch (arguments.length) {
case 0: throw _error('Missing a val', 'even');
case 1: return _isEven(val);
default: return _are(arguments, _isEven);
}
};
//////////////////////////////////////////////////////////
// PRIVATE METHODS - ARE
//////////////////////////////////////////////////////////
/**
* @private
* @param {!Arguments} vals
* @param {function} check
* @return {boolean}
*/
function _are(vals, check) {
/** @type {number} */
var i;
i = vals.length;
while (i--) {
if ( !check(vals[i]) ) return false;
}
return true;
}
//////////////////////////////////////////////////////////
// PRIVATE METHODS - IS
//////////////////////////////////////////////////////////
/**
* @private
* @param {(Object|?function)} val
* @return {boolean}
*/
function _isFrozen(val) {
if ( _is.nil(val) ) return false;
if ( !_is._obj(val) ) throw _error.type('val', 'frozen');
return _is.frozen(val);
}
/**
* @private
* @param {number} val
* @return {boolean}
*/
function _isWhole(val) {
if ( !_is.num(val) ) throw _error.type('val', 'whole');
return _is.whole(val);
}
/**
* @private
* @param {number} val
* @return {boolean}
*/
function _isOdd(val) {
if ( !_is.num(val) ) throw _error.type('val', 'odd');
if ( !_is.whole(val) ) throw _error.range('val', 'whole numbers', 'odd');
return _is.odd(val);
}
/**
* @private
* @param {number} val
* @return {boolean}
*/
function _isEven(val) {
if ( !_is.num(val) ) throw _error.type('val', 'even');
if ( !_is.whole(val) ) throw _error.range('val', 'whole numbers', 'even');
return _is.even(val);
}
//////////////////////////////////////////////////////////
// PRIVATE METHODS - CHECKS
//////////////////////////////////////////////////////////
/**
* @private
* @param {!Array<function>} checks
* @param {*} val
* @param {boolean=} nullable
* @return {boolean}
*/
function _checkVal(checks, val, nullable) {
/** @type {number} */
var i;
i = checks.length;
while (i--) {
if ( checks[i](val, nullable) ) return true;
}
return false;
}
/**
* @private
* @param {!Array<function>} checks
* @param {!Arguments} vals
* @param {boolean=} nullable
* @return {boolean}
*/
function _checkVals(checks, vals, nullable) {
/** @type {number} */
var i;
i = vals.length;
while (--i) {
if ( !_checkVal(checks, vals[i], nullable) ) return false;
}
return true;
}
//////////////////////////////////////////////////////////
// PRIVATE METHODS - TYPES
//////////////////////////////////////////////////////////
/**
* @typedef {!Object<string, function(*, boolean=): boolean>} DataTypes
*/
/**
* @private
* @type {DataTypes}
*/
var TYPES = (function() {
/**
* @type {DataTypes}
*/
var _types = {};
/**
* Adds types to the _types hash map with a check method that evaluates
* nullable properties and invokes their type section's method.
* @private
* @param {string} section - The category for the types.
* @param {!Object<string, function(*): boolean>} types - Each type's
* "key => value" pair should be expressed as "typeName => checkMethod".
* @param {boolean=} nullable - The type's default nullable value. Defaults
* to true if not set.
* @return {DataTypes}
*/
function addTypes(section, types, nullable) {
/** @type {string} */
var type;
for (type in types) {
if( own(types, type) ) addType(section, type, types[type], nullable);
}
return _types;
}
/**
* Adds type to the _types hash map with a check method that evaluates
* nullable properties and invokes its type section's method.
* @private
* @param {string} section - The type's category.
* @param {string} type - The type's name.
* @param {function(*): boolean} check - The type's check method.
* @param {boolean=} nullable - The type's default nullable value. Defaults
* to true if not set.
* @return {DataTypes}
*/
function addType(section, type, check, nullable) {
check = own(addType, section) ? addType[section](check) : check;
nullable = nullable !== false;
_types['_' + type] = function(val, _nullable) {
_nullable = _is.bool(_nullable) ? _nullable : nullable;
return _is.nil(val) ? _nullable : check(val);
};
return _types;
}
/**
* Adds the type shortcuts to the _types hash map.
* @private
* @param {!Object<string, string>} shortcuts
* @return {DataTypes}
*/
function addShortcuts(shortcuts) {
/** @type {string} */
var shortcut;
/** @type {string} */
var type;
for (shortcut in shortcuts) {
if( own(shortcuts, shortcut) ) {
type = '_' + shortcuts[shortcut];
shortcut = '_' + shortcut;
_types[shortcut] = _types[type];
}
}
return _types;
}
/**
* @private
* @param {function(*): boolean} eachCheck - The check method for each of
* the array's values.
* @return {function(*): boolean} The array type's check method.
*/
addType.arrays = function(eachCheck) {
/** @type {function(*): boolean} */
return function check(arr) {
/** @type {number} */
var i;
if ( !_is.arr(arr) ) return false;
i = arr.length;
while (i--) {
if ( !eachCheck(arr[i]) ) return false;
}
return true;
};
};
/**
* @private
* @param {function(*): boolean} eachCheck - The check method for each of
* the hash map's properties.
* @return {function(*): boolean} The hash map type's check method.
*/
addType.maps = function(eachCheck) {
/** @type {function(*): boolean} */
return function check(obj) {
/** @type {string} */
var prop;
if ( !_is.obj(obj) ) return false;
for (prop in obj) {
if( own(obj, prop) && !eachCheck(obj[prop]) ) return false;
}
return true;
};
};
_types = addTypes('primitives', {
'undefined': _is.undefined,
'boolean': _is.bool,
'string': _is.str,
'number': _is.num,
'nan': _is.nan
}, false);
_types = addType('primitives', 'null', _is.nil);
_types = addTypes('js_objects', {
'object': _is.obj,
'regexp': _is.regex,
'array': _is.arr,
'date': _is.date,
'error': _is.err
});
_types = addType('js_objects', 'arguments', _is.args);
_types = addType('js_objects', 'function', _is.func, false);
_types = addTypes('dom_objects', {
'element': _is.elem,
'document': _is.doc
});
_types = addType('others', 'empty', _is.empty);
_types = addTypes('arrays', {
'nulls': _is.nil,
'booleans': _is.bool,
'strings': _is.str,
'numbers': _is.num,
'nans': _is.nan,
'objects': _is.obj,
'functions': _is.func,
'regexps': _is.regex,
'arrays': _is.arr,
'dates': _is.date,
'errors': _is.err,
'elements': _is.elem,
'documents': _is.doc
});
_types = addTypes('maps', {
'nullmap': _is.nil,
'booleanmap': _is.bool,
'stringmap': _is.str,
'numbermap': _is.num,
'nanmap': _is.nan,
'objectmap': _is.obj,
'functionmap': _is.func,
'regexpmap': _is.regex,
'arraymap': _is.arr,
'datemap': _is.date,
'errormap': _is.err,
'elementmap': _is.elem,
'documentmap': _is.doc
});
_types = addShortcuts({
// primitives
nil: 'null',
bool: 'boolean',
str: 'string',
num: 'number',
// js objects
obj: 'object',
func: 'function',
fn: 'function',
regex: 'regexp',
re: 'regexp',
arr: 'array',
err: 'error',
args: 'arguments',
// dom objects
elem: 'element',
doc: 'document',
// arrays
nils: 'nulls',
strs: 'strings',
nums: 'numbers',
bools: 'booleans',
objs: 'objects',
funcs: 'functions',
fns: 'functions',
regexs: 'regexps',
res: 'regexps',
arrs: 'arrays',
errs: 'errors',
elems: 'elements',
docs: 'documents',
// maps
nilmap: 'nullmap',
strmap: 'stringmap',
nummap: 'numbermap',
boolmap: 'booleanmap',
objmap: 'objectmap',
funcmap: 'functionmap',
fnmap: 'functionmap',
regexmap: 'regexpmap',
remap: 'regexpmap',
arrmap: 'arraymap',
errmap: 'errormap',
elemmap: 'elementmap',
docmap: 'documentmap'
});
return _types;
})();
//////////////////////////////////////////////////////////
// PRIVATE METHODS - PARSING
//////////////////////////////////////////////////////////
/**
* @private
* @type {!RegExp}
*/
var ALL_SPECIALS = /[^a-z\|]/g;
/**
* @private
* @type {!Object<string, function(string): boolean>}
*/
var SPECIALS = (function(pipe, exPoint, quesMark, equals, asterisk) {
return {
'|': function(str) { return pipe.test(str); },
'!': function(str) { return exPoint.test(str); },
'?': function(str) { return quesMark.test(str); },
'=': function(str) { return equals.test(str); },
'*': function(str) { return asterisk.test(str); }
};
})(/\|/, /\!/, /\?/, /\=/, /\*|any/);
/**
* @private
* @param {string} special
* @param {string} types
* @return {boolean}
*/
function _hasSpecial(special, types) {
return SPECIALS[special](types);
}
/**
* @private
* @param {string} types
* @return {Array<function>}
*/
function _getChecks(types) {
/** @type {Array<function>} */
var checks;
/** @type {string} */
var type;
/** @type {number} */
var i;
if ( _hasSpecial('=', types) ) types += '|undefined';
types = types.toLowerCase();
types = types.replace(ALL_SPECIALS, '');
checks = types.split('|');
i = checks.length;
while (i--) {
type = '_' + checks[i];
if ( !own(TYPES, type) ) return null;
checks[i] = TYPES[type];
}
return checks.length ? checks : null;
}
/**
* Method checks whether "!" or "?" exists in the types.
* @private
* @param {string} types
* @return {(undefined|boolean)} If undefined no override exists.
*/
function _getNullable(types) {
/** @type {boolean} */
var override;
/** @type {boolean} */
var ensure;
/** @type {boolean} */
var negate;
ensure = _hasSpecial('?', types);
negate = _hasSpecial('!', types);
override = ensure && negate ? false : ensure || negate;
return override ? !negate && ensure : undefined;
}
//////////////////////////////////////////////////////////
// PRIVATE METHODS - GENERAL
//////////////////////////////////////////////////////////
/**
* @private
* @type {!ErrorAid}
*/
var _error = newErrorMaker('is');
/**
* @private
* @type {string}
*/
var DOCS = 'https://github.com/imaginate/vitals/wiki/vitals.is-types';
//////////////////////////////////////////////////////////
// END OF PRIVATE SCOPE FOR IS
return is;
})();
module.exports = is;