From 403a7a88033a9117ae4d1168763d5d72221a9d82 Mon Sep 17 00:00:00 2001 From: Jason Quense Date: Mon, 31 Jul 2017 11:15:21 -0400 Subject: [PATCH] Remove set and use lodash's clone --- README.md | 11 +++- src/mixed.js | 38 ++++++------ src/util/clone.js | 69 ---------------------- src/util/set.js | 33 ----------- src/util/sortFields.js | 3 +- test/mixed.js | 3 +- test/yup.js | 129 ----------------------------------------- 7 files changed, 29 insertions(+), 257 deletions(-) delete mode 100644 src/util/clone.js delete mode 100644 src/util/set.js diff --git a/README.md b/README.md index 7f9aaeed0..3d757bd21 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,13 @@ json separate from validating it, via the `cast` method. npm install -S yup ``` -**Yup always relies on the `Promise` global object to handle asynchronous values. -If your environment doesn't have Promise, you'll need to include a polyfill.** +Yup always relies on the `Promise` global object to handle asynchronous values as well `Set`. +For browsers that do not support these, you'll need to include a polyfill, such as core-js: + +```js +import 'core-js/es6/promise'; +import 'core-js/es6/set'; +``` ## Usage @@ -734,7 +739,7 @@ v.isValid('nope').should.eventually().equal(false) #### `string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema` -An alternate signature for `string.matches` with an options object. `excludeEmptyString`, when true, +An alternate signature for `string.matches` with an options object. `excludeEmptyString`, when true, short circuits the regex test when the value is an empty string ```javascript diff --git a/src/mixed.js b/src/mixed.js index c6fa6ceda..7ae2f4bef 100644 --- a/src/mixed.js +++ b/src/mixed.js @@ -1,13 +1,14 @@ import has from 'lodash/has'; +import cloneDeepWith from 'lodash/cloneDeepWith'; +import toArray from 'lodash/toArray'; import { mixed as locale } from './locale'; import Condition from './Condition'; import runValidations from './util/runValidations'; import merge from './util/merge'; +import isSchema from './util/isSchema'; import isAbsent from './util/isAbsent'; -import cloneDeep from './util/clone'; import createValidation from './util/createValidation'; -import BadSet from './util/set'; import Ref from './Reference'; let notEmpty = value => !isAbsent(value); @@ -40,8 +41,8 @@ export default function SchemaType(options = {}){ this._conditions = [] this._options = { abortEarly: true, recursive: true } this._exclusive = Object.create(null) - this._whitelist = new BadSet() - this._blacklist = new BadSet() + this._whitelist = new Set() + this._blacklist = new Set() this.tests = [] this.transforms = [] @@ -65,7 +66,12 @@ SchemaType.prototype = { if (this._mutate) return this; - return cloneDeep(this); + // if the nested value is a schema we can skip cloning, since + // they are already immutable + return cloneDeepWith(this, (value) => { + if (isSchema(value) && value !== this) + return value + }); }, label(label) { @@ -231,7 +237,7 @@ SchemaType.prototype = { return typeof defaultValue === 'function' ? defaultValue.call(this) - : cloneDeep(defaultValue) + : cloneDeepWith(defaultValue) } var next = this.clone() @@ -346,7 +352,8 @@ SchemaType.prototype = { var next = this.clone(); enums.forEach(val => { - next._blacklist.delete(val) + if (next._blacklist.has(val)) + next._blacklist.delete(val) next._whitelist.add(val) }) @@ -355,10 +362,10 @@ SchemaType.prototype = { name: 'oneOf', test(value) { let valids = this.schema._whitelist - if (valids.length && !(value === undefined || valids.has(value))) + if (valids.size && !(value === undefined || valids.has(value))) return this.createError({ params: { - values: valids.values().join(', ') + values: toArray(valids).join(', ') } }) return true @@ -381,10 +388,10 @@ SchemaType.prototype = { name: 'notOneOf', test(value) { let invalids = this.schema._blacklist - if (invalids.length && invalids.has(value)) + if (invalids.size && invalids.has(value)) return this.createError({ params: { - values: invalids.values().join(', ') + values: toArray(invalids).join(', ') } }) return true @@ -428,12 +435,3 @@ Object.keys(aliases).forEach(method => { SchemaType.prototype[alias] = SchemaType.prototype[method] ) }) - -function nodeify(promise, cb){ - if (typeof cb !== 'function') return promise - - promise.then( - val => cb(null, val), - err => cb(err) - ) -} diff --git a/src/util/clone.js b/src/util/clone.js deleted file mode 100644 index 381cd1207..000000000 --- a/src/util/clone.js +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright (c) 2011-2014, Walmart and other contributors. -// Copyright (c) 2011, Yahoo Inc. -// All rights reserved. https://github.com/hapijs/hoek/blob/master/LICENSE - -import isSchema from './isSchema'; - -export default function clone(obj, seen) { - var isFirst = !seen - , isImmutable = isSchema(obj) && !isFirst - - if (typeof obj !== 'object' || obj === null || isImmutable) - return obj; - - seen = seen || { orig: [], copy: [] }; - - var lookup = seen.orig.indexOf(obj); - - if (lookup !== -1) - return seen.copy[lookup]; - - var newObj; - var cloneDeep = false; - - if (!Array.isArray(obj)) { - if (obj instanceof Date) { - newObj = new Date(obj.getTime()); - } - else if (obj instanceof RegExp) { - newObj = new RegExp(obj); - } - else { - var proto = Object.getPrototypeOf(obj); - - if (proto !== null && !proto) { - newObj = obj; - } - else { - newObj = Object.create(proto); - cloneDeep = true; - } - } - } - else { - newObj = []; - cloneDeep = true; - } - - seen.orig.push(obj); - seen.copy.push(newObj); - - if (cloneDeep) { - var keys = Object.getOwnPropertyNames(obj); - - for (var i = 0, il = keys.length; i < il; ++i) { - var key = keys[i]; - - var descriptor = Object.getOwnPropertyDescriptor(obj, key); - - if (descriptor && (descriptor.get || descriptor.set)) { - Object.defineProperty(newObj, key, descriptor); - } - else { - newObj[key] = clone(obj[key], seen); - } - } - } - - return newObj; -} diff --git a/src/util/set.js b/src/util/set.js deleted file mode 100644 index 6638ccb5b..000000000 --- a/src/util/set.js +++ /dev/null @@ -1,33 +0,0 @@ -import has from 'lodash/has'; - -export default class BadSet { - - constructor(){ - this._map = Object.create(null) - this._refs = Object.create(null) - } - - values(){ - return Object.keys(this._map).map(v => this._map[v]) - } - - get length(){ - return Object.keys(this._map).length - } - - add(item){ - this._map[stringify(item)] = item - } - - delete(item){ - delete this._map[stringify(item)] - } - - has(item){ - return has(this._map, stringify(item)) - } -} - -function stringify(item){ - return JSON.stringify(item) -} diff --git a/src/util/sortFields.js b/src/util/sortFields.js index 5a429cfce..0a88ca6bf 100644 --- a/src/util/sortFields.js +++ b/src/util/sortFields.js @@ -1,6 +1,7 @@ -import toposort from 'toposort'; import has from 'lodash/has'; +import toposort from 'toposort'; import { split } from 'property-expr'; + import Ref from '../Reference'; import isSchema from './isSchema'; diff --git a/test/mixed.js b/test/mixed.js index 897d15eb1..cd2de5d68 100644 --- a/test/mixed.js +++ b/test/mixed.js @@ -1,12 +1,11 @@ import mixed from '../src/mixed'; import object from '../src/object'; import string from '../src/string'; -import ValidationError from '../src/ValidationError'; import reach from '../src/util/reach'; let noop = () => {} -describe( 'Mixed Types ', function(){ +describe('Mixed Types ', () => { it('should be immutable', () => { let inst = mixed(), next; diff --git a/test/yup.js b/test/yup.js index cc4537ac8..64e7111fb 100644 --- a/test/yup.js +++ b/test/yup.js @@ -1,5 +1,4 @@ import reach from '../src/util/reach'; -import BadSet from '../src/util/set'; import merge from '../src/util/merge'; import { settled } from '../src/util/runValidations'; @@ -150,132 +149,4 @@ describe('Yup', function(){ err.message.should.match(/must be a `number` type/) }) - describe('BadSet', function(){ - it('should preserve primitive types', function(){ - var set = new BadSet() - - set.add(2) - set.has(2).should.be.true() - set.has('2').should.be.false() - set.values().should.eql([2]) - - set.add('3') - set.has('3').should.be.true() - set.has(3).should.be.false() - set.values().should.eql([2, '3']) - - set.add(false) - set.has(false).should.be.true() - set.has('false').should.be.false() - set.values().should.eql([2, '3', false]) - - set.add('true') - set.has('true').should.be.true() - set.has(true).should.be.false() - set.values().should.eql([2, '3', false, 'true']) - - set.add(null) - set.has(null).should.be.true() - set.has('null').should.be.false() - set.values().should.eql([2, '3', false, 'true', null]) - - set.add(undefined) - set.has(undefined).should.be.true() - set.has('undefined').should.be.false() - set.values().should.eql([2, '3', false, 'true', null, undefined]) - }) - - it('should perform value equality for arrays and objects', function(){ - var set = new BadSet() - - var oneTwoThree = [1, '2', 3] - set.add(oneTwoThree) - set.has(oneTwoThree).should.be.true() - set.has([1, '2', 3]).should.be.true() - set.values().should.eql([[1, '2', 3]]) - set.length.should.equal(1) - - set.add([1, '2', 3]) - set.has(oneTwoThree).should.be.true() - set.has([1, '2', 3]).should.be.true() - set.values().should.eql([[1, '2', 3]]) - set.length.should.equal(1) - - var aOnebTwo = { a: 1, b: '2'} - set.add(aOnebTwo) - set.has(aOnebTwo).should.be.true() - set.has({ a: 1, b: '2'}).should.be.true() - set.values().should.eql([[1, '2', 3], { a: 1, b: '2'}]) - set.length.should.equal(2) - - set.add({ a: 1, b: '2'}) - set.has(aOnebTwo).should.be.true() - set.has({ a: 1, b: '2'}).should.be.true() - set.values().should.eql([[1, '2', 3], { a: 1, b: '2'}]) - set.length.should.equal(2) - }) - - it('should perform value equality for dates', function(){ - var set = new BadSet() - - var someDate = new Date('12-12-12') - set.add(someDate) - set.has(someDate).should.be.true() - set.has(new Date('12-12-12')).should.be.true() - set.values().should.eql([new Date('12-12-12')]) - set.length.should.equal(1) - - set.add(new Date('12-12-12')) - set.has(someDate).should.be.true() - set.has(new Date('12-12-12')).should.be.true() - set.values().should.eql([new Date('12-12-12')]) - set.length.should.equal(1) - }) - - it('should not contain the same value twice', function(){ - var set = new BadSet() - - var arrayWithDuplicates = [ - 1, - 2, - 3, - '2', - 3, - 'abc', - new Date('12-12-12'), - 4, - new Date('12-12-12') - ] - - arrayWithDuplicates.forEach(item => set.add(item)) - set.values().sort().should.eql( - [1, 2, 3, '2', 'abc', new Date('12-12-12'), 4].sort()) - }) - - it('should delete values', function(){ - var set = new BadSet() - - set.add(2) - set.has(2).should.be.true() - set.length.should.equal(1) - set.values().should.eql([2]) - - set.delete('2') - set.has(2).should.be.true() - set.length.should.equal(1) - set.values().should.eql([2]) - - set.add('3') - set.has(2).should.be.true() - set.has('3').should.be.true() - set.length.should.equal(2) - set.values().should.eql([2, '3']) - - set.delete('3') - set.has(2).should.be.true() - set.has('3').should.be.false() - set.length.should.equal(1) - set.values().should.eql([2]) - }) - }) })