Comparing version 0.8.1 to 0.8.2
@@ -0,2 +1,11 @@ | ||
## 0.8.2 | ||
- the default, default for objects now adds keys for all fields, not just fields with non empty defaults | ||
## 0.8.1 | ||
- bug fix | ||
## 0.8.0 | ||
__breaking__ | ||
- `test` functions are now passed `path` and `context` values along with the field value. Only breaks if using the callback style of defining custom validations | ||
## 0.7.0 | ||
@@ -3,0 +12,0 @@ __breaking__ |
@@ -39,3 +39,4 @@ 'use strict'; | ||
object: {}, | ||
object: { | ||
noUnknown: '${path} field cannot have keys not specified in the objcet shape' }, | ||
@@ -42,0 +43,0 @@ array: { |
@@ -33,3 +33,3 @@ 'use strict'; | ||
if (_.has(options, 'default')) this._default = options['default']; | ||
if (_.has(options, 'default')) this._defaultDefault = options['default']; | ||
@@ -57,3 +57,3 @@ this._type = options.type || 'mixed'; | ||
// undefined isn't merged over, but is a valid value for default | ||
if (schema._default === undefined && _.has(schema, '_default')) next._default = schema._default; | ||
if (schema._default === undefined && _.has(this, '_default')) next._default = schema._default; | ||
@@ -175,3 +175,3 @@ // trim exclusive tests, take the most recent ones | ||
if (arguments.length === 0) { | ||
var dflt = this._default; | ||
var dflt = _.has(this, '_default') ? this._default : this._defaultDefault; | ||
return typeof dflt === 'function' ? dflt.call(this) : cloneDeep(dflt); | ||
@@ -178,0 +178,0 @@ } |
@@ -90,3 +90,3 @@ 'use strict'; | ||
}).test('integer', msg, function (val) { | ||
return val === (val | 0); | ||
return value == null || val === (val | 0); | ||
}); | ||
@@ -93,0 +93,0 @@ }, |
@@ -6,5 +6,7 @@ 'use strict'; | ||
var MixedSchema = require('./mixed'); | ||
var Promise = require('promise/lib/es6-extensions'); | ||
var cloneDeep = require('./util/clone'); | ||
var Promise = require('promise/lib/es6-extensions') | ||
//, Reference = require('./util/Reference') | ||
;var cloneDeep = require('./util/clone'); | ||
var toposort = require('toposort'); | ||
var locale = require('./locale.js').object; | ||
var split = require('property-expr').split; | ||
@@ -38,4 +40,3 @@ var c = require('case'); | ||
var dft = transform(this._nodes, function (obj, key) { | ||
var fieldDft = _this.fields[key]['default'](); | ||
if (fieldDft !== undefined) obj[key] = fieldDft; | ||
obj[key] = _this.fields[key]['default'] ? _this.fields[key]['default']() : undefined; | ||
}, {}); | ||
@@ -47,3 +48,3 @@ | ||
this.transforms.push(function (value) { | ||
this.transforms.push(function coerce(value) { | ||
if (typeof value === 'string') { | ||
@@ -91,5 +92,9 @@ try { | ||
if (exists && fields[prop]) obj[prop] = fields[prop].cast(value[prop], { context: obj });else if (exists && !strip) obj[prop] = cloneDeep(value[prop]);else if (fields[prop]) { | ||
var fieldDefault = fields[prop]['default'](); | ||
if (exists && fields[prop]) { | ||
var fieldSchema = fields[prop] === '$this' ? schema['default'](undefined) : fields[prop]; | ||
obj[prop] = fieldSchema.cast(value[prop], { context: obj }); | ||
} else if (exists && !strip) obj[prop] = cloneDeep(value[prop]);else if (fields[prop]) { | ||
var fieldDefault = fields[prop]['default'] ? fields[prop]['default']() : undefined; | ||
if (fieldDefault !== undefined) obj[prop] = fieldDefault; | ||
@@ -103,2 +108,3 @@ } | ||
// The issue with this is that there are two phases of validating a schema, transformation, and validation. They both work by processing a stack of transforms and validations, it is a very generic strategy which helps make yup a good bit smaller then joi and a lot more flexible in terms of doing custom stuff. The down side is that it doesn't leave a lot of room for tiny tweaks like this. `stripUnknown` is a transform | ||
_validate: function _validate(_value, _opts, _state) { | ||
@@ -126,3 +132,3 @@ var errors = [], | ||
var result = schema._nodes.map(function (key) { | ||
var field = schema.fields[key], | ||
var field = schema.fields[key] === '$this' ? schema : schema.fields[key], | ||
path = (_state.path ? _state.path + '.' : '') + key; | ||
@@ -193,2 +199,19 @@ | ||
noUnknown: function noUnknown(noAllow, message) { | ||
if (typeof noAllow === 'string') message = noAllow, noAllow = true; | ||
var next = this.test({ | ||
name: 'noUnknown', | ||
exclusive: true, | ||
message: message || locale.noUnknown, | ||
test: function test(value) { | ||
return value == null || !noAllow || unknown(this, value).length === 0; | ||
} | ||
}); | ||
if (!!noAllow) this._options.stripUnknown = true; | ||
return next; | ||
}, | ||
camelcase: function camelcase() { | ||
@@ -211,2 +234,9 @@ return this.transform(function (obj) { | ||
function unknown(ctx, value) { | ||
var known = Object.keys(ctx.fields); | ||
return Object.keys(value).filter(function (key) { | ||
return known.indexOf(key) === -1; | ||
}); | ||
} | ||
function sortFields(fields) { | ||
@@ -221,3 +251,3 @@ var excludes = arguments[1] === undefined ? [] : arguments[1]; | ||
fields[key]._deps.forEach(function (node) { | ||
fields[key]._deps && fields[key]._deps.forEach(function (node) { | ||
//eslint-disable-line no-loop-func | ||
@@ -224,0 +254,0 @@ node = split(node)[0]; |
{ | ||
"name": "yup", | ||
"version": "0.8.1", | ||
"version": "0.8.2", | ||
"description": "Dead simple Object schema validation", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -280,11 +280,11 @@ Yup | ||
For more advanced validations you can use the alternate signature to provide more options (see below): | ||
the `test` function is called with the current `value`, along with `path` and `context` if they exist. For more advanced validations you can use the alternate signature to provide more options (see below): | ||
```js | ||
var jimmySchema = yup.string() | ||
.test('is-jimmy', '${path} is not Jimmy', value => value=jimmy); | ||
.test('is-jimmy', '${path} is not Jimmy', value => value === 'jimmy'); | ||
// or make it async by returning a promise | ||
var asyncJimmySchema = yup.string() | ||
.test('is-jimmy', '${path} is not Jimmy', function (value){ | ||
.test('is-jimmy', '${path} is not Jimmy', function (value, path, context){ | ||
return fetch('/is-jimmy/' + value) | ||
@@ -298,3 +298,3 @@ .then(response => response.responseText === 'true') | ||
function test(value, done){ | ||
function test(value, path, context, done){ | ||
// error argument is for exceptions, not an failed tests | ||
@@ -301,0 +301,0 @@ done(null, value === 'jimmy') |
@@ -39,3 +39,5 @@ | ||
object: {}, | ||
object: { | ||
noUnknown: '${path} field cannot have keys not specified in the objcet shape', | ||
}, | ||
@@ -42,0 +44,0 @@ array: { |
@@ -31,3 +31,3 @@ 'use strict'; | ||
if (_.has(options, 'default')) | ||
this._default = options.default | ||
this._defaultDefault = options.default | ||
@@ -57,3 +57,3 @@ this._type = options.type || 'mixed' | ||
// undefined isn't merged over, but is a valid value for default | ||
if ( schema._default === undefined && _.has(schema, '_default') ) | ||
if( schema._default === undefined && _.has(this, '_default') ) | ||
next._default = schema._default | ||
@@ -83,2 +83,3 @@ | ||
if( value === undefined && _.has(this, '_default') ) | ||
@@ -174,3 +175,3 @@ value = this.default() | ||
if( arguments.length === 0){ | ||
var dflt = this._default | ||
var dflt = _.has(this, '_default') ? this._default : this._defaultDefault | ||
return typeof dflt === 'function' | ||
@@ -177,0 +178,0 @@ ? dflt.call(this) : cloneDeep(dflt) |
@@ -61,3 +61,3 @@ 'use strict'; | ||
.transform( v => v != null ? (v | 0) : v) | ||
.test('integer', msg, val => val === (val | 0)) | ||
.test('integer', msg, val => value == null || val === (val | 0)) | ||
}, | ||
@@ -64,0 +64,0 @@ |
'use strict'; | ||
var MixedSchema = require('./mixed') | ||
, Promise = require('promise/lib/es6-extensions') | ||
//, Reference = require('./util/Reference') | ||
, cloneDeep = require('./util/clone') | ||
, toposort = require('toposort') | ||
, locale = require('./locale.js').object | ||
, split = require('property-expr').split | ||
@@ -29,4 +31,3 @@ , c = require('case') | ||
var dft = transform(this._nodes, (obj, key) => { | ||
var fieldDft = this.fields[key].default() | ||
if(fieldDft !== undefined ) obj[key] = fieldDft | ||
obj[key] = this.fields[key].default ? this.fields[key].default() : undefined | ||
}, {}) | ||
@@ -38,3 +39,3 @@ | ||
this.transforms.push(function (value) { | ||
this.transforms.push(function coerce(value) { | ||
if (typeof value === 'string') { | ||
@@ -81,4 +82,7 @@ try { | ||
if( exists && fields[prop] ) | ||
obj[prop] = fields[prop].cast(value[prop], { context: obj }) | ||
if( exists && fields[prop] ){ | ||
var fieldSchema = fields[prop] === '$this' ? schema.default(undefined) : fields[prop] | ||
obj[prop] = fieldSchema.cast(value[prop], { context: obj }) | ||
} | ||
@@ -89,3 +93,3 @@ else if( exists && !strip) | ||
else if(fields[prop]){ | ||
var fieldDefault = fields[prop].default() | ||
var fieldDefault = fields[prop].default ? fields[prop].default() : undefined | ||
@@ -102,2 +106,3 @@ if ( fieldDefault !== undefined) | ||
// The issue with this is that there are two phases of validating a schema, transformation, and validation. They both work by processing a stack of transforms and validations, it is a very generic strategy which helps make yup a good bit smaller then joi and a lot more flexible in terms of doing custom stuff. The down side is that it doesn't leave a lot of room for tiny tweaks like this. `stripUnknown` is a transform | ||
_validate(_value, _opts, _state) { | ||
@@ -125,5 +130,5 @@ var errors = [] | ||
let result = schema._nodes.map(function(key){ | ||
var field = schema.fields[key] | ||
var field = schema.fields[key] === '$this' ? schema : schema.fields[key] | ||
, path = (_state.path ? (_state.path + '.') : '') + key; | ||
return field._validate(value[key] | ||
@@ -183,2 +188,21 @@ , _opts | ||
noUnknown(noAllow, message) { | ||
if ( typeof noAllow === 'string') | ||
message = noAllow, noAllow = true; | ||
var next = this.test({ | ||
name: 'noUnknown', | ||
exclusive: true, | ||
message: message || locale.noUnknown, | ||
test(value) { | ||
return value == null || !noAllow || unknown(this, value).length === 0 | ||
} | ||
}) | ||
if ( !!noAllow ) | ||
this._options.stripUnknown = true | ||
return next | ||
}, | ||
camelcase(){ | ||
@@ -195,2 +219,8 @@ return this.transform(obj => obj == null ? obj | ||
function unknown(ctx, value) { | ||
var known = Object.keys(ctx.fields) | ||
return Object.keys(value) | ||
.filter(key => known.indexOf(key) === -1) | ||
} | ||
function sortFields(fields, excludes = []){ | ||
@@ -202,11 +232,12 @@ var edges = [], nodes = [] | ||
fields[key]._deps.forEach(node => { //eslint-disable-line no-loop-func | ||
node = split(node)[0] | ||
if ( !~nodes.indexOf(node) ) | ||
nodes.push(node) | ||
fields[key]._deps && | ||
fields[key]._deps.forEach(node => { //eslint-disable-line no-loop-func | ||
node = split(node)[0] | ||
if ( !~nodes.indexOf(node) ) | ||
nodes.push(node) | ||
if ( !~excludes.indexOf(`${key}-${node}`) ) | ||
edges.push([key, node]) | ||
}) | ||
if ( !~excludes.indexOf(`${key}-${node}`) ) | ||
edges.push([key, node]) | ||
}) | ||
} | ||
@@ -213,0 +244,0 @@ |
@@ -129,3 +129,3 @@ 'use strict'; | ||
it.only('tests should receive path and context', function(done){ | ||
it('tests should receive path and context', function(done){ | ||
var inst = object({ | ||
@@ -154,3 +154,3 @@ other: mixed(), | ||
}) | ||
.test('name', 'test b', function(val, context, done){ | ||
.test('name', 'test b', function(val, path, context, done){ | ||
process.nextTick(function(){ | ||
@@ -157,0 +157,0 @@ done(null, val !== 'jim') |
@@ -91,6 +91,2 @@ 'use strict'; | ||
// return inst.validate().should.be.rejected.then(function(err){ | ||
// console.log(err) | ||
// }) | ||
return inst.validate(obj).should.be.rejected | ||
@@ -131,3 +127,3 @@ .then(function(err){ | ||
}) | ||
.default().should.eql({ nest: { str: 'hi' } }) | ||
.default().should.eql({ nest: { str: 'hi' }, str: undefined }) | ||
@@ -147,3 +143,3 @@ object({ | ||
}) | ||
.default()).to.eql(undefined) | ||
.default()).to.eql({ nest: { str: undefined }, str: undefined }) | ||
}) | ||
@@ -156,10 +152,5 @@ | ||
// return inst.isValid({}).should.be.fulfilled.then(function(err){ | ||
// console.log(err) | ||
// }) | ||
return Promise.all([ | ||
// inst.validate({}).should.be.rejected.then(function(err){ | ||
// console.log(err) | ||
// }), | ||
inst.isValid({}).should.eventually.equal(true), | ||
@@ -172,2 +163,16 @@ | ||
it('should work with noUnknown', function(){ | ||
var inst = object().shape({ | ||
prop: mixed(), | ||
other: mixed() | ||
}) | ||
.noUnknown('hi') | ||
return inst.validate({ extra: 'field' }).should.be.rejected | ||
.then(function(err){ | ||
err.errors[0].should.equal('hi') | ||
}) | ||
}) | ||
it('should handle custom validation', function(){ | ||
@@ -189,2 +194,20 @@ var inst = object().shape({ | ||
it('should allow nesting with "$this"', function(){ | ||
var inst = object().shape({ | ||
child: "$this", | ||
other: string().required('required!') | ||
}) | ||
inst.default().should.eql({ child: undefined, other: undefined }) | ||
return Promise.all([ | ||
inst.validate({ child: { other: undefined }, other: 'ff' }).should.be.rejected | ||
.then(function(err){ | ||
err.errors[0].should.equal('required!') | ||
}), | ||
]) | ||
}) | ||
it('should respect abortEarly', function(){ | ||
@@ -278,3 +301,13 @@ var inst = object({ | ||
it('should use correct default when concating', function(){ | ||
var inst = object().shape({ | ||
other: bool(), | ||
}) | ||
.default(undefined) | ||
chai.expect(inst.concat(object()).default()).to.equal(undefined) | ||
chai.expect(inst.concat(object().default({})).default()).to.eql({}) | ||
}) | ||
it('should handle nested conditionals', function(){ | ||
@@ -288,2 +321,3 @@ var countSchema = number().when('isBig', { is: true, then: number().min(5) }) | ||
}) | ||
.default(undefined) | ||
.when('other', { is: true, then: object().required() }) | ||
@@ -290,0 +324,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
157214
53
3401