From 9abf9c084188353cfbe6b5b46fcc53f4144a4628 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Sun, 26 Apr 2020 14:17:51 +0530 Subject: [PATCH 01/12] [Feat] Added path to error; Added isObject rule --- package.json | 1 + src/RuleSet.js | 49 ++++++++++++++-- src/Validator.js | 46 ++++++++++++++- src/index.js | 1 + src/rules/index.js | 1 + src/rules/isAlpha.js | 16 +++++- src/rules/isAlphaNum.js | 16 +++++- src/rules/isEmail.js | 16 +++++- src/rules/isIn.js | 16 +++++- src/rules/isInt.js | 16 +++++- src/rules/isLen.js | 16 +++++- src/rules/isNumber.js | 16 +++++- src/rules/isObject.js | 120 +++++++++++++++++++++++++++++++++++++++ src/rules/isRequired.js | 16 +++++- src/rules/isString.js | 16 +++++- src/rules/matchRegex.js | 16 +++++- src/rules/toInt.js | 16 +++++- src/rules/toLowerCase.js | 13 ++++- src/rules/toNumber.js | 16 +++++- src/rules/toUpperCase.js | 13 ++++- test/15.isObject.js | 103 +++++++++++++++++++++++++++++++++ 21 files changed, 505 insertions(+), 34 deletions(-) create mode 100644 src/rules/isObject.js create mode 100644 test/15.isObject.js diff --git a/package.json b/package.json index 0ebf75f..285dff1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "build:es": "babel --env-name es src --out-dir es", "build:docs": "./node_modules/.bin/esdoc", "test": "mocha", + "dev:test": "mocha", "pretest": "npm run build" }, "devDependencies": { diff --git a/src/RuleSet.js b/src/RuleSet.js index 7542f2d..5cf9d2d 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -54,12 +54,16 @@ class RuleSet { * @param {String} key Key of the value being checked * @param {Object} options Options for validate * @param {Object} options.returnEarly If `true` returns the after getting the first error. + * @param {String} options.path Validation path. + * @param {Boolean} options.showNestedError If `true` shows nested errors. * @returns {validationError[]} An object containing `value` and `errors` if any */ validate(valueToCheck, key, options) { - const errors = []; + let errors = []; let modifiedValue = valueToCheck; let returnEarly = false; + let path = null; + let showNestedError = false; if (options !== undefined) { if (typeof options !== 'object') { @@ -73,19 +77,52 @@ class RuleSet { returnEarly = options.returnEarly; } + + if (options.path !== undefined) { + if (typeof options.path !== 'string') { + throw new TypeError('`options.path` should be a string.'); + } + + path = options.path; + } + + if (options.showNestedError !== undefined) { + if (typeof options.showNestedError !== 'boolean') { + throw new TypeError('`options.showNestedError` should be a boolean.'); + } + + showNestedError = options.showNestedError; + } } for (const rule of this.__rules) { if (!(rule instanceof Rule)) { throw new TypeError('Rule should be an instance of `Rule` class.'); } - const { value, error } = rule.validate( - modifiedValue, - this.__label || key, - ); + + let currentPath = path ? `${path}.${key}` : key; + const { value, error } = rule.validate(modifiedValue, { + label: this.__label || key, + path: currentPath, + showNestedError, + }); + modifiedValue = value; + if (error) { - errors.push({ error, validator: rule.__name, value }); + if (!showNestedError && typeof error === 'object') { + for (const innerKey in error) { + if (error[innerKey]) { + errors = errors.concat(error[innerKey]); + } + } + } else + errors.push({ + error, + validator: rule.__name, + value, + path: currentPath, + }); if (returnEarly) break; } } diff --git a/src/Validator.js b/src/Validator.js index 1730056..9bac19d 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -22,12 +22,19 @@ export default class Validator { */ __returnRuleSetEarly; + /** + * @ignore + * @private + */ + __showNestedError; + /** * Creates a validator schema. * @param {Object} objectOfRuleSet Set of `RuleSet`. `key` should match with the `key` of object being validated. * @param {Object} options Options for validator schema. * @param {Boolean} options.returnEarly If `true` returns whenever first `key` in `values` fails the test. * @param {Boolean} options.returnRuleSetEarly If `true` returns the after getting the first error on all `keys`. + * @param {Boolean} options.showNestedError If `true` shows nested errors. */ constructor(objectOfRuleSet, options) { if (!objectOfRuleSet || typeof objectOfRuleSet !== 'object') { @@ -60,6 +67,14 @@ export default class Validator { this.__returnRuleSetEarly = options.returnRuleSetEarly; } + + if (options.showNestedError !== undefined) { + if (typeof options.showNestedError !== 'boolean') { + throw new TypeError('`options.showNestedError` should be a boolean.'); + } + + this.__showNestedError = options.showNestedError; + } } this.__ruleSets = { ...objectOfRuleSet }; @@ -69,13 +84,40 @@ export default class Validator { * Validates the `values` passed and returns `error` object if any, * otherwise return `null` along with `values`. * @param {Object} valuesToCheck Object of values to be checked. + * @param {Object} options Options for validator. + * @param {String} options.path Validation path. + * @param {Boolean} options.showNestedError If `true` shows nested errors. * @returns {{values: Object, errors: Object}} Object containing `values` and `errors` */ - validate(valuesToCheck) { + validate(valuesToCheck, options) { + let path = ''; + let showNestedError = this.__showNestedError; if (!valuesToCheck || typeof valuesToCheck !== 'object') { throw new TypeError('`valuesToCheck` should be an object.'); } + if (options !== undefined && typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (options) { + if (options.path !== undefined && typeof options.path !== 'string') { + throw new TypeError('`options.path` should be a string.'); + } + + path = options.path; + + if (typeof showNestedError !== 'boolean') { + if ( + options.showNestedError !== undefined && + typeof options.showNestedError !== 'boolean' + ) { + throw new TypeError('`options.showNestedError` should be a boolean.'); + } + showNestedError = options.showNestedError; + } + } + const allErrors = {}; const modifiedValues = {}; for (const key in this.__ruleSets) { @@ -88,7 +130,7 @@ export default class Validator { const { value, errors: currentErrors } = ruleSet.validate( valuesToCheck[key], key, - { returnEarly: this.__returnRuleSetEarly }, + { returnEarly: this.__returnRuleSetEarly, path, showNestedError }, ); modifiedValues[key] = value; if (currentErrors) { diff --git a/src/index.js b/src/index.js index 18aeb98..ceeea5c 100644 --- a/src/index.js +++ b/src/index.js @@ -16,4 +16,5 @@ export { toLowerCase, toUpperCase, isEmail, + isObject, } from './rules'; diff --git a/src/rules/index.js b/src/rules/index.js index e710bd9..a637906 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -13,3 +13,4 @@ export { default as isAlphaNum } from './isAlphaNum'; export { default as toLowerCase } from './toLowerCase'; export { default as toUpperCase } from './toUpperCase'; export { default as isEmail } from './isEmail'; +export { default as isObject } from './isObject'; diff --git a/src/rules/isAlpha.js b/src/rules/isAlpha.js index 1203619..2c26414 100644 --- a/src/rules/isAlpha.js +++ b/src/rules/isAlpha.js @@ -63,10 +63,22 @@ export default class isAlpha extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value === 'string') { const data = { name: label, diff --git a/src/rules/isAlphaNum.js b/src/rules/isAlphaNum.js index 1d1ab08..d95089b 100644 --- a/src/rules/isAlphaNum.js +++ b/src/rules/isAlphaNum.js @@ -63,10 +63,22 @@ export default class isAlphaNum extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value === 'string') { const data = { name: label, diff --git a/src/rules/isEmail.js b/src/rules/isEmail.js index 6df2d20..99aa36d 100644 --- a/src/rules/isEmail.js +++ b/src/rules/isEmail.js @@ -45,10 +45,22 @@ export default class isEmail extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value === 'string') { const data = { name: label, diff --git a/src/rules/isIn.js b/src/rules/isIn.js index b401c02..c9707d1 100644 --- a/src/rules/isIn.js +++ b/src/rules/isIn.js @@ -46,10 +46,22 @@ export default class isIn extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + const data = { name: label, in: this.in.join(', '), diff --git a/src/rules/isInt.js b/src/rules/isInt.js index 3dbf467..f2d921c 100644 --- a/src/rules/isInt.js +++ b/src/rules/isInt.js @@ -63,10 +63,22 @@ export default class isInt extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + const data = { name: label, min: this.min, diff --git a/src/rules/isLen.js b/src/rules/isLen.js index ef9cfd0..375d7eb 100644 --- a/src/rules/isLen.js +++ b/src/rules/isLen.js @@ -64,10 +64,22 @@ export default class isLen extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value === 'string') { const len = value.length; const data = { diff --git a/src/rules/isNumber.js b/src/rules/isNumber.js index 8c79aae..6f107fb 100644 --- a/src/rules/isNumber.js +++ b/src/rules/isNumber.js @@ -63,10 +63,22 @@ export default class isNumber extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + const data = { name: label, min: this.min, diff --git a/src/rules/isObject.js b/src/rules/isObject.js new file mode 100644 index 0000000..5bf109e --- /dev/null +++ b/src/rules/isObject.js @@ -0,0 +1,120 @@ +import Rule from './Rule'; +import Validator from '../Validator'; + +/** + * Checks if the value is an object and satisfies the given schema + */ +export default class isObject extends Rule { + /** + * @ignore + */ + message; + + /** + * @ignore + */ + schema; + + /** + * Checks if the value is an object and satisfies the given schema + * @param {Object} options Options for `isObject` + * @param {Validator} options.schema Schema for the object + * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + */ + constructor(options) { + super('isObject'); + + this.message = undefined; + this.schema = undefined; + + if (options !== undefined && typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (options !== undefined) { + if ( + options.message !== undefined && + typeof options.message !== 'string' + ) { + throw new Error('`message` key in `options` should be a string.'); + } + + this.message = options.message; + + if ( + options.schema !== undefined && + !(options.schema instanceof Validator) + ) { + throw new Error( + '`schema` key in `options` should be an instance of class `Validator`.', + ); + } + + this.schema = options.schema; + } + } + + /** + * Validate the `value` and return the error `string` if there are any + * otherwise return `null`. + * @param {any} value The value to be checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. + * @param {Boolean} options.showNestedError If `true` shows nested errors. + * @returns {{ value: any, error: String }} Value and error string. + */ + validate(value, options) { + let showNestedError = undefined; + + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + + if (typeof options.path !== 'string') { + throw new TypeError('`options.path` should be a string.'); + } + + const path = options.path; + + if(options.showNestedError !== undefined) { + if ( + typeof options.showNestedError !== 'boolean' + ) { + throw new TypeError('`options.showNestedError` should be a boolean.'); + } + + showNestedError = options.showNestedError; + } + + const data = { + name: label, + }; + + if (typeof value !== 'object') { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage("'%name%' should be an object.", data), + }; + } + + const { errors, values } = this.schema.validate(value, { path, showNestedError }); + + if (errors && Object.keys(errors).length > 0) { + return { + value: {...values}, + error: errors, + }; + } + + return { value: values, error: null }; + } +} diff --git a/src/rules/isRequired.js b/src/rules/isRequired.js index f3ed3fe..06f21d3 100644 --- a/src/rules/isRequired.js +++ b/src/rules/isRequired.js @@ -39,10 +39,22 @@ export default class isRequired extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if ( value === null || value === undefined || diff --git a/src/rules/isString.js b/src/rules/isString.js index 3a001d2..e489994 100644 --- a/src/rules/isString.js +++ b/src/rules/isString.js @@ -38,10 +38,22 @@ export default class isString extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value !== 'string') { const data = { name: label, diff --git a/src/rules/matchRegex.js b/src/rules/matchRegex.js index 1049623..ea88149 100644 --- a/src/rules/matchRegex.js +++ b/src/rules/matchRegex.js @@ -53,10 +53,22 @@ export default class matchRegex extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + if (typeof value === 'string') { const data = { name: label, diff --git a/src/rules/toInt.js b/src/rules/toInt.js index f279044..e47d13b 100644 --- a/src/rules/toInt.js +++ b/src/rules/toInt.js @@ -38,10 +38,22 @@ export default class toInt extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + const data = { name: label, }; diff --git a/src/rules/toLowerCase.js b/src/rules/toLowerCase.js index 22d7af3..e6efd63 100644 --- a/src/rules/toLowerCase.js +++ b/src/rules/toLowerCase.js @@ -15,9 +15,20 @@ export default class toLowerCase extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + let newVal = value; if (typeof newVal === 'string') { newVal = newVal.toLowerCase(); diff --git a/src/rules/toNumber.js b/src/rules/toNumber.js index 296d3fc..c959858 100644 --- a/src/rules/toNumber.js +++ b/src/rules/toNumber.js @@ -38,10 +38,22 @@ export default class toNumber extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. - * @param {String} label Name or Label of the value being checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + const data = { name: label, }; diff --git a/src/rules/toUpperCase.js b/src/rules/toUpperCase.js index 13d1627..ec2c5cb 100644 --- a/src/rules/toUpperCase.js +++ b/src/rules/toUpperCase.js @@ -15,9 +15,20 @@ export default class toUpperCase extends Rule { * Validate the `value` and return the error `string` if there are any * otherwise return `null`. * @param {any} value The value to be checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value) { + validate(value, options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + let newVal = value; if (typeof newVal === 'string') { newVal = newVal.toUpperCase(); diff --git a/test/15.isObject.js b/test/15.isObject.js new file mode 100644 index 0000000..0d00d16 --- /dev/null +++ b/test/15.isObject.js @@ -0,0 +1,103 @@ +const assert = require('assert'); +const util = require('util'); +const { + Validator, + RuleSet, + isString, + isObject, + toLowerCase, + toInt +} = require('../lib'); + +const addressSchema = new Validator({ + city: RuleSet.create([new toInt()]), +}); + +const userSchema = new Validator( + { + name: RuleSet.create([new isString(), new toLowerCase()]), + address: RuleSet.create([new isObject({ schema: addressSchema })]), + }, + { showNestedError: false }, +); + +const schema = new Validator({ + user1: RuleSet.create([new isObject({ schema: userSchema })]), +}); + +/** + * @test {isObject} + */ +describe('15. isObject', () => { + describe('With invalid values', () => { + let result; + before(() => { + const data = schema.validate({ + user1: { name: 'IRSHAD', address: { city: '100' } }, + }); + result = data.errors; + console.log(util.inspect(result, { depth: null })); + console.log(data.values); + }); + + it('Should return error', () => { + assert.equal(typeof result, 'object'); + assert.notEqual(result, null); + }); + + // it('Should return error if only user name', () => { + // const errorArray = result.email1; + // assert.equal(Array.isArray(errorArray), true); + // assert.equal(errorArray.length, 1); + // assert.equal(typeof errorArray[0], 'object'); + // assert.equal(errorArray[0].validator, 'isEmail'); + // assert.equal(errorArray[0].value, 'irshad'); + // }); + + // it('Should return error if only user name with @', () => { + // const errorArray = result.email2; + // assert.equal(Array.isArray(errorArray), true); + // assert.equal(errorArray.length, 1); + // assert.equal(typeof errorArray[0], 'object'); + // assert.equal(errorArray[0].validator, 'isEmail'); + // assert.equal(errorArray[0].value, 'irshad@'); + // }); + + // it('Should return error if top level domain is missing', () => { + // const errorArray = result.email3; + // assert.equal(Array.isArray(errorArray), true); + // assert.equal(errorArray.length, 1); + // assert.equal(typeof errorArray[0], 'object'); + // assert.equal(errorArray[0].validator, 'isEmail'); + // assert.equal(errorArray[0].value, 'irshad@gmail'); + // }); + + // it('Should return custom message on error', () => { + // const errorArray = result.email3; + // assert.equal(Array.isArray(errorArray), true); + // assert.equal(errorArray.length, 1); + // assert.equal(typeof errorArray[0], 'object'); + // assert.equal(errorArray[0].validator, 'isEmail'); + // assert.equal( + // errorArray[0].error, + // 'Email is not an email.', + // ); + // }); + }); + + // describe('With valid strings', () => { + // let result; + // before(() => { + // const data = schema.validate({ + // email1: 'irshad@gmail.com', + // email2: 'irshad@mail.gmail.com', + // email3: 'irshad@gmail.in', + // }); + // result = data.errors; + // }); + + // it('Should not return error', () => { + // assert.equal(result, null); + // }); + // }); +}); From b1eda6f6579474dee1fe3b3a3a6e0eb4c703eb63 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Wed, 6 May 2020 00:41:16 +0530 Subject: [PATCH 02/12] [Feat] Added direct `isObject` support to `RuleSet` --- src/RuleSet.js | 70 +++++++++++---- test/15.isObject.js | 201 ++++++++++++++++++++++++++------------------ 2 files changed, 176 insertions(+), 95 deletions(-) diff --git a/src/RuleSet.js b/src/RuleSet.js index 5cf9d2d..c85347e 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -1,4 +1,5 @@ -import { Rule } from './rules'; +import { Rule, isObject } from './rules'; +import Validator from './Validator'; /** * Vaidation error object. @@ -26,24 +27,50 @@ class RuleSet { /** * Create a ruleset for a particular `key` or `value`. - * @param {Array} arrayOfRules Array of `Rule` objects - * @param {String} label The name or label of the value being checked + * @param {Object} options Options for `RuleSet`. + * @param {Array} options.rules Array of `Rule` object (should not be set if the key is an obejct) + * @param {Validator} options.schema Validator object (only if the key is an object) + * @param {String} options.label The name or label of the value being checked + * @param {Object} options.schemaOptions Options for `isObject` */ - constructor(arrayOfRules, label) { - if (label && typeof label !== 'string') { - throw new TypeError('`label` should be a string.'); + constructor(options) { + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); } - if (!arrayOfRules || !Array.isArray(arrayOfRules)) { - throw new TypeError('`arrayOfRules` should be an array.'); + if (options.label && typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); } - if (arrayOfRules.length <= 0) { - throw new TypeError('`arrayOfRules` should not be empty.'); + const isSchema = options.rules === undefined; + + if (isSchema) { + if (!(options.schema instanceof Validator)) { + throw new TypeError( + '`options.schema` should be an instance of `Validator`.', + ); + } + + let objectOptions = {}; + if (options.schemaOptions) { + objectOptions = { ...options.schemaOptions }; + } + + objectOptions.schema = options.schema; + this.__rules = [new isObject(objectOptions)]; + } else { + if (!Array.isArray(options.rules)) { + throw new TypeError('`options.rules` should be an array of `Rule`.'); + } + + if (options.rules.length <= 0) { + throw new TypeError('`options.rules` should not be empty.'); + } + + this.__rules = [...options.rules]; } - this.__rules = [...arrayOfRules]; - this.__label = label; + this.__label = options.label; } /** @@ -134,12 +161,25 @@ class RuleSet { /** * Create a ruleset for a particular `key` or `value`. * Can be used as an alternative to the constructor. - * @param {Array} arrayOfRules Array of `Rule` objects + * @param {Array} rules Array of `Rule` object + * @param {String} label The name or label of the value being checked + * @returns {RuleSet} A new `RuleSet` object + */ + static create(rules, label) { + return new RuleSet({ rules, label }); + } + + /** + * Create a ruleset for a particular `key` or `value` if it is supposed to be an object. + * Can be used as an alternative to the constructor. + * @param {Validator} schema Array of `Rule` object * @param {String} label The name or label of the value being checked + * @param {Object} schemaOptions Options for `isObject` + * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) * @returns {RuleSet} A new `RuleSet` object */ - static create(arrayOfRules, label) { - return new RuleSet(arrayOfRules, label); + static schema(schema, label, schemaOptions) { + return new RuleSet({ schema, label, schemaOptions }); } } diff --git a/test/15.isObject.js b/test/15.isObject.js index 0d00d16..bd67e9a 100644 --- a/test/15.isObject.js +++ b/test/15.isObject.js @@ -1,28 +1,19 @@ const assert = require('assert'); -const util = require('util'); -const { - Validator, - RuleSet, - isString, - isObject, - toLowerCase, - toInt -} = require('../lib'); +const { Validator, RuleSet, isString, toLowerCase } = require('../lib'); const addressSchema = new Validator({ - city: RuleSet.create([new toInt()]), + city: RuleSet.create([new isString(), new toLowerCase()]), }); -const userSchema = new Validator( - { - name: RuleSet.create([new isString(), new toLowerCase()]), - address: RuleSet.create([new isObject({ schema: addressSchema })]), - }, - { showNestedError: false }, -); +const userSchema = new Validator({ + name: RuleSet.create([new isString(), new toLowerCase()]), + address: RuleSet.schema(addressSchema, 'Address', { + message: '%name% must be an object.', + }), +}); const schema = new Validator({ - user1: RuleSet.create([new isObject({ schema: userSchema })]), + user: RuleSet.schema(userSchema), }); /** @@ -30,74 +21,124 @@ const schema = new Validator({ */ describe('15. isObject', () => { describe('With invalid values', () => { - let result; + describe('Should return error if not an object.', () => { + let result; + before(() => { + const data = schema.validate({ + user: 'Irshad', + }); + result = data.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result, 'object'); + assert.notEqual(result, null); + }); + + it('Should return error of not an object', () => { + const errorArray = result.user; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isObject'); + assert.equal(errorArray[0].value, 'Irshad'); + assert.equal(errorArray[0].path, 'user'); + }); + }); + + describe('Should return error if key is not present.', () => { + let result; + before(() => { + const data = schema.validate({ + user: {}, + }); + result = data.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result, 'object'); + assert.notEqual(result, null); + }); + + it('Should return error if name is not present', () => { + const errorArray = result.user; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 2); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(typeof errorArray[1], 'object'); + + assert.equal(errorArray[0].validator, 'isString'); + assert.equal(errorArray[0].value, undefined); + assert.equal(errorArray[0].path, 'user.name'); + + assert.equal(errorArray[1].validator, 'isObject'); + assert.equal(errorArray[1].value, undefined); + assert.equal(errorArray[1].path, 'user.address'); + }); + }); + + describe('Should return error if object key is invalid.', () => { + let result; + before(() => { + const data = schema.validate({ + user: { name: 21, address: 1 }, + }); + result = data.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result, 'object'); + assert.notEqual(result, null); + }); + + it('Should return error if object key is invalid', () => { + const errorArray = result.user; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 2); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(typeof errorArray[1], 'object'); + + assert.equal(errorArray[0].validator, 'isString'); + assert.equal(errorArray[0].value, 21); + assert.equal(errorArray[0].path, 'user.name'); + + assert.equal(errorArray[1].validator, 'isObject'); + assert.equal(errorArray[1].value, 1); + assert.equal(errorArray[1].path, 'user.address'); + }); + + it('Should return custom message', () => { + const errorArray = result.user; + assert.equal(errorArray[1].error, 'Address must be an object.'); + }); + }); + }); + + describe('With valid values', () => { + let errors, values; before(() => { const data = schema.validate({ - user1: { name: 'IRSHAD', address: { city: '100' } }, + user: { name: 'IRSHAD', address: { city: 'Bangalore' } }, }); - result = data.errors; - console.log(util.inspect(result, { depth: null })); - console.log(data.values); + errors = data.errors; + values = data.values; }); - it('Should return error', () => { - assert.equal(typeof result, 'object'); - assert.notEqual(result, null); + it('Should not return error', () => { + assert.equal(typeof errors, 'object'); + assert.equal(errors, null); }); - // it('Should return error if only user name', () => { - // const errorArray = result.email1; - // assert.equal(Array.isArray(errorArray), true); - // assert.equal(errorArray.length, 1); - // assert.equal(typeof errorArray[0], 'object'); - // assert.equal(errorArray[0].validator, 'isEmail'); - // assert.equal(errorArray[0].value, 'irshad'); - // }); - - // it('Should return error if only user name with @', () => { - // const errorArray = result.email2; - // assert.equal(Array.isArray(errorArray), true); - // assert.equal(errorArray.length, 1); - // assert.equal(typeof errorArray[0], 'object'); - // assert.equal(errorArray[0].validator, 'isEmail'); - // assert.equal(errorArray[0].value, 'irshad@'); - // }); - - // it('Should return error if top level domain is missing', () => { - // const errorArray = result.email3; - // assert.equal(Array.isArray(errorArray), true); - // assert.equal(errorArray.length, 1); - // assert.equal(typeof errorArray[0], 'object'); - // assert.equal(errorArray[0].validator, 'isEmail'); - // assert.equal(errorArray[0].value, 'irshad@gmail'); - // }); - - // it('Should return custom message on error', () => { - // const errorArray = result.email3; - // assert.equal(Array.isArray(errorArray), true); - // assert.equal(errorArray.length, 1); - // assert.equal(typeof errorArray[0], 'object'); - // assert.equal(errorArray[0].validator, 'isEmail'); - // assert.equal( - // errorArray[0].error, - // 'Email is not an email.', - // ); - // }); - }); + it('Should transform nested keys', () => { + assert.equal(typeof values, 'object'); + let value = values.user.name; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad'); - // describe('With valid strings', () => { - // let result; - // before(() => { - // const data = schema.validate({ - // email1: 'irshad@gmail.com', - // email2: 'irshad@mail.gmail.com', - // email3: 'irshad@gmail.in', - // }); - // result = data.errors; - // }); - - // it('Should not return error', () => { - // assert.equal(result, null); - // }); - // }); + value = values.user.address.city; + assert.equal(typeof values, 'object'); + assert.equal(typeof value, 'string'); + assert.equal(value, 'bangalore'); + }); + }); }); From b8447995d998185cbba4cf8c175d1d93231dd1b0 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Thu, 7 May 2020 16:44:55 +0530 Subject: [PATCH 03/12] [Feat] Added `path` assert to all test --- test/00.Base.js | 6 ++++++ test/01.isString.js | 5 +++++ test/02.isRequired.js | 4 ++++ test/03.isLen.js | 7 +++++++ test/04.isIn.js | 4 ++++ test/05.isNumber.js | 12 +++++++++++- test/06.toNumber.js | 4 ++++ test/07.isInt.js | 8 ++++++++ test/08.toInt.js | 4 ++++ test/09.matchRegex.js | 2 ++ test/10.isAlpha.js | 4 ++++ test/11.isAlphaNum.js | 3 +++ test/14.isEmail.js | 9 +++++---- 13 files changed, 67 insertions(+), 5 deletions(-) diff --git a/test/00.Base.js b/test/00.Base.js index 82ece1d..3d34f37 100644 --- a/test/00.Base.js +++ b/test/00.Base.js @@ -35,6 +35,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should not return error on undefined', () => { @@ -73,6 +74,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error on undefined', () => { @@ -82,6 +84,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, undefined); + assert.equal(errorArray[0].path, 'username'); }); }); @@ -115,6 +118,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should not return error on undefined', () => { @@ -153,6 +157,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error on undefined', () => { @@ -162,6 +167,7 @@ describe('00. Base for all Rules', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, undefined); + assert.equal(errorArray[0].path, 'username'); }); }); }); diff --git a/test/01.isString.js b/test/01.isString.js index 29ea01a..e26857f 100644 --- a/test/01.isString.js +++ b/test/01.isString.js @@ -41,6 +41,7 @@ describe('01. isString', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error on undefined', () => { @@ -50,6 +51,7 @@ describe('01. isString', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, undefined); + assert.equal(errorArray[0].path, 'username'); }); it('Should return error on object', () => { @@ -59,6 +61,7 @@ describe('01. isString', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.deepEqual(errorArray[0].value, {}); + assert.equal(errorArray[0].path, 'email'); }); it('Should return error on array', () => { @@ -68,6 +71,7 @@ describe('01. isString', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isString'); assert.deepEqual(errorArray[0].value, []); + assert.equal(errorArray[0].path, 'password'); }); it('Should return custom message on error', () => { @@ -78,6 +82,7 @@ describe('01. isString', () => { assert.equal(errorArray[0].validator, 'isString'); assert.equal(errorArray[0].value, null); assert.equal(errorArray[0].error, 'Confirm password should be a string.'); + assert.equal(errorArray[0].path, 'confirmPassword'); }); }); diff --git a/test/02.isRequired.js b/test/02.isRequired.js index 4649726..da93fa4 100644 --- a/test/02.isRequired.js +++ b/test/02.isRequired.js @@ -38,6 +38,7 @@ describe('02. isRequired', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isRequired'); assert.equal(errorArray[0].value, null); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error on undefined', () => { @@ -47,6 +48,7 @@ describe('02. isRequired', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isRequired'); assert.equal(errorArray[0].value, undefined); + assert.equal(errorArray[0].path, 'email'); }); it('Should return error on empty string', () => { @@ -56,6 +58,7 @@ describe('02. isRequired', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isRequired'); assert.equal(errorArray[0].value, ''); + assert.equal(errorArray[0].path, 'password'); }); it('Should return custom message on error', () => { @@ -66,6 +69,7 @@ describe('02. isRequired', () => { assert.equal(errorArray[0].validator, 'isRequired'); assert.equal(errorArray[0].value, null); assert.equal(errorArray[0].error, 'Confirm password is to be present.'); + assert.equal(errorArray[0].path, 'confirmPassword'); }); }); diff --git a/test/03.isLen.js b/test/03.isLen.js index b1d7f73..3f02429 100644 --- a/test/03.isLen.js +++ b/test/03.isLen.js @@ -45,6 +45,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, 'irsh'); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error if greater than `eq`', () => { @@ -54,6 +55,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, '20181'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return error if less than `min`', () => { @@ -63,6 +65,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, 'ir'); + assert.equal(errorArray[0].path, 'username'); }); it('Should return error if greater than `max`', () => { @@ -72,6 +75,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, 'irshad@gmail.com'); + assert.equal(errorArray[0].path, 'email'); }); it('Should return error if less than `min` when both `min` & `max` are present', () => { @@ -81,6 +85,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, '1234567'); + assert.equal(errorArray[0].path, 'password'); }); it('Should return error if greater than `max` when both `min` & `max` are present', () => { @@ -90,6 +95,7 @@ describe('03. isLen', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, '12345678910'); + assert.equal(errorArray[0].path, 'confirmPassword'); }); it('Should return custom message on error', () => { @@ -100,6 +106,7 @@ describe('03. isLen', () => { assert.equal(errorArray[0].validator, 'isLen'); assert.equal(errorArray[0].value, 'MALE'); assert.equal(errorArray[0].error, 'Gender should only have 1 charecter.'); + assert.equal(errorArray[0].path, 'gender'); }); }); diff --git a/test/04.isIn.js b/test/04.isIn.js index 895df7f..965b919 100644 --- a/test/04.isIn.js +++ b/test/04.isIn.js @@ -39,6 +39,7 @@ describe('04. isIn', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isIn'); assert.equal(errorArray[0].value, 'irsh'); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error if not in given numbers', () => { @@ -48,6 +49,7 @@ describe('04. isIn', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isIn'); assert.equal(errorArray[0].value, '20181'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return error if not in given single string', () => { @@ -57,6 +59,7 @@ describe('04. isIn', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isIn'); assert.equal(errorArray[0].value, 'ir'); + assert.equal(errorArray[0].path, 'username'); }); it('Should return custom message on error', () => { @@ -67,6 +70,7 @@ describe('04. isIn', () => { assert.equal(errorArray[0].validator, 'isIn'); assert.equal(errorArray[0].value, 'MALE'); assert.equal(errorArray[0].error, 'Gender can only be M, F.'); + assert.equal(errorArray[0].path, 'gender'); }); }); diff --git a/test/05.isNumber.js b/test/05.isNumber.js index 0a47f51..a5bc405 100644 --- a/test/05.isNumber.js +++ b/test/05.isNumber.js @@ -22,7 +22,9 @@ const schema = new Validator({ }); /** - * @test {isNumber} + * @test {isNumber + assert.equal(errorArray[0].path, 'name'); + *} */ describe('05. isNumber', () => { describe('With error values', () => { @@ -53,6 +55,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '201a4'); + assert.equal(errorArray[0].path, 'id'); }); it('Should return error if a symbol is present', () => { @@ -62,6 +65,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '~10'); + assert.equal(errorArray[0].path, 'age'); }); it('Should return error if character after `.`', () => { @@ -71,6 +75,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '20181.01a'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return error if less than `min`', () => { @@ -80,6 +85,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '5'); + assert.equal(errorArray[0].path, 'monthOfBirth'); }); it('Should return error if greater than `max`', () => { @@ -89,6 +95,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '55'); + assert.equal(errorArray[0].path, 'dateOfBirth'); }); it('Should return error if less than `min` when both `min` & `max` are present', () => { @@ -98,6 +105,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '-10'); + assert.equal(errorArray[0].path, 'score'); }); it('Should return error if greater than `max` when both `min` & `max` are present', () => { @@ -107,6 +115,7 @@ describe('05. isNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '150'); + assert.equal(errorArray[0].path, 'avgScore'); }); it('Should return custom message on error', () => { @@ -117,6 +126,7 @@ describe('05. isNumber', () => { assert.equal(errorArray[0].validator, 'isNumber'); assert.equal(errorArray[0].value, '187'); assert.equal(errorArray[0].error, 'Max Score should be in the range of 0 to 100.77'); + assert.equal(errorArray[0].path, 'maxScore'); }); }); diff --git a/test/06.toNumber.js b/test/06.toNumber.js index bfdff77..2c68577 100644 --- a/test/06.toNumber.js +++ b/test/06.toNumber.js @@ -43,6 +43,7 @@ describe('06. toNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toNumber'); assert.equal(errorArray[0].value, '201a4'); + assert.equal(errorArray[0].path, 'id'); }); it('Should return error if a symbol is present', () => { @@ -52,6 +53,7 @@ describe('06. toNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toNumber'); assert.equal(errorArray[0].value, '~10'); + assert.equal(errorArray[0].path, 'age'); }); it('Should return error if character after `.`', () => { @@ -61,6 +63,7 @@ describe('06. toNumber', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toNumber'); assert.equal(errorArray[0].value, '20181.01a'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return custom message on error', () => { @@ -71,6 +74,7 @@ describe('06. toNumber', () => { assert.equal(errorArray[0].validator, 'toNumber'); assert.equal(errorArray[0].value, 'Jan'); assert.equal(errorArray[0].error, 'Month of birth should be a number.'); + assert.equal(errorArray[0].path, 'monthOfBirth'); }); }); diff --git a/test/07.isInt.js b/test/07.isInt.js index 7d2a100..4c9911b 100644 --- a/test/07.isInt.js +++ b/test/07.isInt.js @@ -53,6 +53,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '2014.05'); + assert.equal(errorArray[0].path, 'id'); }); it('Should return error if a symbol is present', () => { @@ -62,6 +63,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '~10'); + assert.equal(errorArray[0].path, 'age'); }); it('Should return error if character is present', () => { @@ -71,6 +73,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '20181.01a'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return error if less than `min`', () => { @@ -80,6 +83,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '5'); + assert.equal(errorArray[0].path, 'monthOfBirth'); }); it('Should return error if greater than `max`', () => { @@ -89,6 +93,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '55'); + assert.equal(errorArray[0].path, 'dateOfBirth'); }); it('Should return error if less than `min` when both `min` & `max` are present', () => { @@ -98,6 +103,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '-10'); + assert.equal(errorArray[0].path, 'score'); }); it('Should return error if greater than `max` when both `min` & `max` are present', () => { @@ -107,6 +113,7 @@ describe('07. isInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isInt'); assert.equal(errorArray[0].value, '150'); + assert.equal(errorArray[0].path, 'avgScore'); }); it('Should return custom message on error', () => { @@ -120,6 +127,7 @@ describe('07. isInt', () => { errorArray[0].error, 'Max Score should be in the range of 0 to 100', ); + assert.equal(errorArray[0].path, 'maxScore'); }); }); diff --git a/test/08.toInt.js b/test/08.toInt.js index 5d61e9e..8ff67a7 100644 --- a/test/08.toInt.js +++ b/test/08.toInt.js @@ -43,6 +43,7 @@ describe('08. toInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toInt'); assert.equal(errorArray[0].value, '2014.05'); + assert.equal(errorArray[0].path, 'id'); }); it('Should return error if a symbol is present', () => { @@ -52,6 +53,7 @@ describe('08. toInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toInt'); assert.equal(errorArray[0].value, '~10'); + assert.equal(errorArray[0].path, 'age'); }); it('Should return error if character after `.`', () => { @@ -61,6 +63,7 @@ describe('08. toInt', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'toInt'); assert.equal(errorArray[0].value, '20181.01a'); + assert.equal(errorArray[0].path, 'yearOfBirth'); }); it('Should return custom message on error', () => { @@ -71,6 +74,7 @@ describe('08. toInt', () => { assert.equal(errorArray[0].validator, 'toInt'); assert.equal(errorArray[0].value, 'Jan'); assert.equal(errorArray[0].error, 'Month of birth should be a number.'); + assert.equal(errorArray[0].path, 'monthOfBirth'); }); }); diff --git a/test/09.matchRegex.js b/test/09.matchRegex.js index 2706b67..48b30ec 100644 --- a/test/09.matchRegex.js +++ b/test/09.matchRegex.js @@ -40,6 +40,7 @@ describe('09. matchRegex', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'matchRegex'); assert.equal(errorArray[0].value, 'Asdk!#sa'); + assert.equal(errorArray[0].path, 'email'); }); it('Should return custom message on error', () => { @@ -53,6 +54,7 @@ describe('09. matchRegex', () => { errorArray[0].error, 'Password should only contain alphabets.', ); + assert.equal(errorArray[0].path, 'password'); }); }); diff --git a/test/10.isAlpha.js b/test/10.isAlpha.js index 8d46b67..684bc77 100644 --- a/test/10.isAlpha.js +++ b/test/10.isAlpha.js @@ -39,6 +39,7 @@ describe('10. isAlpha', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isAlpha'); assert.equal(errorArray[0].value, 'asd#!'); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error if spaces are present', () => { @@ -48,6 +49,7 @@ describe('10. isAlpha', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isAlpha'); assert.equal(errorArray[0].value, 'ahsdg jasdg j!hasgd'); + assert.equal(errorArray[0].path, 'email'); }); it('Should return error if numbers are present', () => { @@ -57,6 +59,7 @@ describe('10. isAlpha', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isAlpha'); assert.equal(errorArray[0].value, 'kasjdhkaq212jsdh'); + assert.equal(errorArray[0].path, 'password'); }); it('Should return custom message on error', () => { @@ -70,6 +73,7 @@ describe('10. isAlpha', () => { errorArray[0].error, 'Confirm password should only be alphabets.', ); + assert.equal(errorArray[0].path, 'confirmPassword'); }); }); diff --git a/test/11.isAlphaNum.js b/test/11.isAlphaNum.js index 815a99a..99cc126 100644 --- a/test/11.isAlphaNum.js +++ b/test/11.isAlphaNum.js @@ -37,6 +37,7 @@ describe('11. isAlphaNum', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isAlphaNum'); assert.equal(errorArray[0].value, 'as12d#!'); + assert.equal(errorArray[0].path, 'name'); }); it('Should return error if spaces are present', () => { @@ -46,6 +47,7 @@ describe('11. isAlphaNum', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isAlphaNum'); assert.equal(errorArray[0].value, 'ahsdg jasd2g jhasgd'); + assert.equal(errorArray[0].path, 'email'); }); it('Should return custom message on error', () => { @@ -59,6 +61,7 @@ describe('11. isAlphaNum', () => { errorArray[0].error, 'Password should only be a-z or 0-9.', ); + assert.equal(errorArray[0].path, 'password'); }); }); diff --git a/test/14.isEmail.js b/test/14.isEmail.js index c346a58..c5ee336 100644 --- a/test/14.isEmail.js +++ b/test/14.isEmail.js @@ -37,6 +37,7 @@ describe('14. isEmail', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isEmail'); assert.equal(errorArray[0].value, 'irshad'); + assert.equal(errorArray[0].path, 'email1'); }); it('Should return error if only user name with @', () => { @@ -46,6 +47,7 @@ describe('14. isEmail', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isEmail'); assert.equal(errorArray[0].value, 'irshad@'); + assert.equal(errorArray[0].path, 'email2'); }); it('Should return error if top level domain is missing', () => { @@ -55,6 +57,7 @@ describe('14. isEmail', () => { assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isEmail'); assert.equal(errorArray[0].value, 'irshad@gmail'); + assert.equal(errorArray[0].path, 'email3'); }); it('Should return custom message on error', () => { @@ -63,10 +66,8 @@ describe('14. isEmail', () => { assert.equal(errorArray.length, 1); assert.equal(typeof errorArray[0], 'object'); assert.equal(errorArray[0].validator, 'isEmail'); - assert.equal( - errorArray[0].error, - 'Email is not an email.', - ); + assert.equal(errorArray[0].error, 'Email is not an email.'); + assert.equal(errorArray[0].path, 'email3'); }); }); From f0e499924426ea56f87baebf4a61e75ff695c1c1 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Thu, 7 May 2020 21:27:40 +0530 Subject: [PATCH 04/12] [Feat] Changed static function name for RuleSet obejct --- src/RuleSet.js | 2 +- test/15.isObject.js | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/RuleSet.js b/src/RuleSet.js index c85347e..dfeaf48 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -178,7 +178,7 @@ class RuleSet { * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) * @returns {RuleSet} A new `RuleSet` object */ - static schema(schema, label, schemaOptions) { + static object(schema, label, schemaOptions) { return new RuleSet({ schema, label, schemaOptions }); } } diff --git a/test/15.isObject.js b/test/15.isObject.js index bd67e9a..c0d8f41 100644 --- a/test/15.isObject.js +++ b/test/15.isObject.js @@ -7,13 +7,13 @@ const addressSchema = new Validator({ const userSchema = new Validator({ name: RuleSet.create([new isString(), new toLowerCase()]), - address: RuleSet.schema(addressSchema, 'Address', { + address: RuleSet.object(addressSchema, 'Address', { message: '%name% must be an object.', }), }); const schema = new Validator({ - user: RuleSet.schema(userSchema), + user: RuleSet.object(userSchema), }); /** @@ -112,6 +112,31 @@ describe('15. isObject', () => { assert.equal(errorArray[1].error, 'Address must be an object.'); }); }); + + describe('Should return error if multi-nested object key is invalid.', () => { + let result; + before(() => { + const data = schema.validate({ + user: { name: 'Irshad', address: { city: 2 } }, + }); + result = data.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result, 'object'); + assert.notEqual(result, null); + }); + + it('Should return error if multi-nested object key is invalid', () => { + const errorArray = result.user; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isString'); + assert.equal(errorArray[0].value, 2); + assert.equal(errorArray[0].path, 'user.address.city'); + }); + }); }); describe('With valid values', () => { From deb0296bdff795871124567b788174eab2a50089 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Fri, 15 May 2020 05:24:07 +0530 Subject: [PATCH 05/12] [Refact] Improved documentation --- README.md | 203 +++++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- src/RuleSet.js | 3 +- 3 files changed, 199 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5cd9b08..94839df 100644 --- a/README.md +++ b/README.md @@ -3,37 +3,226 @@ [![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/) ## Overview -A tree shakable validator which for both `Nodejs` and `Browser`. +A light weight, powerful, tree shakable javascript schema validator which works on both `Nodejs` and `Browser`. ## Installation ``` npm install shark-validator ``` -## Example usage -Create a schema for validation and import only the required Rules. +## Importing +Remeber to import everything from the `shark-validator` directly for tree shaking to work. +#### ES6 +```js +import { Validator, RuleSet, isRequired, isString, isLen } from 'shark-validator'; +``` + +#### CommonJS ```js const { Validator, RuleSet, isRequired, isString, isLen } = require('shark-validator'); +``` + +## Quick start +There are 3 classes in `shark-validator` you need to know: +- `Validator` - The validation schema that will be used to validate a set of values. +- `RuleSet` - A set of rules for a particular key. +- `Rule` - There are a lot of predefined Rules that you can use or create your own Rule by extending the `Rule` class. + +Some predefined rules are `isString`, `isRequired` and `isLen`. You can lookup more predefined rules here [https://shark.imirshad.com/](https://shark.imirshad.com/). + + +### Example usage +Here we'll create a validator schema to validate for `name`, `email` and `password`. We have to create a `RuleSet` object for each key in this schema. `RuleSet` is nothing but a set of rules. So for defining a `RuleSet` we can create it's static method `create` and pass in an array of Rules. + + +The rules we'll use in this example are: +- `isString`: It makes sure that the value is a string. +- `isRequired`: It makes sure that the value is present. +- `isEmail`: It makes sure that the value is an email. +- `isLen`: It makes sure that the value is of a particular length. + +```js +const { Validator, RuleSet, isRequired, isString, isLen, isEmail } = require('shark-validator'); const schema = new Validator({ name: RuleSet.create([new isString(), new isRequired()]), - email: RuleSet.create([new isString(), new isRequired()]), + email: RuleSet.create([new isString(), new isRequired(), new isEmail()]), password: RuleSet.create([new isString(), new isRequired(), new isLen({ min:8 })]), }); ``` -Validate values using the created schema +Now our validation schema is created which we can use to test any value. To test a particular object we can use the `validate` method of `schema` as shown below. ```js const valuesToCheck = { name: 'Dan', - email: 'dan@daninc.com', + email: 'dandaninc.com', password: '123456' }; const { values, errors } = schema.validate(valuesToCheck); ``` -## Reference and API Documentation +The `validate` methods returns an object with `values` and `errros` (if any otherwise `null`). +The value of the variable `values` and `errors` will be. + +**Values** +```json +{ + "name": "Dan", + "email": "dan@daninc.com", + "password": "123456" +} +``` + +**Errors** +```json +{ + "email": + [ + { "error": "\'email\' should be a valid email.", + "validator": "isEmail", + "value": "dandaninc.com", + "path": "email" + } + ], + + "password": + [ + { "error": "\'password\' should not be less than 8 characters.", + "validator": "isLen", + "value": "123456", + "path": "password" + } + ] +} +``` + + +The `errors` object contains all the data about which rule failed and where did it fail. + +As in this example we can see that the `email` key failed to validate on `isEmail` rule and the password field failed to validate on `isLen` rule. + +The `errors` object contains the following data: +- `error`: Error message. +- `validator`: Name of the `Rule` where the error occured. +- `value`: The value on which the rule failed. +- `path`: Path to the value. + + +### More fetures +------ + +#### Custom label +You can provide a custom name to a particular key which can be displayed on the error message if the test for that key fails. + +##### Example +If in the above example we defined the `RuleSet` for the `email` key as: + +```js +RuleSet.create([new isString(), new isRequired(), new isEmail()], 'Business Email') +``` + +Then the returned error message will use the name `Business Email`. + +```json +{ + "email": + [ + { "error": "\'Business Email\' should be a valid email.", + "validator": "isEmail", + "value": "dandaninc.com", + "path": "email" + } + ], + + ... +} +``` + +#### Return early +If you want to stop the check if one error is found, then you can pass in an additional parameters `returnEarly` and `returnRuleSetEarly` to the `Validator` constructor. + +If set to true, then the functionality of the two parameters will be: +- `returnEarly`: Stops validating when other keys when one or more errors are found for a particular key. +- `returnRuleSetEarly`: Stops further validation of a particular key if an error is found on the same key. + +You can use a combination of both the parameters to acheive different funtionalities. + +##### Example +If the validator is defined as below. + +```js +const schema = new Validator({ + name: RuleSet.create([new isString(), new isRequired()]), + email: RuleSet.create([new isString(), new isRequired(), new isEmail()]), + password: RuleSet.create([new isString(), new isRequired(), new isLen({ min:8 })]), +}, +{ + returnEarly: true +}); +``` + +Then the errors object will be: + +```json +{ + "email": + [ + { "error": "\'Business Email\' should be a valid email.", + "validator": "isEmail", + "value": "dandaninc.com", + "path": "email" + } + ], +} +``` + +Notice that no `password` error is returned because the validation stopped when the email failed the test. + +#### Custom error message +If you don't like the existing error messages, you can provide custom error messages if a particular rule fails just by adding a parameter `message` to the `Rule` constructor as: + +```js +RuleSet.create([ new SomeRule({ message: 'Please provide a valid input.' }) ]) +``` + +Additionally you can use some variable inside the message string like `name` of the field. To use a variable you just have to enclose the name of the variable between `%`, like this. + +```js +RuleSet.create([ new SomeRule({ message: 'Please provide a valid input for %name%.' }) ]) +``` + +Other then the `name` valriable, you can use any other variable that you provide in the `Rule` constructor along with the message. + +##### Example +If in the above example we defined the `RuleSet` for the `password` key as: + +```js +RuleSet.create([ + new isString(), + new isRequired(), + new isLen({ min:8, message: '%name% must be equal to or greater than %min% charecters.' }) +]) +``` + +Then the returned error will use our custom message. + +```json +{ + "password": + [ + { "error": "password must be equal to or greater than 8 charecters.", + "validator": "isLen", + "value": "123456", + "path": "password" + } + ], + + ... +} +``` + +## Reference and API Documentation for more features [https://shark.imirshad.com/](https://shark.imirshad.com/) \ No newline at end of file diff --git a/package.json b/package.json index 285dff1..41f10e6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "shark-validator", "version": "1.3.1", - "description": "A simple schema validator", + "description": "A light weight, powerful, tree shakable javascript schema validator which works on both Nodejs and Browser.", "main": "lib/index.js", "module": "es/index.js", "scripts": { diff --git a/src/RuleSet.js b/src/RuleSet.js index dfeaf48..311a9fd 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -6,6 +6,7 @@ import Validator from './Validator'; * @typedef {Object} validationError * @property {String} error - Error string * @property {String} validator - Name of validator (`Rule`) where the error occured + * @property {String} path - Path to the error value * @property {Any} value - Value which caused the `Rule` to fail */ @@ -172,7 +173,7 @@ class RuleSet { /** * Create a ruleset for a particular `key` or `value` if it is supposed to be an object. * Can be used as an alternative to the constructor. - * @param {Validator} schema Array of `Rule` object + * @param {Validator} schema A `Validator` object to be checked against the object * @param {String} label The name or label of the value being checked * @param {Object} schemaOptions Options for `isObject` * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) From 7582fb0cf46738b978ca27b0539fdf1dc4165be8 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Mon, 18 May 2020 17:11:01 +0530 Subject: [PATCH 06/12] [Feat] Added `isArray` Rule; Improved returnEarly with isObject --- src/RuleSet.js | 101 ++++++++++----- src/Validator.js | 22 ++++ src/index.js | 1 + src/rules/index.js | 1 + src/rules/isArray.js | 219 ++++++++++++++++++++++++++++++++ src/rules/isObject.js | 15 ++- test/16.isArray.js | 285 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 609 insertions(+), 35 deletions(-) create mode 100644 src/rules/isArray.js create mode 100644 test/16.isArray.js diff --git a/src/RuleSet.js b/src/RuleSet.js index 311a9fd..60610a7 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -1,5 +1,6 @@ import { Rule, isObject } from './rules'; import Validator from './Validator'; +import isArray from '../lib/rules/isArray'; /** * Vaidation error object. @@ -30,9 +31,7 @@ class RuleSet { * Create a ruleset for a particular `key` or `value`. * @param {Object} options Options for `RuleSet`. * @param {Array} options.rules Array of `Rule` object (should not be set if the key is an obejct) - * @param {Validator} options.schema Validator object (only if the key is an object) * @param {String} options.label The name or label of the value being checked - * @param {Object} options.schemaOptions Options for `isObject` */ constructor(options) { if (typeof options !== 'object') { @@ -43,34 +42,16 @@ class RuleSet { throw new TypeError('`options.label` should be a string.'); } - const isSchema = options.rules === undefined; - - if (isSchema) { - if (!(options.schema instanceof Validator)) { - throw new TypeError( - '`options.schema` should be an instance of `Validator`.', - ); - } - - let objectOptions = {}; - if (options.schemaOptions) { - objectOptions = { ...options.schemaOptions }; - } - - objectOptions.schema = options.schema; - this.__rules = [new isObject(objectOptions)]; - } else { - if (!Array.isArray(options.rules)) { - throw new TypeError('`options.rules` should be an array of `Rule`.'); - } - - if (options.rules.length <= 0) { - throw new TypeError('`options.rules` should not be empty.'); - } + if (!Array.isArray(options.rules)) { + throw new TypeError('`options.rules` should be an array of `Rule`.'); + } - this.__rules = [...options.rules]; + if (options.rules.length <= 0) { + throw new TypeError('`options.rules` should not be empty.'); } + this.__rules = [...options.rules]; + this.__label = options.label; } @@ -83,6 +64,7 @@ class RuleSet { * @param {Object} options Options for validate * @param {Object} options.returnEarly If `true` returns the after getting the first error. * @param {String} options.path Validation path. + * @param {Boolean} options.isArrayElem `true` when validating array element. * @param {Boolean} options.showNestedError If `true` shows nested errors. * @returns {validationError[]} An object containing `value` and `errors` if any */ @@ -128,11 +110,17 @@ class RuleSet { throw new TypeError('Rule should be an instance of `Rule` class.'); } - let currentPath = path ? `${path}.${key}` : key; + let currentPath = options.isArrayElem + ? path + : path + ? `${path}.${key}` + : key; const { value, error } = rule.validate(modifiedValue, { label: this.__label || key, + key: key, path: currentPath, showNestedError, + returnEarly, }); modifiedValue = value; @@ -180,7 +168,62 @@ class RuleSet { * @returns {RuleSet} A new `RuleSet` object */ static object(schema, label, schemaOptions) { - return new RuleSet({ schema, label, schemaOptions }); + let objectOptions = {}; + if (schemaOptions) { + objectOptions = { ...schemaOptions }; + } + + objectOptions.schema = schema; + const rules = [new isObject(objectOptions)]; + + return new RuleSet({ rules, label }); + } + + /** + * Create a ruleset for a particular `key` or `value` if it is supposed to be an object. + * Can be used as an alternative to the constructor. + * @param {Array} rules Array of rules + * @param {String} label The name or label of the value being checked + * @param {Object} schemaOptions Options for `isArray` + * @param {Number} schemaOptions.eq Length should be equal to `eq` + * @param {Number} schemaOptions.min Length should be min `min` + * @param {Number} schemaOptions.max Length should be max to `max` + * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @returns {RuleSet} A new `RuleSet` object + */ + static array(rules, label, schemaOptions) { + let objectOptions = {}; + if (schemaOptions) { + objectOptions = { ...schemaOptions }; + } + + objectOptions.rules = RuleSet.create(rules); + const arrayOfRules = [new isArray(objectOptions)]; + + return new RuleSet({ rules: arrayOfRules, label }); + } + + /** + * Checks if value is an array and each value satisfies the given rules + * @param {Validator} schema A `Validator` object to be checked against the object + * @param {String} label The name or label of the value being checked + * @param {Object} schemaOptions Options for `isArray` + * @param {Number} schemaOptions.eq Length should be equal to `eq` + * @param {Number} schemaOptions.min Length should be min `min` + * @param {Number} schemaOptions.max Length should be max to `max` + * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @returns {RuleSet} A new `RuleSet` object + */ + static arrayOfObject(schema, label, schemaOptions) { + let objectOptions = {}; + if (schemaOptions) { + objectOptions = { ...schemaOptions }; + } + + objectOptions.rules = RuleSet.object(schema); + const arrayOfRules = [new isArray(objectOptions)]; + + return new RuleSet({ rules: arrayOfRules, label }); } } diff --git a/src/Validator.js b/src/Validator.js index 9bac19d..965a75c 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -87,6 +87,8 @@ export default class Validator { * @param {Object} options Options for validator. * @param {String} options.path Validation path. * @param {Boolean} options.showNestedError If `true` shows nested errors. + * @param {Boolean} options.returnEarly If `true` returns whenever first `key` in `values` fails the test. + * @param {Boolean} options.returnRuleSetEarly If `true` returns the after getting the first error on all `keys`. * @returns {{values: Object, errors: Object}} Object containing `values` and `errors` */ validate(valuesToCheck, options) { @@ -116,6 +118,26 @@ export default class Validator { } showNestedError = options.showNestedError; } + + if (typeof this.__returnEarly !== 'boolean') { + if ( + options.returnEarly !== undefined && + typeof options.returnEarly !== 'boolean' + ) { + throw new TypeError('`options.returnEarly` should be a boolean.'); + } + this.__returnEarly = options.returnEarly; + } + + if (typeof this.__returnRuleSetEarly !== 'boolean') { + if ( + options.returnRuleSetEarly !== undefined && + typeof options.returnRuleSetEarly !== 'boolean' + ) { + throw new TypeError('`options.returnRuleSetEarly` should be a boolean.'); + } + this.__returnRuleSetEarly = options.returnRuleSetEarly; + } } const allErrors = {}; diff --git a/src/index.js b/src/index.js index ceeea5c..414c048 100644 --- a/src/index.js +++ b/src/index.js @@ -17,4 +17,5 @@ export { toUpperCase, isEmail, isObject, + isArray, } from './rules'; diff --git a/src/rules/index.js b/src/rules/index.js index a637906..cf0f4cb 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -14,3 +14,4 @@ export { default as toLowerCase } from './toLowerCase'; export { default as toUpperCase } from './toUpperCase'; export { default as isEmail } from './isEmail'; export { default as isObject } from './isObject'; +export { default as isArray } from './isArray'; diff --git a/src/rules/isArray.js b/src/rules/isArray.js new file mode 100644 index 0000000..b66aba3 --- /dev/null +++ b/src/rules/isArray.js @@ -0,0 +1,219 @@ +import Rule from './Rule'; +import RuleSet from '../RuleSet'; + +/** + * Checks if value is an array and each value satisfies the given rules + */ +export default class isArray extends Rule { + /** + * @ignore + */ + message; + + /** + * @ignore + */ + rules; + + /** + * @ignore + */ + max; + + /** + * @ignore + */ + min; + + /** + * @ignore + */ + eq; + + /** + * Checks if value is an array and each value satisfies the given rules + * @param {Object} options Options for `isArray` + * @param {RuleSet} options.rules RuleSet for validating array value + * @param {Number} options.eq Length should be equal to `eq` + * @param {Number} options.min Length should be min `min` + * @param {Number} options.max Length should be max to `max` + * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + */ + constructor(options) { + super('isArray'); + + this.message = undefined; + this.rules = undefined; + + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (options.message !== undefined && typeof options.message !== 'string') { + throw new Error('`options.message` key in `options` should be a string.'); + } + + this.message = options.message; + + if (options.rules !== undefined && !(options.rules instanceof RuleSet)) { + throw new Error( + '`options.rules` key in `options` should be an instance of class `RuleSet`.', + ); + } + + this.rules = options.rules; + + const keys = ['min', 'max', 'eq']; + for (const key of keys) { + if (options[key] !== undefined && typeof options[key] !== 'number') { + throw new TypeError(`\`${key}\` key in options should be an integer.`); + } + } + + this.min = options.min; + this.max = options.max; + this.eq = options.eq; + } + + /** + * Validate the `value` and return the error `string` if there are any + * otherwise return `null`. + * @param {any} value The value to be checked. + * @param {Object} options Options for validate. + * @param {String} options.label Name or Label of the value being checked. + * @param {String} options.key Key of the value being checked + * @param {String} options.path Validator path. + * @param {Boolean} options.showNestedError If `true` shows nested errors. + * @param {Boolean} options.returnEarly If `true` returns the after getting the first error. + * @returns {{ value: any, error: String }} Value and error string. + */ + validate(value, options) { + let showNestedError = undefined; + + if (typeof options !== 'object') { + throw new TypeError('`options` should be an object.'); + } + + if (typeof options.label !== 'string') { + throw new TypeError('`options.label` should be a string.'); + } + + const label = options.label; + + if (typeof options.path !== 'string') { + throw new TypeError('`options.path` should be a string.'); + } + + const path = options.path; + + if (options.showNestedError !== undefined) { + if (typeof options.showNestedError !== 'boolean') { + throw new TypeError('`options.showNestedError` should be a boolean.'); + } + + showNestedError = options.showNestedError; + } + + const data = { + name: label, + max: this.max, + min: this.min, + eq: this.eq, + }; + + if (!Array.isArray(value)) { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage("'%name%' should be an array.", data), + }; + } + + const len = value.length; + + if (this.eq !== undefined && len !== this.eq) { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage("'%name%' should contain %eq% elements.", data), + }; + } + + if ( + this.min !== undefined && + this.max !== undefined && + (len > this.max || len < this.min) + ) { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage( + "'%name%' should have %min% - %max% elements.", + data, + ), + }; + } + + if (this.min !== undefined && len < this.min) { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage( + "'%name%' should have greater than %min% elements.", + data, + ), + }; + } + + if (this.max !== undefined && len > this.max) { + return { + value, + error: this.message + ? this.formatMessage(this.message, data) + : this.formatMessage( + "'%name%' should have less than %max% elements.", + data, + ), + }; + } + + let allErrors = {}; + const allValues = []; + + for (let i = 0; i < len; i++) { + const { errors, value: localValue } = this.rules.validate( + value[i], + options.key, + { + path: path + `[${i}]`, + showNestedError, + isArrayElem: true, + returnEarly: options.returnEarly, + }, + ); + if (errors && Object.keys(errors).length > 0) { + allErrors[i] = errors; + if (options.returnEarly) { + return { + value: allValues.concat(value.slice(i + 1)), + error: allErrors, + }; + } + } + allValues.push(localValue); + } + + if (allErrors && Object.keys(allErrors).length > 0) { + return { + value: allValues, + error: allErrors, + }; + } + + return { value: allValues, error: null }; + } +} diff --git a/src/rules/isObject.js b/src/rules/isObject.js index 5bf109e..c66bc30 100644 --- a/src/rules/isObject.js +++ b/src/rules/isObject.js @@ -62,6 +62,7 @@ export default class isObject extends Rule { * @param {String} options.label Name or Label of the value being checked. * @param {String} options.path Validator path. * @param {Boolean} options.showNestedError If `true` shows nested errors. + * @param {Boolean} options.returnEarly If `true` returns the after getting the first error. * @returns {{ value: any, error: String }} Value and error string. */ validate(value, options) { @@ -83,10 +84,8 @@ export default class isObject extends Rule { const path = options.path; - if(options.showNestedError !== undefined) { - if ( - typeof options.showNestedError !== 'boolean' - ) { + if (options.showNestedError !== undefined) { + if (typeof options.showNestedError !== 'boolean') { throw new TypeError('`options.showNestedError` should be a boolean.'); } @@ -106,11 +105,15 @@ export default class isObject extends Rule { }; } - const { errors, values } = this.schema.validate(value, { path, showNestedError }); + const { errors, values } = this.schema.validate(value, { + path, + showNestedError, + returnRuleSetEarly: options.returnEarly, + }); if (errors && Object.keys(errors).length > 0) { return { - value: {...values}, + value: values, error: errors, }; } diff --git a/test/16.isArray.js b/test/16.isArray.js new file mode 100644 index 0000000..0a34c94 --- /dev/null +++ b/test/16.isArray.js @@ -0,0 +1,285 @@ +const assert = require('assert'); +const { + Validator, + RuleSet, + isString, + toLowerCase, + isRequired, + isEmail, +} = require('../lib'); + +const userSchema = new Validator( + { + name: RuleSet.create([new isRequired(), new isString(), new toLowerCase()]), + email: RuleSet.create([ + new isRequired(), + new isString(), + new isEmail(), + new toLowerCase(), + ]), + }, + { returnRuleSetEarly: true }, +); + +const schema = new Validator({ + users: RuleSet.arrayOfObject(userSchema, null, { min: 1, max: 3 }), +}); + +const plainArraySchema = new Validator({ + users: RuleSet.array( + [new isRequired(), new isString(), new toLowerCase()], + null, + { + min: 1, + max: 3, + }, + ), +}); + +/** + * @test {isArray} + */ +describe('16. isArray', () => { + describe('With invalid values', () => { + describe('Should return error if not an array.', () => { + let result1, result2; + before(() => { + const data1 = schema.validate({ + users: 'Irshad', + }); + result1 = data1.errors; + + const data2 = plainArraySchema.validate({ + users: 'Irshad', + }); + result2 = data2.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result1, 'object'); + assert.notEqual(result1, null); + + assert.equal(typeof result2, 'object'); + assert.notEqual(result2, null); + }); + + it('Should return error if not an array', () => { + let errorArray = result1.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.equal(errorArray[0].value, 'Irshad'); + assert.equal(errorArray[0].path, 'users'); + + errorArray = result2.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.equal(errorArray[0].value, 'Irshad'); + assert.equal(errorArray[0].path, 'users'); + }); + }); + + describe('Should return error if less than min elements.', () => { + let result1, result2; + before(() => { + const data1 = schema.validate({ + users: [], + }); + result1 = data1.errors; + + const data2 = plainArraySchema.validate({ + users: [], + }); + result2 = data2.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result1, 'object'); + assert.notEqual(result1, null); + + assert.equal(typeof result2, 'object'); + assert.notEqual(result2, null); + }); + + it('Should return error if less than min elements', () => { + let errorArray = result1.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.deepStrictEqual(errorArray[0].value, []); + assert.equal(errorArray[0].path, 'users'); + + errorArray = result2.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.deepStrictEqual(errorArray[0].value, []); + assert.equal(errorArray[0].path, 'users'); + }); + }); + + describe('Should return error if greater than max elements.', () => { + let result1, result2; + before(() => { + const data1 = schema.validate({ + users: [{}, {}, {}, {}, {}], + }); + result1 = data1.errors; + + const data2 = plainArraySchema.validate({ + users: [{}, {}, {}, {}, {}], + }); + result2 = data2.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result1, 'object'); + assert.notEqual(result1, null); + + assert.equal(typeof result2, 'object'); + assert.notEqual(result2, null); + }); + + it('Should return error if greater than max elements', () => { + let errorArray = result1.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.deepStrictEqual(errorArray[0].value, [{}, {}, {}, {}, {}]); + assert.equal(errorArray[0].path, 'users'); + + errorArray = result2.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 1); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(errorArray[0].validator, 'isArray'); + assert.deepStrictEqual(errorArray[0].value, [{}, {}, {}, {}, {}]); + assert.equal(errorArray[0].path, 'users'); + }); + }); + + describe('Should return error if invalid element is present.', () => { + let result1, result2; + before(() => { + const data1 = schema.validate({ + users: [ + { name: 'Irshad', email: 'irshad@gmail.com' }, + { name: '', email: 'irshad@gmail.com' }, + { name: 'Irshad', email: 'gmail.com' }, + ], + }); + result1 = data1.errors; + + const data2 = plainArraySchema.validate({ + users: ['Irshad', '', []], + }); + result2 = data2.errors; + }); + + it('Should return error', () => { + assert.equal(typeof result1, 'object'); + assert.notEqual(result1, null); + + assert.equal(typeof result2, 'object'); + assert.notEqual(result2, null); + }); + + it('Should return error if invalid element is present', () => { + let errorArray = result1.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 2); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(typeof errorArray[1], 'object'); + + assert.equal(errorArray[0].validator, 'isRequired'); + assert.equal(errorArray[0].value, ''); + assert.equal(errorArray[0].path, 'users[1].name'); + + assert.equal(errorArray[1].validator, 'isEmail'); + assert.equal(errorArray[1].value, 'gmail.com'); + assert.equal(errorArray[1].path, 'users[2].email'); + + errorArray = result2.users; + assert.equal(Array.isArray(errorArray), true); + assert.equal(errorArray.length, 2); + assert.equal(typeof errorArray[0], 'object'); + assert.equal(typeof errorArray[1], 'object'); + + assert.equal(errorArray[0].validator, 'isRequired'); + assert.equal(errorArray[0].value, ''); + assert.equal(errorArray[0].path, 'users[1]'); + + assert.equal(errorArray[1].validator, 'isString'); + assert.deepEqual(errorArray[1].value, []); + assert.equal(errorArray[1].path, 'users[2]'); + }); + }); + }); + + describe('With valid values', () => { + let errors1, errors2, values1, values2; + before(() => { + const data1 = schema.validate({ + users: [ + { name: 'Irshad', email: 'irshad@gmail.com' }, + { name: 'IRSHAD', email: 'irshad@gmail.com' }, + { name: 'ANSari', email: 'IRShad@gmail.com' }, + ], + }); + errors1 = data1.errors; + values1 = data1.values; + + const data2 = plainArraySchema.validate({ + users: ['Irshad', 'IRshad', 'Ansari'], + }); + errors2 = data2.errors; + values2 = data2.values; + }); + + it('Should not return error', () => { + assert.equal(typeof errors1, 'object'); + assert.equal(errors1, null); + + assert.equal(typeof errors2, 'object'); + assert.equal(errors2, null); + }); + + it('Should transform nested keys', () => { + assert.equal(typeof values1, 'object'); + let value = values1.users[0].name; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad'); + + value = values1.users[1].name; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad'); + + value = values1.users[2].name; + assert.equal(typeof value, 'string'); + assert.equal(value, 'ansari'); + + value = values1.users[2].email; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad@gmail.com'); + + assert.equal(typeof values2, 'object'); + value = values2.users[0]; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad'); + + value = values2.users[1]; + assert.equal(typeof value, 'string'); + assert.equal(value, 'irshad'); + + value = values2.users[2]; + assert.equal(typeof value, 'string'); + assert.equal(value, 'ansari'); + }); + }); +}); From 304b2f6074d45b40578ba014b7c0396e9549acb4 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Tue, 19 May 2020 03:05:46 +0530 Subject: [PATCH 07/12] [Feat] Added eslint --- .eslintrc.json | 24 + package-lock.json | 1147 ++++++++++++++++++++++++++++++++++++-- package.json | 5 + src/RuleSet.js | 33 +- src/rules/Rule.js | 24 +- src/rules/index.js | 1 + src/rules/isAlpha.js | 22 +- src/rules/isAlphaNum.js | 22 +- src/rules/isArray.js | 40 +- src/rules/isEmail.js | 14 +- src/rules/isIn.js | 9 +- src/rules/isInt.js | 29 +- src/rules/isLen.js | 36 +- src/rules/isNumber.js | 29 +- src/rules/isObject.js | 17 +- src/rules/isRequired.js | 15 +- src/rules/isString.js | 9 +- src/rules/matchRegex.js | 18 +- src/rules/toInt.js | 12 +- src/rules/toLowerCase.js | 1 + src/rules/toNumber.js | 10 +- src/rules/toUpperCase.js | 1 + test/00.Base.js | 4 +- test/06.toNumber.js | 5 +- test/08.toInt.js | 1 + test/12.toLowerCase.js | 3 +- test/13.toUpperCase.js | 3 +- test/15.isObject.js | 7 +- test/16.isArray.js | 33 +- 29 files changed, 1355 insertions(+), 219 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..e56c822 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": ["airbnb-base"], + "parser": "babel-eslint", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly", + "it": true, + "describe": true, + "before": true + }, + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "new-cap": "warn", + "no-underscore-dangle": "off" + } +} diff --git a/package-lock.json b/package-lock.json index 312c32a..1248406 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "shark-validator", - "version": "1.1.0", + "version": "1.3.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1481,6 +1481,12 @@ } } }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/node": { "version": "13.9.5", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.5.tgz", @@ -1511,12 +1517,17 @@ "acorn": "^2.1.0" } }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", "dev": true, - "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1530,6 +1541,23 @@ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", "dev": true }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dev": true, + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "dev": true + } + } + }, "ansi-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", @@ -1578,6 +1606,27 @@ "dev": true, "optional": true }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, "asn1": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", @@ -1602,6 +1651,12 @@ "dev": true, "optional": true }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, "async-each": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", @@ -1690,6 +1745,20 @@ } } }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + } + }, "babel-generator": { "version": "6.26.1", "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", @@ -1945,6 +2014,12 @@ } } }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1986,6 +2061,12 @@ } } }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", + "dev": true + }, "cheerio": { "version": "1.0.0-rc.2", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", @@ -2081,6 +2162,21 @@ } } }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, "cliui": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", @@ -2175,6 +2271,18 @@ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, + "confusing-browser-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.9.tgz", + "integrity": "sha512-KbS1Y0jMtyPgIxjO7ZzMAuUpAKMt1SzCL9fsrKsX6b0zJPTaT0SiSPmewwVZg9UAO83HVIlEhZF84LIjZ0lmAw==", + "dev": true + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, "convert-source-map": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", @@ -2221,6 +2329,19 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, "css-select": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", @@ -2292,8 +2413,7 @@ "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true, - "optional": true + "dev": true }, "define-properties": { "version": "1.1.3", @@ -2385,6 +2505,15 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, "dom-serializer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", @@ -2449,6 +2578,15 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", "dev": true }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, "es-abstract": { "version": "1.17.4", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", @@ -2787,18 +2925,248 @@ "integrity": "sha1-H5h0xqfCvr+a05fDzrdcnGnaurE=", "dev": true }, + "eslint": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", + "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^5.0.0", + "eslint-utils": "^1.4.3", + "eslint-visitor-keys": "^1.1.0", + "espree": "^6.1.2", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^7.0.0", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.14", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.3", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^6.1.2", + "strip-ansi": "^5.2.0", + "strip-json-comments": "^3.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + } + } + }, + "eslint-config-airbnb-base": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz", + "integrity": "sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==", + "dev": true, + "requires": { + "confusing-browser-globals": "^1.0.9", + "object.assign": "^4.1.0", + "object.entries": "^1.1.1" + } + }, + "eslint-import-resolver-node": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", + "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.20.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz", + "integrity": "sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg==", + "dev": true, + "requires": { + "array-includes": "^3.0.3", + "array.prototype.flat": "^1.2.1", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.2", + "eslint-module-utils": "^2.4.1", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.0", + "read-pkg-up": "^2.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", + "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", + "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "dev": true, + "requires": { + "acorn": "^7.1.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "acorn": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", + "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", + "dev": true + } + } + }, "esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "optional": true + "dev": true }, "esutils": { "version": "2.0.3", @@ -2836,6 +3204,17 @@ } } }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -2847,22 +3226,37 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==", - "dev": true, - "optional": true + "dev": true }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "optional": true + "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", "dev": true, - "optional": true + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } }, "file-uri-to-path": { "version": "1.0.0", @@ -2917,6 +3311,23 @@ } } }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3540,6 +3951,12 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, "gensync": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", @@ -3744,6 +4161,12 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hosted-git-info": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", + "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", + "dev": true + }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -3869,6 +4292,37 @@ } } }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -3885,30 +4339,147 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "inquirer": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", + "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", "dev": true, - "optional": true, "requires": { - "kind-of": "^3.0.2" - } - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, + "ansi-escapes": "^4.2.1", + "chalk": "^3.0.0", + "cli-cursor": "^3.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.15", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.5.3", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, "optional": true, "requires": { "binary-extensions": "^1.0.0" @@ -4032,6 +4603,12 @@ "has": "^1.0.3" } }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", @@ -4059,8 +4636,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true + "dev": true }, "isexe": { "version": "2.0.0", @@ -4148,8 +4724,13 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "optional": true + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true }, "json-stringify-safe": { "version": "5.0.1", @@ -4228,12 +4809,31 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, - "optional": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" } }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "locate-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", @@ -4390,6 +4990,12 @@ "mime-db": "1.43.0" } }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -4506,6 +5112,12 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, "nan": { "version": "2.14.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", @@ -4556,6 +5168,18 @@ } } }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, "node-environment-flags": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", @@ -4583,6 +5207,18 @@ } } }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4679,6 +5315,18 @@ "object-keys": "^1.0.11" } }, + "object.entries": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", + "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "object.getownpropertydescriptors": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", @@ -4708,6 +5356,18 @@ } } }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -4717,12 +5377,20 @@ "wrappy": "1" } }, + "onetime": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", + "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", "dev": true, - "optional": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.6", @@ -4732,6 +5400,12 @@ "word-wrap": "~1.2.3" } }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, "p-limit": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", @@ -4756,6 +5430,24 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, "parse5": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", @@ -4791,12 +5483,35 @@ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, "path-parse": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", @@ -4816,6 +5531,60 @@ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "dev": true }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -4827,8 +5596,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "optional": true + "dev": true }, "private": { "version": "0.1.8", @@ -4843,6 +5611,12 @@ "dev": true, "optional": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -4854,8 +5628,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "optional": true + "dev": true }, "qs": { "version": "6.5.2", @@ -4864,6 +5637,72 @@ "dev": true, "optional": true }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + }, + "dependencies": { + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + } + } + }, "readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -5226,6 +6065,12 @@ "safe-regex": "^1.1.0" } }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "dev": true + }, "remove-trailing-separator": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", @@ -5306,6 +6151,12 @@ "path-parse": "^1.0.6" } }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, "resolve-url": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", @@ -5313,6 +6164,16 @@ "dev": true, "optional": true }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -5320,6 +6181,30 @@ "dev": true, "optional": true }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "rxjs": { + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", + "integrity": "sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ==", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -5340,8 +6225,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true + "dev": true }, "sax": { "version": "1.2.4", @@ -5387,6 +6271,38 @@ } } }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -5533,6 +6449,38 @@ "dev": true, "optional": true }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, "split-string": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", @@ -5638,6 +6586,12 @@ "ansi-regex": "^3.0.0" } }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -5660,12 +6614,73 @@ "dev": true, "optional": true }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "taffydb": { "version": "2.7.3", "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", "integrity": "sha1-KtNxaWKUmPylvIQkMJbTzeDsOjQ=", "dev": true }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, "to-fast-properties": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", @@ -5742,6 +6757,12 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -5764,11 +6785,16 @@ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, - "optional": true, "requires": { "prelude-ls": "~1.1.2" } }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -5879,7 +6905,6 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", "dev": true, - "optional": true, "requires": { "punycode": "^2.1.0" } @@ -5911,6 +6936,22 @@ "dev": true, "optional": true }, + "v8-compile-cache": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", + "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "dev": true + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", @@ -5968,8 +7009,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "optional": true + "dev": true }, "wrap-ansi": { "version": "5.1.0", @@ -6016,6 +7056,15 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, "xml-name-validator": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", diff --git a/package.json b/package.json index 41f10e6..f506211 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build:cjs": "babel --env-name cjs src --out-dir lib", "build:es": "babel --env-name es src --out-dir es", "build:docs": "./node_modules/.bin/esdoc", + "lint": "eslint src/**/* test/**/* --fix", "test": "mocha", "dev:test": "mocha", "pretest": "npm run build" @@ -18,6 +19,7 @@ "@babel/core": "^7.8.7", "@babel/plugin-proposal-class-properties": "^7.8.3", "@babel/preset-env": "^7.8.7", + "babel-eslint": "^10.1.0", "esdoc": "^1.1.0", "esdoc-brand-plugin": "^1.0.1", "esdoc-ecmascript-proposal-plugin": "^1.0.0", @@ -25,6 +27,9 @@ "esdoc-integrate-test-plugin": "^1.0.0", "esdoc-lint-plugin": "^1.0.2", "esdoc-standard-plugin": "^1.0.0", + "eslint": "^6.8.0", + "eslint-config-airbnb-base": "^14.1.0", + "eslint-plugin-import": "^2.20.2", "mocha": "^7.1.1" }, "repository": { diff --git a/src/RuleSet.js b/src/RuleSet.js index 60610a7..f6f2061 100644 --- a/src/RuleSet.js +++ b/src/RuleSet.js @@ -1,5 +1,5 @@ +/* eslint-disable import/no-cycle */ import { Rule, isObject } from './rules'; -import Validator from './Validator'; import isArray from '../lib/rules/isArray'; /** @@ -30,7 +30,8 @@ class RuleSet { /** * Create a ruleset for a particular `key` or `value`. * @param {Object} options Options for `RuleSet`. - * @param {Array} options.rules Array of `Rule` object (should not be set if the key is an obejct) + * @param {Array} options.rules Array of `Rule` object + * (should not be set if the key is an obejct) * @param {String} options.label The name or label of the value being checked */ constructor(options) { @@ -105,19 +106,22 @@ class RuleSet { } } + // eslint-disable-next-line no-restricted-syntax for (const rule of this.__rules) { if (!(rule instanceof Rule)) { throw new TypeError('Rule should be an instance of `Rule` class.'); } - let currentPath = options.isArrayElem - ? path - : path - ? `${path}.${key}` - : key; + let currentPath = key; + if (options.isArrayElem) { + currentPath = path; + } else if (path) { + currentPath = `${path}.${key}`; + } + const { value, error } = rule.validate(modifiedValue, { label: this.__label || key, - key: key, + key, path: currentPath, showNestedError, returnEarly, @@ -127,18 +131,20 @@ class RuleSet { if (error) { if (!showNestedError && typeof error === 'object') { + // eslint-disable-next-line no-restricted-syntax for (const innerKey in error) { if (error[innerKey]) { errors = errors.concat(error[innerKey]); } } - } else + } else { errors.push({ error, validator: rule.__name, value, path: currentPath, }); + } if (returnEarly) break; } } @@ -164,7 +170,8 @@ class RuleSet { * @param {Validator} schema A `Validator` object to be checked against the object * @param {String} label The name or label of the value being checked * @param {Object} schemaOptions Options for `isObject` - * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} schemaOptions.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) * @returns {RuleSet} A new `RuleSet` object */ static object(schema, label, schemaOptions) { @@ -188,7 +195,8 @@ class RuleSet { * @param {Number} schemaOptions.eq Length should be equal to `eq` * @param {Number} schemaOptions.min Length should be min `min` * @param {Number} schemaOptions.max Length should be max to `max` - * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} schemaOptions.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) * @returns {RuleSet} A new `RuleSet` object */ static array(rules, label, schemaOptions) { @@ -211,7 +219,8 @@ class RuleSet { * @param {Number} schemaOptions.eq Length should be equal to `eq` * @param {Number} schemaOptions.min Length should be min `min` * @param {Number} schemaOptions.max Length should be max to `max` - * @param {String} schemaOptions.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} schemaOptions.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) * @returns {RuleSet} A new `RuleSet` object */ static arrayOfObject(schema, label, schemaOptions) { diff --git a/src/rules/Rule.js b/src/rules/Rule.js index af4acab..e728dcf 100644 --- a/src/rules/Rule.js +++ b/src/rules/Rule.js @@ -1,3 +1,4 @@ +/* eslint-disable class-methods-use-this */ /** * Rule class should be extended in order to create any rule. * @abstract @@ -42,25 +43,30 @@ export default class Rule { * @param {String} label Name or Label of the value being checked. * @returns {{ value: any, error: String }} Value and error string. */ - validate(value, label) {} + // eslint-disable-next-line no-unused-vars + validate(value, label) { + // To be overridden + } /** * This formats the formatter string and includes the variables in it * from `values` object. * The variable key must be surrounded by `%` char. - * + * * @param {String} formatter The format string. * @param {Object} values Object containing `key` and `value` pairs used in formatter `string`. * @returns {String} Returns formatted string - * + * * @example * const formattedString = formatMessage('%name% should not be empty.', { name: 'Email' }); * // Returns 'Email should not be empty.' - * + * * @example * // If the message contains actual `%` symbol, it should be prefixed with `-`. - * - * const formattedString = formatMessage('%name% should be greater than 90-%.', { name: 'Marks' }); + * + * const formattedString = formatMessage('%name% should be greater than 90-%.', + * { name: 'Marks' } + * ); * // Returns 'Marks should be greater than 90%.' */ formatMessage(formatter, values) { @@ -78,13 +84,13 @@ export default class Rule { let key = ''; let isBuildingKey = false; - for (let i = 0; i < formatter.length; i++) { + for (let i = 0; i < formatter.length; i += 1) { const char = formatter.charAt(i); switch (char) { case '-': if (i + 1 < formatter.length && formatter.charAt(i + 1) === '%') { newString += '%'; - i++; + i += 1; } else { newString += '-'; } @@ -94,7 +100,7 @@ export default class Rule { if (isBuildingKey) { key = ''; } else { - let value = __values[key]; + const value = __values[key]; if (value === null || value === undefined) { throw new Error(`Value of \`${key}\` is not present.`); } diff --git a/src/rules/index.js b/src/rules/index.js index cf0f4cb..af28109 100644 --- a/src/rules/index.js +++ b/src/rules/index.js @@ -1,3 +1,4 @@ +/* eslint-disable import/no-cycle */ export { default as Rule } from './Rule'; export { default as isString } from './isString'; export { default as isRequired } from './isRequired'; diff --git a/src/rules/isAlpha.js b/src/rules/isAlpha.js index 2c26414..9b77177 100644 --- a/src/rules/isAlpha.js +++ b/src/rules/isAlpha.js @@ -23,7 +23,8 @@ export default class isAlpha extends Rule { * Checks if the value contains only Alphabets. * @param {Object} options Options for `isAlpha` * @param {Boolean} options.allowSpaces If `true`, it allows spaces - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isAlpha'); @@ -37,15 +38,15 @@ export default class isAlpha extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } if ( - options.allowSpaces !== undefined && - typeof options.allowSpaces !== 'boolean' + options.allowSpaces !== undefined + && typeof options.allowSpaces !== 'boolean' ) { throw new Error('`allowSpaces` key in `options` should be a boolean.'); } @@ -77,23 +78,24 @@ export default class isAlpha extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value === 'string') { const data = { name: label, }; - if (!this.regex.test(value)) + if (!this.regex.test(value)) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should contain only alphabets.", - data, - ), + "'%name%' should contain only alphabets.", + data, + ), }; + } } return { value, error: null }; } diff --git a/src/rules/isAlphaNum.js b/src/rules/isAlphaNum.js index d95089b..cb5772d 100644 --- a/src/rules/isAlphaNum.js +++ b/src/rules/isAlphaNum.js @@ -23,7 +23,8 @@ export default class isAlphaNum extends Rule { * Checks if the value contains only Alphabets and numbers. * @param {Object} options Options for `isAlphaNum` * @param {Boolean} options.allowSpaces If `true`, it allows spaces - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isAlphaNum'); @@ -37,15 +38,15 @@ export default class isAlphaNum extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } if ( - options.allowSpaces !== undefined && - typeof options.allowSpaces !== 'boolean' + options.allowSpaces !== undefined + && typeof options.allowSpaces !== 'boolean' ) { throw new Error('`allowSpaces` key in `options` should be a boolean.'); } @@ -77,23 +78,24 @@ export default class isAlphaNum extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value === 'string') { const data = { name: label, }; - if (!this.regex.test(value)) + if (!this.regex.test(value)) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should contain only alphabets and numbers.", - data, - ), + "'%name%' should contain only alphabets and numbers.", + data, + ), }; + } } return { value, error: null }; } diff --git a/src/rules/isArray.js b/src/rules/isArray.js index b66aba3..d4275c5 100644 --- a/src/rules/isArray.js +++ b/src/rules/isArray.js @@ -37,7 +37,8 @@ export default class isArray extends Rule { * @param {Number} options.eq Length should be equal to `eq` * @param {Number} options.min Length should be min `min` * @param {Number} options.max Length should be max to `max` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isArray'); @@ -64,6 +65,7 @@ export default class isArray extends Rule { this.rules = options.rules; const keys = ['min', 'max', 'eq']; + // eslint-disable-next-line no-restricted-syntax for (const key of keys) { if (options[key] !== undefined && typeof options[key] !== 'number') { throw new TypeError(`\`${key}\` key in options should be an integer.`); @@ -88,7 +90,7 @@ export default class isArray extends Rule { * @returns {{ value: any, error: String }} Value and error string. */ validate(value, options) { - let showNestedError = undefined; + let showNestedError; if (typeof options !== 'object') { throw new TypeError('`options` should be an object.'); @@ -98,13 +100,13 @@ export default class isArray extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof options.path !== 'string') { throw new TypeError('`options.path` should be a string.'); } - const path = options.path; + const { path } = options; if (options.showNestedError !== undefined) { if (typeof options.showNestedError !== 'boolean') { @@ -142,18 +144,18 @@ export default class isArray extends Rule { } if ( - this.min !== undefined && - this.max !== undefined && - (len > this.max || len < this.min) + this.min !== undefined + && this.max !== undefined + && (len > this.max || len < this.min) ) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should have %min% - %max% elements.", - data, - ), + "'%name%' should have %min% - %max% elements.", + data, + ), }; } @@ -163,9 +165,9 @@ export default class isArray extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should have greater than %min% elements.", - data, - ), + "'%name%' should have greater than %min% elements.", + data, + ), }; } @@ -175,21 +177,21 @@ export default class isArray extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should have less than %max% elements.", - data, - ), + "'%name%' should have less than %max% elements.", + data, + ), }; } - let allErrors = {}; + const allErrors = {}; const allValues = []; - for (let i = 0; i < len; i++) { + for (let i = 0; i < len; i += 1) { const { errors, value: localValue } = this.rules.validate( value[i], options.key, { - path: path + `[${i}]`, + path: `${path}[${i}]`, showNestedError, isArrayElem: true, returnEarly: options.returnEarly, diff --git a/src/rules/isEmail.js b/src/rules/isEmail.js index 99aa36d..7222491 100644 --- a/src/rules/isEmail.js +++ b/src/rules/isEmail.js @@ -17,7 +17,8 @@ export default class isEmail extends Rule { /** * Checks if the value is a valid email address. * @param {Object} options Options for `isEmail` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isEmail'); @@ -29,8 +30,8 @@ export default class isEmail extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -38,7 +39,7 @@ export default class isEmail extends Rule { this.message = options.message; } - this.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,}))$/; + this.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,}))$/; } /** @@ -59,20 +60,21 @@ export default class isEmail extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value === 'string') { const data = { name: label, }; - if (!this.regex.test(value)) + if (!this.regex.test(value)) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage("'%name%' should be a valid email.", data), }; + } } return { value, error: null }; } diff --git a/src/rules/isIn.js b/src/rules/isIn.js index c9707d1..ea221a1 100644 --- a/src/rules/isIn.js +++ b/src/rules/isIn.js @@ -18,7 +18,8 @@ export default class isIn extends Rule { * Checks if the value is in the given array (works for number and strings) (is type sensitive) * @param {Object} options Options for `isIn` * @param {Array} options.in Array containing possible values - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isIn'); @@ -60,7 +61,7 @@ export default class isIn extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; const data = { name: label, @@ -71,8 +72,8 @@ export default class isIn extends Rule { : this.formatMessage("'%name%' should be one of '%in%'.", data); if ( - (typeof value !== 'string' && typeof value !== 'number') || - !this.in.includes(value) + (typeof value !== 'string' && typeof value !== 'number') + || !this.in.includes(value) ) { return { value, error: errorMsg }; } diff --git a/src/rules/isInt.js b/src/rules/isInt.js index f2d921c..1108014 100644 --- a/src/rules/isInt.js +++ b/src/rules/isInt.js @@ -24,7 +24,8 @@ export default class isInt extends Rule { * @param {Object} options Options for `isInt` * @param {Number} options.min Number should be min to `min` * @param {Number} options.max Number should be max to `max` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isInt'); @@ -38,6 +39,7 @@ export default class isInt extends Rule { if (options !== undefined) { const keys = ['min', 'max']; + // eslint-disable-next-line no-restricted-syntax for (const key of keys) { if (options[key] !== undefined && typeof options[key] !== 'number') { throw new TypeError( @@ -47,8 +49,8 @@ export default class isInt extends Rule { } if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -77,7 +79,7 @@ export default class isInt extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; const data = { name: label, @@ -85,6 +87,7 @@ export default class isInt extends Rule { max: this.max, }; + // eslint-disable-next-line no-restricted-globals if (isNaN(value)) { return { value, @@ -106,18 +109,18 @@ export default class isInt extends Rule { } if ( - this.min !== undefined && - this.max !== undefined && - (num > this.max || num < this.min) + this.min !== undefined + && this.max !== undefined + && (num > this.max || num < this.min) ) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should be a between %min% - %max%.", - data, - ), + "'%name%' should be a between %min% - %max%.", + data, + ), }; } @@ -136,9 +139,9 @@ export default class isInt extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should not be greater than %max%.", - data, - ), + "'%name%' should not be greater than %max%.", + data, + ), }; } diff --git a/src/rules/isLen.js b/src/rules/isLen.js index 375d7eb..07d6e4b 100644 --- a/src/rules/isLen.js +++ b/src/rules/isLen.js @@ -30,7 +30,8 @@ export default class isLen extends Rule { * @param {Number} options.eq Length should be equal to `eq` * @param {Number} options.min Length should be min `min` * @param {Number} options.max Length should be max to `max` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isLen'); @@ -44,6 +45,7 @@ export default class isLen extends Rule { ); } const keys = ['min', 'max', 'eq']; + // eslint-disable-next-line no-restricted-syntax for (const key of keys) { if (options[key] !== undefined && typeof options[key] !== 'number') { throw new TypeError(`\`${key}\` key in options should be an integer.`); @@ -78,7 +80,7 @@ export default class isLen extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value === 'string') { const len = value.length; @@ -95,25 +97,25 @@ export default class isLen extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should be %eq% characters long.", - data, - ), + "'%name%' should be %eq% characters long.", + data, + ), }; } if ( - this.min !== undefined && - this.max !== undefined && - (len > this.max || len < this.min) + this.min !== undefined + && this.max !== undefined + && (len > this.max || len < this.min) ) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should be a between %min% - %max% characters.", - data, - ), + "'%name%' should be a between %min% - %max% characters.", + data, + ), }; } @@ -123,9 +125,9 @@ export default class isLen extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should not be less than %min% characters.", - data, - ), + "'%name%' should not be less than %min% characters.", + data, + ), }; } @@ -135,9 +137,9 @@ export default class isLen extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should not be greater than %max% characters.", - data, - ), + "'%name%' should not be greater than %max% characters.", + data, + ), }; } } diff --git a/src/rules/isNumber.js b/src/rules/isNumber.js index 6f107fb..5428e13 100644 --- a/src/rules/isNumber.js +++ b/src/rules/isNumber.js @@ -24,7 +24,8 @@ export default class isNumber extends Rule { * @param {Object} options Options for `isNumber` * @param {Number} options.min Number should be min to `min` * @param {Number} options.max Number should be max to `max` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isNumber'); @@ -38,6 +39,7 @@ export default class isNumber extends Rule { if (options !== undefined) { const keys = ['min', 'max']; + // eslint-disable-next-line no-restricted-syntax for (const key of keys) { if (options[key] !== undefined && typeof options[key] !== 'number') { throw new TypeError( @@ -47,8 +49,8 @@ export default class isNumber extends Rule { } if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -77,7 +79,7 @@ export default class isNumber extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; const data = { name: label, @@ -85,6 +87,7 @@ export default class isNumber extends Rule { max: this.max, }; + // eslint-disable-next-line no-restricted-globals if (isNaN(value)) { return { value, @@ -97,18 +100,18 @@ export default class isNumber extends Rule { const num = Number.parseFloat(value); if ( - this.min !== undefined && - this.max !== undefined && - (num > this.max || num < this.min) + this.min !== undefined + && this.max !== undefined + && (num > this.max || num < this.min) ) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should be a between %min% - %max%.", - data, - ), + "'%name%' should be a between %min% - %max%.", + data, + ), }; } @@ -127,9 +130,9 @@ export default class isNumber extends Rule { error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should not be greater than %max%.", - data, - ), + "'%name%' should not be greater than %max%.", + data, + ), }; } diff --git a/src/rules/isObject.js b/src/rules/isObject.js index c66bc30..70c5eb8 100644 --- a/src/rules/isObject.js +++ b/src/rules/isObject.js @@ -19,7 +19,8 @@ export default class isObject extends Rule { * Checks if the value is an object and satisfies the given schema * @param {Object} options Options for `isObject` * @param {Validator} options.schema Schema for the object - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isObject'); @@ -33,8 +34,8 @@ export default class isObject extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -42,8 +43,8 @@ export default class isObject extends Rule { this.message = options.message; if ( - options.schema !== undefined && - !(options.schema instanceof Validator) + options.schema !== undefined + && !(options.schema instanceof Validator) ) { throw new Error( '`schema` key in `options` should be an instance of class `Validator`.', @@ -66,7 +67,7 @@ export default class isObject extends Rule { * @returns {{ value: any, error: String }} Value and error string. */ validate(value, options) { - let showNestedError = undefined; + let showNestedError; if (typeof options !== 'object') { throw new TypeError('`options` should be an object.'); @@ -76,13 +77,13 @@ export default class isObject extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof options.path !== 'string') { throw new TypeError('`options.path` should be a string.'); } - const path = options.path; + const { path } = options; if (options.showNestedError !== undefined) { if (typeof options.showNestedError !== 'boolean') { diff --git a/src/rules/isRequired.js b/src/rules/isRequired.js index 06f21d3..9a91df7 100644 --- a/src/rules/isRequired.js +++ b/src/rules/isRequired.js @@ -12,7 +12,8 @@ export default class isRequired extends Rule { /** * Requires the field to be non empty. * @param {Object} options Options for `isRequired` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) * */ constructor(options) { @@ -25,8 +26,8 @@ export default class isRequired extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -53,12 +54,12 @@ export default class isRequired extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if ( - value === null || - value === undefined || - (typeof value === 'string' && value === '') + value === null + || value === undefined + || (typeof value === 'string' && value === '') ) { const data = { name: label, diff --git a/src/rules/isString.js b/src/rules/isString.js index e489994..5549987 100644 --- a/src/rules/isString.js +++ b/src/rules/isString.js @@ -12,7 +12,8 @@ export default class isString extends Rule { /** * Required the field to be a `string` * @param {Object} options Options for `isString` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('isString'); @@ -24,8 +25,8 @@ export default class isString extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -52,7 +53,7 @@ export default class isString extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value !== 'string') { const data = { diff --git a/src/rules/matchRegex.js b/src/rules/matchRegex.js index ea88149..57550d6 100644 --- a/src/rules/matchRegex.js +++ b/src/rules/matchRegex.js @@ -18,7 +18,8 @@ export default class matchRegex extends Rule { * Checks if the value matches the regular expression. * @param {Object} options Options for `matchRegex` * @param {RegExp} options.regex Regex expression - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('matchRegex'); @@ -33,8 +34,8 @@ export default class matchRegex extends Rule { throw new TypeError('`options` should be an object.'); } if ( - typeof options.regex !== 'object' || - !(options.regex instanceof RegExp) + typeof options.regex !== 'object' + || !(options.regex instanceof RegExp) ) { throw new Error( '`regex` key in should be an instance of `RegExp` class.', @@ -67,7 +68,7 @@ export default class matchRegex extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; if (typeof value === 'string') { const data = { @@ -75,16 +76,17 @@ export default class matchRegex extends Rule { regex: this.regex.source, }; - if (!this.regex.test(value)) + if (!this.regex.test(value)) { return { value, error: this.message ? this.formatMessage(this.message, data) : this.formatMessage( - "'%name%' should match the regex '%regex%'.", - data, - ), + "'%name%' should match the regex '%regex%'.", + data, + ), }; + } } return { value, error: null }; } diff --git a/src/rules/toInt.js b/src/rules/toInt.js index e47d13b..422a8e3 100644 --- a/src/rules/toInt.js +++ b/src/rules/toInt.js @@ -12,7 +12,8 @@ export default class toInt extends Rule { /** * Converts the value to an integer and throws error if it cannot be converted * @param {Object} options Options for `toInt` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('toInt'); @@ -24,8 +25,8 @@ export default class toInt extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -52,12 +53,13 @@ export default class toInt extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; const data = { name: label, }; + // eslint-disable-next-line no-restricted-globals if (isNaN(value)) { return { value, @@ -78,7 +80,7 @@ export default class toInt extends Rule { }; } - const num = Number.parseInt(floatNum); + const num = Number.parseInt(floatNum, 10); return { value: num, error: null }; } diff --git a/src/rules/toLowerCase.js b/src/rules/toLowerCase.js index e6efd63..4a06493 100644 --- a/src/rules/toLowerCase.js +++ b/src/rules/toLowerCase.js @@ -20,6 +20,7 @@ export default class toLowerCase extends Rule { * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ + // eslint-disable-next-line class-methods-use-this validate(value, options) { if (typeof options !== 'object') { throw new TypeError('`options` should be an object.'); diff --git a/src/rules/toNumber.js b/src/rules/toNumber.js index c959858..24858cc 100644 --- a/src/rules/toNumber.js +++ b/src/rules/toNumber.js @@ -12,7 +12,8 @@ export default class toNumber extends Rule { /** * Converts the value to Number and throws error if it cannot be converted * @param {Object} options Options for `toInt` - * @param {String} options.message Custom error message if test fails (check {@link Rule#formatMessage} for more customization details) + * @param {String} options.message Custom error message if test fails + * (check {@link Rule#formatMessage} for more customization details) */ constructor(options) { super('toNumber'); @@ -24,8 +25,8 @@ export default class toNumber extends Rule { if (options !== undefined) { if ( - options.message !== undefined && - typeof options.message !== 'string' + options.message !== undefined + && typeof options.message !== 'string' ) { throw new Error('`message` key in `options` should be a string.'); } @@ -52,12 +53,13 @@ export default class toNumber extends Rule { throw new TypeError('`options.label` should be a string.'); } - const label = options.label; + const { label } = options; const data = { name: label, }; + // eslint-disable-next-line no-restricted-globals if (isNaN(value)) { return { value, diff --git a/src/rules/toUpperCase.js b/src/rules/toUpperCase.js index ec2c5cb..0070c3d 100644 --- a/src/rules/toUpperCase.js +++ b/src/rules/toUpperCase.js @@ -20,6 +20,7 @@ export default class toUpperCase extends Rule { * @param {String} options.path Validator path. * @returns {{ value: any, error: String }} Value and error string. */ + // eslint-disable-next-line class-methods-use-this validate(value, options) { if (typeof options !== 'object') { throw new TypeError('`options` should be an object.'); diff --git a/test/00.Base.js b/test/00.Base.js index 3d34f37..38525ff 100644 --- a/test/00.Base.js +++ b/test/00.Base.js @@ -1,5 +1,7 @@ const assert = require('assert'); -const { Validator, RuleSet, isString, isRequired } = require('../lib'); +const { + Validator, RuleSet, isString, isRequired, +} = require('../lib'); /** * @test {Validator} diff --git a/test/06.toNumber.js b/test/06.toNumber.js index 2c68577..e1f8b67 100644 --- a/test/06.toNumber.js +++ b/test/06.toNumber.js @@ -26,7 +26,7 @@ describe('06. toNumber', () => { id: '201a4', age: '~10', yearOfBirth: '20181.01a', - monthOfBirth: 'Jan' + monthOfBirth: 'Jan', }); errors = data.errors; }); @@ -80,12 +80,13 @@ describe('06. toNumber', () => { describe('With valid values', () => { let errors; + let values; before(() => { const data = schema.validate({ id: '10.12', age: 1.0, yearOfBirth: '2018', - monthOfBirth: '5' + monthOfBirth: '5', }); errors = data.errors; values = data.values; diff --git a/test/08.toInt.js b/test/08.toInt.js index 8ff67a7..2c7da00 100644 --- a/test/08.toInt.js +++ b/test/08.toInt.js @@ -80,6 +80,7 @@ describe('08. toInt', () => { describe('With valid values', () => { let errors; + let values; before(() => { const data = schema.validate({ id: '10', diff --git a/test/12.toLowerCase.js b/test/12.toLowerCase.js index 8e7ca24..556684e 100644 --- a/test/12.toLowerCase.js +++ b/test/12.toLowerCase.js @@ -10,7 +10,8 @@ const schema = new Validator({ * @test {toLowerCase} */ describe('12. toLowerCase', () => { - let values, errors; + let values; let + errors; before(() => { const data = schema.validate({ email: 'Irshad@GMAIL.com', diff --git a/test/13.toUpperCase.js b/test/13.toUpperCase.js index cee0ea2..d19da00 100644 --- a/test/13.toUpperCase.js +++ b/test/13.toUpperCase.js @@ -10,7 +10,8 @@ const schema = new Validator({ * @test {toUpperCase} */ describe('13. toUpperCase', () => { - let values, errors; + let values; let + errors; before(() => { const data = schema.validate({ email: 'Irshad@GMAIL.com', diff --git a/test/15.isObject.js b/test/15.isObject.js index c0d8f41..705bd39 100644 --- a/test/15.isObject.js +++ b/test/15.isObject.js @@ -1,5 +1,7 @@ const assert = require('assert'); -const { Validator, RuleSet, isString, toLowerCase } = require('../lib'); +const { + Validator, RuleSet, isString, toLowerCase, +} = require('../lib'); const addressSchema = new Validator({ city: RuleSet.create([new isString(), new toLowerCase()]), @@ -140,7 +142,8 @@ describe('15. isObject', () => { }); describe('With valid values', () => { - let errors, values; + let errors; let + values; before(() => { const data = schema.validate({ user: { name: 'IRSHAD', address: { city: 'Bangalore' } }, diff --git a/test/16.isArray.js b/test/16.isArray.js index 0a34c94..fa3842d 100644 --- a/test/16.isArray.js +++ b/test/16.isArray.js @@ -42,7 +42,8 @@ const plainArraySchema = new Validator({ describe('16. isArray', () => { describe('With invalid values', () => { describe('Should return error if not an array.', () => { - let result1, result2; + let result1; + let result2; before(() => { const data1 = schema.validate({ users: 'Irshad', @@ -83,7 +84,8 @@ describe('16. isArray', () => { }); describe('Should return error if less than min elements.', () => { - let result1, result2; + let result1; + let result2; before(() => { const data1 = schema.validate({ users: [], @@ -124,7 +126,8 @@ describe('16. isArray', () => { }); describe('Should return error if greater than max elements.', () => { - let result1, result2; + let result1; + let result2; before(() => { const data1 = schema.validate({ users: [{}, {}, {}, {}, {}], @@ -165,7 +168,8 @@ describe('16. isArray', () => { }); describe('Should return error if invalid element is present.', () => { - let result1, result2; + let result1; + let result2; before(() => { const data1 = schema.validate({ users: [ @@ -223,7 +227,10 @@ describe('16. isArray', () => { }); describe('With valid values', () => { - let errors1, errors2, values1, values2; + let errors1; + let errors2; + let values1; + let values2; before(() => { const data1 = schema.validate({ users: [ @@ -269,17 +276,15 @@ describe('16. isArray', () => { assert.equal(value, 'irshad@gmail.com'); assert.equal(typeof values2, 'object'); - value = values2.users[0]; - assert.equal(typeof value, 'string'); - assert.equal(value, 'irshad'); + const [elem1, elem2, elem3] = values2.users; + assert.equal(typeof elem1, 'string'); + assert.equal(elem1, 'irshad'); - value = values2.users[1]; - assert.equal(typeof value, 'string'); - assert.equal(value, 'irshad'); + assert.equal(typeof elem2, 'string'); + assert.equal(elem2, 'irshad'); - value = values2.users[2]; - assert.equal(typeof value, 'string'); - assert.equal(value, 'ansari'); + assert.equal(typeof elem3, 'string'); + assert.equal(elem3, 'ansari'); }); }); }); From 3778d400afd47d79565f50ce9d316610766249fa Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Tue, 19 May 2020 03:19:58 +0530 Subject: [PATCH 08/12] [Feat] Added pre-commit hooks; Added lint to CI --- .github/workflows/nodejs.yml | 1 + .github/workflows/npmpublish.yml | 1 + package-lock.json | 990 +++++++++++++++++++++++++++++-- package.json | 12 +- src/Validator.js | 40 +- 5 files changed, 974 insertions(+), 70 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index e6ef727..0e6ed86 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -25,6 +25,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm ci + - run: npm run lint - run: npm test env: CI: true diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index dc26ae6..f0e5147 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -17,6 +17,7 @@ jobs: node-version: 10 registry-url: https://registry.npmjs.org/ - run: npm ci + - run: npm run lint - run: npm test - run: npm publish env: diff --git a/package-lock.json b/package-lock.json index 1248406..84ccec2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1481,6 +1481,15 @@ } } }, + "@samverschueren/stream-to-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz", + "integrity": "sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg==", + "dev": true, + "requires": { + "any-observable": "^0.3.0" + } + }, "@types/color-name": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", @@ -1493,6 +1502,12 @@ "integrity": "sha512-hkzMMD3xu6BrJpGVLeQ3htQQNAcOrJjX7WFmtK8zWQpz2UJf13LCFF2ALA7c9OVdvc2vQJeDdjfR35M0sBCxvw==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "abab": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", @@ -1523,6 +1538,16 @@ "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", "dev": true }, + "aggregate-error": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", + "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, "ajv": { "version": "6.12.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", @@ -1573,6 +1598,12 @@ "color-convert": "^1.9.0" } }, + "any-observable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/any-observable/-/any-observable-0.3.0.tgz", + "integrity": "sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog==", + "dev": true + }, "anymatch": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", @@ -2130,6 +2161,12 @@ } } }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, "class-utils": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", @@ -2162,6 +2199,12 @@ } } }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true + }, "cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -2171,6 +2214,98 @@ "restore-cursor": "^3.1.0" } }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, "cli-width": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", @@ -2216,6 +2351,12 @@ } } }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "dev": true + }, "collection-visit": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", @@ -2258,6 +2399,18 @@ "delayed-stream": "~1.0.0" } }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -2329,6 +2482,39 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, + "cosmiconfig": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" + }, + "dependencies": { + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -2409,12 +2595,27 @@ "dev": true, "optional": true }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", "dev": true }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -2566,12 +2767,36 @@ "integrity": "sha512-cm2WzMKf/3dW5+hNANKm8GAW6SwIWOqLTJ6GPCD0Bbw1qJ9Wzm9nmx9M+byzSsgw8CdCv5fb/wzLFqVS5h6QrA==", "dev": true }, + "elegant-spinner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-2.0.0.tgz", + "integrity": "sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA==", + "dev": true + }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.5.tgz", + "integrity": "sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==", + "dev": true, + "requires": { + "ansi-colors": "^3.2.1" + } + }, "entities": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", @@ -3174,6 +3399,66 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "execa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.1.tgz", + "integrity": "sha512-SCjM/zlBdOK8Q5TIjOn6iEHZaPHFsMoTxXQ2nvUvtPnuohz3H2dIozSg+etNR98dGoYUp2ENSKLL/XaMmbxVgw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "cross-spawn": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz", + "integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3294,6 +3579,15 @@ "locate-path": "^3.0.0" } }, + "find-versions": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", + "dev": true, + "requires": { + "semver-regex": "^2.0.0" + } + }, "flat": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", @@ -3969,12 +4263,27 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, "get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", "dev": true }, + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -4206,75 +4515,194 @@ "sshpk": "^1.7.0" } }, - "ice-cap": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/ice-cap/-/ice-cap-0.0.4.tgz", - "integrity": "sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg=", - "dev": true, - "requires": { - "cheerio": "0.20.0", - "color-logger": "0.0.3" + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/husky/-/husky-4.2.5.tgz", + "integrity": "sha512-SYZ95AjKcX7goYVZtVZF2i6XiZcHknw50iXvY7b0MiGoj5RwdgRQNEHdb+gPDPCXKlzwrybjFjkL6FOj8uRhZQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "ci-info": "^2.0.0", + "compare-versions": "^3.6.0", + "cosmiconfig": "^6.0.0", + "find-versions": "^3.2.0", + "opencollective-postinstall": "^2.0.2", + "pkg-dir": "^4.2.0", + "please-upgrade-node": "^3.2.0", + "slash": "^3.0.0", + "which-pm-runs": "^1.0.0" }, "dependencies": { - "cheerio": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", - "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", "dev": true, "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "~3.8.1", - "jsdom": "^7.0.2", - "lodash": "^4.1.0" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" } }, - "color-logger": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/color-logger/-/color-logger-0.0.3.tgz", - "integrity": "sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg=", - "dev": true - }, - "domhandler": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", - "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", "dev": true, "requires": { - "domelementtype": "1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "htmlparser2": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", - "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "domelementtype": "1", - "domhandler": "2.3", - "domutils": "1.5", - "entities": "1.0", - "readable-stream": "1.1" - }, - "dependencies": { - "entities": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", - "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", - "dev": true - } + "color-name": "~1.1.4" } }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ice-cap": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ice-cap/-/ice-cap-0.0.4.tgz", + "integrity": "sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg=", + "dev": true, + "requires": { + "cheerio": "0.20.0", + "color-logger": "0.0.3" + }, + "dependencies": { + "cheerio": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", + "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", + "dev": true, + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "~3.8.1", + "jsdom": "^7.0.2", + "lodash": "^4.1.0" + } + }, + "color-logger": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/color-logger/-/color-logger-0.0.3.tgz", + "integrity": "sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg=", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + } + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -4323,6 +4751,12 @@ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", "dev": true }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4575,6 +5009,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -4603,6 +5043,18 @@ "has": "^1.0.3" } }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, "is-string": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", @@ -4713,6 +5165,12 @@ "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", "dev": true }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -4814,6 +5272,189 @@ "type-check": "~0.3.2" } }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "lint-staged": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.2.4.tgz", + "integrity": "sha512-doTMGKXQAT34c3S3gwDrTnXmCZp/z1/92D8suPqqh755sKPT18ew1NoPNHxJdrvv1D4WrJ7CEnx79Ns3EdEFbg==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "commander": "^5.1.0", + "cosmiconfig": "^6.0.0", + "debug": "^4.1.1", + "dedent": "^0.7.0", + "execa": "^4.0.1", + "listr2": "^2.0.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "requires": { + "chalk": "^4.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "listr2": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-2.0.2.tgz", + "integrity": "sha512-HkbraLsbHRFtuT0p1g9KUiMoJeqlPdgsi4Q3mCvBlYnVK+2I1vPdCxBvJ+nAxwpL7SZiyaICWMvLOyMBtu+VKw==", + "dev": true, + "requires": { + "@samverschueren/stream-to-observable": "^0.3.0", + "chalk": "^4.0.0", + "cli-cursor": "^3.1.0", + "cli-truncate": "^2.1.0", + "elegant-spinner": "^2.0.0", + "enquirer": "^2.3.5", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "pad": "^3.2.0", + "rxjs": "^6.5.5", + "through": "^2.3.8", + "uuid": "^7.0.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", + "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "dev": true + } + } + }, "load-json-file": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -4931,6 +5572,111 @@ "chalk": "^2.4.2" } }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -4973,6 +5719,22 @@ "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==", "dev": true }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mime-db": { "version": "1.43.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz", @@ -5225,6 +5987,23 @@ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + } + } + }, "nth-check": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", @@ -5386,6 +6165,12 @@ "mimic-fn": "^2.1.0" } }, + "opencollective-postinstall": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.2.tgz", + "integrity": "sha512-pVOEP16TrAO2/fjej1IdOyupJY8KDUM1CvsaScRbw6oddvpQoOfGk4ywha0HKKVAD6RkW4x6Q+tNBwhf3Bgpuw==", + "dev": true + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -5424,12 +6209,30 @@ "p-limit": "^2.0.0" } }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true }, + "pad": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/pad/-/pad-3.2.0.tgz", + "integrity": "sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg==", + "dev": true, + "requires": { + "wcwidth": "^1.0.1" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5585,6 +6388,15 @@ } } }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -5624,6 +6436,16 @@ "dev": true, "optional": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", @@ -6240,6 +7062,18 @@ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", + "integrity": "sha512-mUdIBBvdn0PLOeP3TEkMH7HHeUP3GjsXCwKarjv/kGmUFOYg1VqEemKhoQpWMu6X2I8kHeuVdGibLGkVK+/5Qw==", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -6292,6 +7126,12 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", @@ -6538,6 +7378,12 @@ } } }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -6577,6 +7423,17 @@ "safe-buffer": "~5.1.0" } }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + } + }, "strip-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", @@ -6592,6 +7449,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", @@ -6964,6 +7827,15 @@ "extsprintf": "^1.2.0" } }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, "webidl-conversions": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", @@ -6996,6 +7868,12 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-pm-runs": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz", + "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs=", + "dev": true + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -7078,6 +7956,12 @@ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", "dev": true }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, "yargs": { "version": "13.3.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", diff --git a/package.json b/package.json index f506211..34ae174 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:cjs": "babel --env-name cjs src --out-dir lib", "build:es": "babel --env-name es src --out-dir es", "build:docs": "./node_modules/.bin/esdoc", - "lint": "eslint src/**/* test/**/* --fix", + "lint": "eslint src/**/* src/* test/**/* --fix", "test": "mocha", "dev:test": "mocha", "pretest": "npm run build" @@ -30,8 +30,18 @@ "eslint": "^6.8.0", "eslint-config-airbnb-base": "^14.1.0", "eslint-plugin-import": "^2.20.2", + "husky": "^4.2.5", + "lint-staged": "^10.2.4", "mocha": "^7.1.1" }, + "husky": { + "hooks": { + "pre-commit": "lint-staged && npm run test" + } + }, + "lint-staged": { + "*.js": "eslint --fix" + }, "repository": { "type": "git", "url": "https://github.com/irshadjsr21/Shark-Validator.git" diff --git a/src/Validator.js b/src/Validator.js index 965a75c..df8be06 100644 --- a/src/Validator.js +++ b/src/Validator.js @@ -30,10 +30,13 @@ export default class Validator { /** * Creates a validator schema. - * @param {Object} objectOfRuleSet Set of `RuleSet`. `key` should match with the `key` of object being validated. + * @param {Object} objectOfRuleSet Set of `RuleSet`. `key` should match + * with the `key` of object being validated. * @param {Object} options Options for validator schema. - * @param {Boolean} options.returnEarly If `true` returns whenever first `key` in `values` fails the test. - * @param {Boolean} options.returnRuleSetEarly If `true` returns the after getting the first error on all `keys`. + * @param {Boolean} options.returnEarly If `true` returns whenever first `key` + * in `values` fails the test. + * @param {Boolean} options.returnRuleSetEarly If `true` returns the after getting + * the first error on all `keys`. * @param {Boolean} options.showNestedError If `true` shows nested errors. */ constructor(objectOfRuleSet, options) { @@ -87,9 +90,12 @@ export default class Validator { * @param {Object} options Options for validator. * @param {String} options.path Validation path. * @param {Boolean} options.showNestedError If `true` shows nested errors. - * @param {Boolean} options.returnEarly If `true` returns whenever first `key` in `values` fails the test. - * @param {Boolean} options.returnRuleSetEarly If `true` returns the after getting the first error on all `keys`. - * @returns {{values: Object, errors: Object}} Object containing `values` and `errors` + * @param {Boolean} options.returnEarly If `true` returns whenever first + * `key` in `values` fails the test. + * @param {Boolean} options.returnRuleSetEarly If `true` returns the after + * getting the first error on all `keys`. + * @returns {{values: Object, errors: Object}} Object containing + * `values` and `errors` */ validate(valuesToCheck, options) { let path = ''; @@ -111,8 +117,8 @@ export default class Validator { if (typeof showNestedError !== 'boolean') { if ( - options.showNestedError !== undefined && - typeof options.showNestedError !== 'boolean' + options.showNestedError !== undefined + && typeof options.showNestedError !== 'boolean' ) { throw new TypeError('`options.showNestedError` should be a boolean.'); } @@ -121,8 +127,8 @@ export default class Validator { if (typeof this.__returnEarly !== 'boolean') { if ( - options.returnEarly !== undefined && - typeof options.returnEarly !== 'boolean' + options.returnEarly !== undefined + && typeof options.returnEarly !== 'boolean' ) { throw new TypeError('`options.returnEarly` should be a boolean.'); } @@ -131,10 +137,12 @@ export default class Validator { if (typeof this.__returnRuleSetEarly !== 'boolean') { if ( - options.returnRuleSetEarly !== undefined && - typeof options.returnRuleSetEarly !== 'boolean' + options.returnRuleSetEarly !== undefined + && typeof options.returnRuleSetEarly !== 'boolean' ) { - throw new TypeError('`options.returnRuleSetEarly` should be a boolean.'); + throw new TypeError( + '`options.returnRuleSetEarly` should be a boolean.', + ); } this.__returnRuleSetEarly = options.returnRuleSetEarly; } @@ -142,7 +150,8 @@ export default class Validator { const allErrors = {}; const modifiedValues = {}; - for (const key in this.__ruleSets) { + // eslint-disable-next-line no-restricted-syntax + for (const key of Object.keys(this.__ruleSets)) { const ruleSet = this.__ruleSets[key]; if (!(ruleSet instanceof RuleSet)) { throw new TypeError( @@ -163,8 +172,7 @@ export default class Validator { } } - if (Object.keys(allErrors).length > 0) - return { values: modifiedValues, errors: allErrors }; + if (Object.keys(allErrors).length > 0) { return { values: modifiedValues, errors: allErrors }; } return { values: modifiedValues, errors: null }; } From aa6f99e78f589527d5c81049136dd6afe6af96f9 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Tue, 19 May 2020 03:38:03 +0530 Subject: [PATCH 09/12] [Fix] Fixed github actions and npm scripts --- .github/workflows/nodejs.yml | 1 + .github/workflows/npmpublish.yml | 1 + package.json | 8 ++++---- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml index 0e6ed86..ed79cee 100644 --- a/.github/workflows/nodejs.yml +++ b/.github/workflows/nodejs.yml @@ -25,6 +25,7 @@ jobs: with: node-version: ${{ matrix.node-version }} - run: npm ci + - run: npm run build:js - run: npm run lint - run: npm test env: diff --git a/.github/workflows/npmpublish.yml b/.github/workflows/npmpublish.yml index f0e5147..8a143bc 100644 --- a/.github/workflows/npmpublish.yml +++ b/.github/workflows/npmpublish.yml @@ -17,6 +17,7 @@ jobs: node-version: 10 registry-url: https://registry.npmjs.org/ - run: npm ci + - run: npm run build:js - run: npm run lint - run: npm test - run: npm publish diff --git a/package.json b/package.json index 34ae174..17ebe0d 100644 --- a/package.json +++ b/package.json @@ -5,14 +5,14 @@ "main": "lib/index.js", "module": "es/index.js", "scripts": { - "build": "npm run build:cjs && npm run build:es && npm run build:docs", "build:cjs": "babel --env-name cjs src --out-dir lib", "build:es": "babel --env-name es src --out-dir es", "build:docs": "./node_modules/.bin/esdoc", + "build:js": "npm run build:cjs && npm run build:es", + "build": "npm run build:js && npm run build:docs", "lint": "eslint src/**/* src/* test/**/* --fix", "test": "mocha", - "dev:test": "mocha", - "pretest": "npm run build" + "dev:test": "npm run build:js && npm run test" }, "devDependencies": { "@babel/cli": "^7.8.4", @@ -36,7 +36,7 @@ }, "husky": { "hooks": { - "pre-commit": "lint-staged && npm run test" + "pre-commit": "npm run build && lint-staged && npm run test" } }, "lint-staged": { From 80156473f8914ff1cfe9273901aefc6ad217eced Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Wed, 20 May 2020 22:24:24 +0530 Subject: [PATCH 10/12] [Fix] Fixed single quote rendering in readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 94839df..f032081 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ The value of the variable `values` and `errors` will be. { "email": [ - { "error": "\'email\' should be a valid email.", + { "error": "'email' should be a valid email.", "validator": "isEmail", "value": "dandaninc.com", "path": "email" @@ -90,7 +90,7 @@ The value of the variable `values` and `errors` will be. "password": [ - { "error": "\'password\' should not be less than 8 characters.", + { "error": "'password' should not be less than 8 characters.", "validator": "isLen", "value": "123456", "path": "password" @@ -130,7 +130,7 @@ Then the returned error message will use the name `Business Email`. { "email": [ - { "error": "\'Business Email\' should be a valid email.", + { "error": "'Business Email' should be a valid email.", "validator": "isEmail", "value": "dandaninc.com", "path": "email" @@ -170,7 +170,7 @@ Then the errors object will be: { "email": [ - { "error": "\'Business Email\' should be a valid email.", + { "error": "'Business Email' should be a valid email.", "validator": "isEmail", "value": "dandaninc.com", "path": "email" From f1e7b7b28539592ed4fb6b56addd77a315b2d89f Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Sat, 23 May 2020 21:12:27 +0530 Subject: [PATCH 11/12] [Doc] Added `isArray` & `isObject` documentation --- README.md | 108 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index f032081..bd4c534 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,13 @@ ## Overview A light weight, powerful, tree shakable javascript schema validator which works on both `Nodejs` and `Browser`. +## Table of Contents +1. [Installation](#installation) +2. [Importing](#importing) +3. [Quick start](#quick-start) +4. [More fetures](#more-fetures) +5. [Reference and API Documentation](#reference-and-api-documentation) + ## Installation ``` npm install shark-validator @@ -46,9 +53,9 @@ The rules we'll use in this example are: const { Validator, RuleSet, isRequired, isString, isLen, isEmail } = require('shark-validator'); const schema = new Validator({ - name: RuleSet.create([new isString(), new isRequired()]), - email: RuleSet.create([new isString(), new isRequired(), new isEmail()]), - password: RuleSet.create([new isString(), new isRequired(), new isLen({ min:8 })]), + name: RuleSet.create([new isRequired(), new isString()]), + email: RuleSet.create([new isRequired(), new isString(), new isEmail()]), + password: RuleSet.create([new isRequired(), new isString(), new isLen({ min:8 })]), }); ``` @@ -111,17 +118,16 @@ The `errors` object contains the following data: - `path`: Path to the value. -### More fetures ------- +## More fetures -#### Custom label +### Custom label You can provide a custom name to a particular key which can be displayed on the error message if the test for that key fails. ##### Example If in the above example we defined the `RuleSet` for the `email` key as: ```js -RuleSet.create([new isString(), new isRequired(), new isEmail()], 'Business Email') +RuleSet.create([new isRequired(), new isString(), new isEmail()], 'Business Email') ``` Then the returned error message will use the name `Business Email`. @@ -141,7 +147,7 @@ Then the returned error message will use the name `Business Email`. } ``` -#### Return early +### Return early If you want to stop the check if one error is found, then you can pass in an additional parameters `returnEarly` and `returnRuleSetEarly` to the `Validator` constructor. If set to true, then the functionality of the two parameters will be: @@ -155,9 +161,9 @@ If the validator is defined as below. ```js const schema = new Validator({ - name: RuleSet.create([new isString(), new isRequired()]), - email: RuleSet.create([new isString(), new isRequired(), new isEmail()]), - password: RuleSet.create([new isString(), new isRequired(), new isLen({ min:8 })]), + name: RuleSet.create([new isRequired(), new isString()]), + email: RuleSet.create([new isRequired(), new isString(), new isEmail()]), + password: RuleSet.create([new isRequired(), new isString(), new isLen({ min:8 })]), }, { returnEarly: true @@ -181,7 +187,7 @@ Then the errors object will be: Notice that no `password` error is returned because the validation stopped when the email failed the test. -#### Custom error message +### Custom error message If you don't like the existing error messages, you can provide custom error messages if a particular rule fails just by adding a parameter `message` to the `Rule` constructor as: ```js @@ -201,8 +207,8 @@ If in the above example we defined the `RuleSet` for the `password` key as: ```js RuleSet.create([ - new isString(), new isRequired(), + new isString(), new isLen({ min:8, message: '%name% must be equal to or greater than %min% charecters.' }) ]) ``` @@ -224,5 +230,77 @@ Then the returned error will use our custom message. } ``` -## Reference and API Documentation for more features -[https://shark.imirshad.com/](https://shark.imirshad.com/) \ No newline at end of file +### Validating objects +You can validate objects with `RuleSet`'s static method `object`. You can also nest mustiple object with this method. + +To achieve this you need to create a seperate schema for the object you are validating and use it inside the `RuleSet`'s `object` method. + +##### Example + +```js +const addressSchema = new Validator({ + city: RuleSet.create([new isRequired(), new isString()]), + state: RuleSet.create([new isRequired(), new isString()]), +}); +``` + +Now you can use this `addressSchema` in your main validation schema. + +```js +const schema = new Validator({ + name: RuleSet.create([new isRequired(), new isString()]), + address: RuleSet.object(addressSchema), +}); + +const { values, errors } = schema.validate(valuesToCheck); +``` + +You can check additional parameters in `RuleSet` documentation. + +### Validating arrays +You can validate arrays with `RuleSet`'s static method `array`. + +You have to define a set of rules to validate each array element and also specify the range of array length. + +##### Example + +```js +const schema = new Validator({ + name: RuleSet.create([new isRequired(), new isString()]), + emails: RuleSet.array([new isRequired(), new isString(), new isEmail()]), +}); + +const { values, errors } = schema.validate(valuesToCheck); +``` + +You can check additional parameters in `RuleSet` documentation. + +### Validating array of objects +You can validate array of objects with `RuleSet`'s static method `arrayOfObject`. You can also nest mustiple object with this method. + +To achieve this you need to create a seperate schema for the object you are validating and use it inside the `RuleSet`'s `arrayOfObject` method. + +##### Example + +```js +const addressSchema = new Validator({ + city: RuleSet.create([new isRequired(), new isString()]), + state: RuleSet.create([new isRequired(), new isString()]), +}); +``` + +Now you can use this `addressSchema` in your main validation schema. + +```js +const schema = new Validator({ + name: RuleSet.create([new isRequired(), new isString()]), + addresses: RuleSet.arrayOfObject(addressSchema), +}); + +const { values, errors } = schema.validate(valuesToCheck); +``` + +You can check additional parameters in `RuleSet` documentation. + +## Reference and API Documentation +Checkout the reference and API documentation for more features. [https://shark.imirshad.com/](https://shark.imirshad.com/) \ No newline at end of file From 8073a0dcac734018069643bbb4348a89ae18eb14 Mon Sep 17 00:00:00 2001 From: Irshad Ansari Date: Sat, 23 May 2020 21:14:21 +0530 Subject: [PATCH 12/12] 1.4.0 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 84ccec2..c678d41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "shark-validator", - "version": "1.3.1", + "version": "1.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 17ebe0d..2cd9a45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "shark-validator", - "version": "1.3.1", + "version": "1.4.0", "description": "A light weight, powerful, tree shakable javascript schema validator which works on both Nodejs and Browser.", "main": "lib/index.js", "module": "es/index.js",