diff --git a/lib/any.js b/lib/any.js index addfe8be7..6537ff496 100644 --- a/lib/any.js +++ b/lib/any.js @@ -218,8 +218,14 @@ module.exports = internals.Any = class { raw(isRaw) { + const value = isRaw === undefined ? true : isRaw; + + if (this._flags.raw === value) { + return this; + } + const obj = this.clone(); - obj._flags.raw = isRaw === undefined ? true : isRaw; + obj._flags.raw = value; return obj; } @@ -270,6 +276,10 @@ module.exports = internals.Any = class { required() { + if (this._flags.presence === 'required') { + return this; + } + const obj = this.clone(); obj._flags.presence = 'required'; return obj; @@ -277,6 +287,10 @@ module.exports = internals.Any = class { optional() { + if (this._flags.presence === 'optional') { + return this; + } + const obj = this.clone(); obj._flags.presence = 'optional'; return obj; @@ -285,6 +299,10 @@ module.exports = internals.Any = class { forbidden() { + if (this._flags.presence === 'forbidden') { + return this; + } + const obj = this.clone(); obj._flags.presence = 'forbidden'; return obj; @@ -293,6 +311,10 @@ module.exports = internals.Any = class { strip() { + if (this._flags.strip) { + return this; + } + const obj = this.clone(); obj._flags.strip = true; return obj; diff --git a/lib/array.js b/lib/array.js index 4ebfbfb1f..ae83da2c0 100644 --- a/lib/array.js +++ b/lib/array.js @@ -529,15 +529,27 @@ internals.Array = class extends Any { sparse(enabled) { + const value = enabled === undefined ? true : !!enabled; + + if (this._flags.sparse === value) { + return this; + } + const obj = this.clone(); - obj._flags.sparse = enabled === undefined ? true : !!enabled; + obj._flags.sparse = value; return obj; } single(enabled) { + const value = enabled === undefined ? true : !!enabled; + + if (this._flags.single === value) { + return this; + } + const obj = this.clone(); - obj._flags.single = enabled === undefined ? true : !!enabled; + obj._flags.single = value; return obj; } diff --git a/lib/binary.js b/lib/binary.js index 6d9851f55..d064fd657 100644 --- a/lib/binary.js +++ b/lib/binary.js @@ -43,6 +43,10 @@ internals.Binary = class extends Any { Hoek.assert(Buffer.isEncoding(encoding), 'Invalid encoding:', encoding); + if (this._flags.encoding === encoding) { + return this; + } + const obj = this.clone(); obj._flags.encoding = encoding; return obj; diff --git a/lib/boolean.js b/lib/boolean.js index 8bc02bb13..ae34259d1 100644 --- a/lib/boolean.js +++ b/lib/boolean.js @@ -76,13 +76,13 @@ internals.Boolean = class extends Any { const insensitive = enabled === undefined ? true : !!enabled; - if (insensitive !== this._flags.insensitive) { - const obj = this.clone(); - obj._flags.insensitive = insensitive; - return obj; + if (this._flags.insensitive === insensitive) { + return this; } - return this; + const obj = this.clone(); + obj._flags.insensitive = insensitive; + return obj; } describe() { diff --git a/lib/date.js b/lib/date.js index 53f00b425..e5ad56a06 100644 --- a/lib/date.js +++ b/lib/date.js @@ -97,6 +97,10 @@ internals.Date = class extends Any { iso() { + if (this._flags.format === internals.isoDate) { + return this; + } + const obj = this.clone(); obj._flags.format = internals.isoDate; return obj; @@ -109,6 +113,10 @@ internals.Date = class extends Any { const allowed = ['javascript', 'unix']; Hoek.assert(allowed.indexOf(type) !== -1, '"type" must be one of "' + allowed.join('", "') + '"'); + if (this._flags.timestamp === type) { + return this; + } + const obj = this.clone(); obj._flags.timestamp = type; obj._flags.multiplier = type === 'unix' ? 1000 : 1; diff --git a/lib/object.js b/lib/object.js index 24607d21c..864734a00 100644 --- a/lib/object.js +++ b/lib/object.js @@ -325,8 +325,14 @@ internals.Object = class extends Any { unknown(allow) { + const value = allow !== false; + + if (this._flags.allowUnknown === value) { + return this; + } + const obj = this.clone(); - obj._flags.allowUnknown = (allow !== false); + obj._flags.allowUnknown = value; return obj; } diff --git a/lib/string.js b/lib/string.js index b7a526478..51995da60 100644 --- a/lib/string.js +++ b/lib/string.js @@ -67,6 +67,10 @@ internals.String = class extends Any { insensitive() { + if (this._flags.insensitive) { + return this; + } + const obj = this.clone(); obj._flags.insensitive = true; return obj; @@ -510,8 +514,14 @@ internals.String = class extends Any { truncate(enabled) { + const value = enabled === undefined ? true : !!enabled; + + if (this._flags.truncate === value) { + return this; + } + const obj = this.clone(); - obj._flags.truncate = enabled === undefined ? true : !!enabled; + obj._flags.truncate = value; return obj; } diff --git a/test/any.js b/test/any.js index 6ab0dc0f5..4d6bd6471 100644 --- a/test/any.js +++ b/test/any.js @@ -265,6 +265,13 @@ describe('any', () => { done(); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.any().raw(); + expect(schema.raw()).to.shallow.equal(schema); + done(); + }); }); describe('default()', () => { @@ -773,6 +780,16 @@ describe('any', () => { }); }); + describe('required', () => { + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.any().required(); + expect(schema.required()).to.shallow.equal(schema); + done(); + }); + }); + describe('optional()', () => { it('validates optional with default required', (done) => { @@ -794,6 +811,13 @@ describe('any', () => { [{ b: 5 }, false, null, 'child "a" fails because ["a" is required]'] ], done); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.any().optional(); + expect(schema.optional()).to.shallow.equal(schema); + done(); + }); }); describe('forbidden()', () => { @@ -814,6 +838,13 @@ describe('any', () => { [{ b: null }, false, null, 'child "b" fails because ["b" is not allowed]'] ], done); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.any().forbidden(); + expect(schema.forbidden()).to.shallow.equal(schema); + done(); + }); }); describe('strip()', () => { @@ -841,6 +872,13 @@ describe('any', () => { done(); }); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.any().strip(); + expect(schema.strip()).to.shallow.equal(schema); + done(); + }); }); describe('description()', () => { diff --git a/test/array.js b/test/array.js index 4f81e9bab..78de025fe 100644 --- a/test/array.js +++ b/test/array.js @@ -1145,6 +1145,13 @@ describe('array', () => { }); done(); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.array().sparse(); + expect(schema.sparse()).to.shallow.equal(schema); + done(); + }); }); describe('single()', () => { @@ -1223,6 +1230,13 @@ describe('array', () => { }); done(); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.array().single(); + expect(schema.single()).to.shallow.equal(schema); + done(); + }); }); describe('options()', () => { diff --git a/test/binary.js b/test/binary.js index e8fa99f26..6d733ebd2 100644 --- a/test/binary.js +++ b/test/binary.js @@ -105,6 +105,13 @@ describe('binary', () => { }).to.throw('Invalid encoding: base6'); done(); }); + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.binary().encoding('base64'); + expect(schema.encoding('base64')).to.shallow.equal(schema); + done(); + }); }); describe('min()', () => { diff --git a/test/date.js b/test/date.js index d48e18063..aa8b2500f 100644 --- a/test/date.js +++ b/test/date.js @@ -300,6 +300,13 @@ describe('date', () => { describe('iso()', () => { + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.date().iso(); + expect(schema.iso()).to.shallow.equal(schema); + done(); + }); + it('validates isoDate', (done) => { Helper.validate(Joi.date().iso(), [ @@ -363,6 +370,13 @@ describe('date', () => { describe('timestamp()', () => { + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.date().timestamp('unix'); + expect(schema.timestamp('unix')).to.shallow.equal(schema); + done(); + }); + it('validates javascript timestamp', (done) => { const now = new Date(); diff --git a/test/object.js b/test/object.js index 24edfbb74..cbb9b05eb 100644 --- a/test/object.js +++ b/test/object.js @@ -558,6 +558,13 @@ describe('object', () => { describe('unknown()', () => { + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.object().unknown(); + expect(schema.unknown()).to.shallow.equal(schema); + done(); + }); + it('allows local unknown without applying to children', (done) => { const schema = Joi.object({ diff --git a/test/string.js b/test/string.js index bd0d2e892..b61055401 100644 --- a/test/string.js +++ b/test/string.js @@ -52,6 +52,16 @@ describe('string', () => { ], done); }); + describe('insensitive', () => { + + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.string().insensitive(); + expect(schema.insensitive()).to.shallow.equal(schema); + done(); + }); + }); + describe('valid()', () => { it('validates case sensitive values', (done) => { @@ -1995,6 +2005,13 @@ describe('string', () => { describe('truncate()', () => { + it('avoids unnecessary cloning when called twice', (done) => { + + const schema = Joi.string().truncate(); + expect(schema.truncate()).to.shallow.equal(schema); + done(); + }); + it('switches the truncate flag', (done) => { const schema = Joi.string().truncate();