Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jquense/yup
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4c1099b15064239125d185df5362b4eb9fa77ac3
Choose a base ref
...
head repository: jquense/yup
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a1cb0ac30c1bb59a357b26b5d11af34505cf59a9
Choose a head ref
  • 14 commits
  • 13 files changed
  • 8 contributors

Commits on Mar 6, 2020

  1. readme

    jquense committed Mar 6, 2020
    Copy the full SHA
    e87b9a9 View commit details

Commits on Mar 13, 2020

  1. Copy the full SHA
    ad5d015 View commit details

Commits on Apr 13, 2020

  1. Copy the full SHA
    48ac4c3 View commit details

Commits on Apr 20, 2020

  1. fix: array reaching

    jquense committed Apr 20, 2020
    Copy the full SHA
    81e4058 View commit details
  2. feat: make schema.type and array.innerType public API's

    Also simplify reach/getIn logic
    jquense committed Apr 20, 2020
    Copy the full SHA
    8f00d50 View commit details
  3. Publish v0.28.4

    jquense committed Apr 20, 2020
    Copy the full SHA
    8cb2586 View commit details

Commits on Apr 23, 2020

  1. fix: allow passing of function to .matches() options/message param (#850

    )
    
    * Allow passing of function to .matches()
    
    * No strict type checking
    KeelanM90 authored Apr 23, 2020
    Copy the full SHA
    16efe88 View commit details

Commits on Apr 24, 2020

  1. Copy the full SHA
    ccb7c7d View commit details

Commits on Apr 29, 2020

  1. docs: why does InferType() not default to nonRequired() (#805)

    * Why does InferType not default to nonRequired()
    
    * Added strictNullChecks requirements
    
    * Update README.md
    
    Co-Authored-By: Jason Quense <monastic.panic@gmail.com>
    
    * Update README.md
    
    Co-Authored-By: Jason Quense <monastic.panic@gmail.com>
    
    Co-authored-by: Jason Quense <monastic.panic@gmail.com>
    mauricedb and jquense authored Apr 29, 2020
    Copy the full SHA
    78038ad View commit details
  2. Configure Renovate (#861)

    * chore(deps): add renovate.json
    
    * Update renovate.json
    
    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    Co-authored-by: Jason Quense <monastic.panic@gmail.com>
    3 people authored Apr 29, 2020
    Copy the full SHA
    5aa88b4 View commit details

Commits on Apr 30, 2020

  1. chore(deps): update dependency rollup-plugin-filesize to v7 (#865)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Apr 30, 2020
    Copy the full SHA
    cd7e27c View commit details
  2. chore(deps): update dependency rollup to v2 (#864)

    Co-authored-by: Renovate Bot <bot@renovateapp.com>
    renovate[bot] and renovate-bot authored Apr 30, 2020
    Copy the full SHA
    dc84666 View commit details
  3. chore: bump deps

    jquense committed Apr 30, 2020
    Copy the full SHA
    63c9785 View commit details
  4. Publish v0.28.5

    jquense committed Apr 30, 2020
    Copy the full SHA
    a1cb0ac View commit details
Showing with 1,171 additions and 1,319 deletions.
  1. +29 −0 CHANGELOG.md
  2. +17 −1 README.md
  3. +21 −21 package.json
  4. +3 −0 renovate.json
  5. +8 −6 src/array.js
  6. +1 −1 src/locale.js
  7. +3 −3 src/mixed.js
  8. +5 −1 src/object.js
  9. +2 −1 src/string.js
  10. +24 −32 src/util/reach.js
  11. +1 −1 test/object.js
  12. +51 −19 test/yup.js
  13. +1,006 −1,233 yarn.lock
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,32 @@
## [0.28.5](https://github.com/jquense/yup/compare/v0.28.4...v0.28.5) (2020-04-30)


### Bug Fixes

* allow passing of function to .matches() options/message param ([#850](https://github.com/jquense/yup/issues/850)) ([16efe88](https://github.com/jquense/yup/commit/16efe88a8953db60438f77f43bd5bf614079803d))
* bug in object.noUnknown for nullish values https://github.com/jquense/yup/issues/854 ([#855](https://github.com/jquense/yup/issues/855)) ([ccb7c7d](https://github.com/jquense/yup/commit/ccb7c7d3c450537dffbb7d589e3111fc1f9a86fd))





## [0.28.4](https://github.com/jquense/yup/compare/v0.28.3...v0.28.4) (2020-04-20)


### Bug Fixes

* array reaching ([81e4058](https://github.com/jquense/yup/commit/81e4058))


### Features

* make schema.type and array.innerType public API's ([8f00d50](https://github.com/jquense/yup/commit/8f00d50))
* provide keys in default noUnknown message ([#579](https://github.com/jquense/yup/issues/579)) ([ad5d015](https://github.com/jquense/yup/commit/ad5d015))





## [0.28.3](https://github.com/jquense/yup/compare/v0.28.2...v0.28.3) (2020-03-06)


18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Install](#install)
- [Usage](#usage)
- [Using a custom locale dictionary](#using-a-custom-locale-dictionary)
@@ -84,13 +85,16 @@ Yup's API is heavily inspired by [Joi](https://github.com/hapijs/joi), but leane
- [`array.ensure(): Schema`](#arrayensure-schema)
- [`array.compact(rejector: (value) => boolean): Schema`](#arraycompactrejector-value--boolean-schema)
- [object](#object)
- [Object schema defaults](#object-schema-defaults)
- [`object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema`](#objectshapefields-object-nosortedges-arraystring-string-schema)
- [`object.from(fromKey: string, toKey: string, alias: boolean = false): Schema`](#objectfromfromkey-string-tokey-string-alias-boolean--false-schema)
- [`object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`](#objectnounknownonlyknownkeys-boolean--true-message-string--function-schema)
- [`object.camelCase(): Schema`](#objectcamelcase-schema)
- [`object.constantCase(): Schema`](#objectconstantcase-schema)
- [Extending Schema Types](#extending-schema-types)
- [TypeScript Support](#typescript-support)
- [TypeScript setting](#typescript-setting)
- [Why does InferType not default to nonRequired()?](#why-does-infertype-not-default-to-nonrequired)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

@@ -338,7 +342,7 @@ Thrown on failed validations, with the following properties
- `errors`: array of error messages
- `inner`: in the case of aggregate errors, inner is an array of `ValidationErrors` throw earlier in the
validation chain. When the `abortEarly` option is `false` this is where you can inspect each error thrown,
alternatively `errors` will have all the of the messages from each inner error.
alternatively, `errors` will have all of the messages from each inner error.

### mixed

@@ -1344,3 +1348,15 @@ const fullPerson: Person = {
birthDate: new Date(1976, 9, 5)
};
```

### TypeScript setting

For `yup.InferType<T>` to work correctly with required and nullable types you have to set `strict: true` or `strictNullChecks: true` in your tsconfig.json.

### Why does InferType not default to nonRequired()?

This was considered when inplementing `InferType<T>` but decided against.

The semantics of a required property in Yup is not the same as in TypeScript. For example a `Yup.array().of(Yup.string()).required()` will fail validation if you pass in an empty array. A required array should not be empty: [empty arrays are also considered 'missing' values](#arrayrequiredmessage-string--function-schema). The same is true for a `Yup.string().required()` where passing in an empty string `""` is considered invalid while the non-empty string: `"hello"` is considered valid. With TypeScript both would satisfy required. Another example of that is a `Yup.date()` that will pass validation if you use the string `"2020-02-14T07:52:25.495Z"` if you don't call `.strict()`.

In general there isn't a one to one match between Yup and TypeScript concepts so this is never going to be perfect.
42 changes: 21 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "yup",
"version": "0.28.3",
"version": "0.28.5",
"description": "Dead simple Object schema validation",
"main": "lib/index.js",
"module": "es/index.js",
@@ -65,47 +65,47 @@
]
},
"devDependencies": {
"@4c/rollout": "^2.1.2",
"@4c/rollout": "^2.1.7",
"@babel/cli": "7.8.4",
"@babel/core": "7.8.7",
"@babel/core": "7.9.6",
"babel-core": "^7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^25.1.0",
"babel-jest": "^25.5.1",
"babel-plugin-transform-rename-import": "^2.3.0",
"babel-preset-jason": "^6.1.3",
"babel-preset-jason": "^6.2.0",
"benchmark": "^2.0.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"dirty-chai": "^2.0.1",
"doctoc": "^1.4.0",
"eslint": "^6.8.0",
"eslint-config-jason": "^7.0.1",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jest": "^23.8.1",
"eslint-plugin-react": "^7.18.3",
"eslint-plugin-react-hooks": "^2.5.0",
"husky": "^4.2.3",
"jest": "^25.1.0",
"lint-staged": "^10.0.8",
"prettier": "^1.19.1",
"rollup": "^1.32.0",
"rollup-plugin-babel": "^4.3.3",
"rollup-plugin-filesize": "^6.2.1",
"eslint-config-prettier": "^6.11.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-jest": "^23.8.2",
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^3.0.0",
"husky": "^4.2.5",
"jest": "^25.5.2",
"lint-staged": "^10.2.1",
"prettier": "^2.0.5",
"rollup": "^2.7.5",
"rollup-plugin-babel": "^4.4.0",
"rollup-plugin-filesize": "^7.0.0",
"rollup-plugin-node-resolve": "^5.2.0",
"rollup-plugin-size-snapshot": "^0.11.0",
"sinon": "^9.0.0",
"sinon": "^9.0.2",
"sinon-chai": "^3.5.0"
},
"dependencies": {
"@babel/runtime": "^7.8.7",
"@babel/runtime": "^7.9.6",
"fn-name": "~3.0.0",
"lodash": "^4.17.15",
"lodash-es": "^4.17.11",
"property-expr": "^2.0.0",
"property-expr": "^2.0.2",
"synchronous-promise": "^2.0.10",
"toposort": "^2.0.2"
},
"readme": "ERROR: No README data found!",
"_id": "yup@0.28.2"
"_id": "yup@0.28.3"
}
3 changes: 3 additions & 0 deletions renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": ["github>4Catalyzer/renovate-config:library", ":automergeMinor"]
}
14 changes: 8 additions & 6 deletions src/array.js
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ function ArraySchema(type) {
// `undefined` specifically means uninitialized, as opposed to
// "no subtype"
this._subType = undefined;
this.innerType = undefined;

this.withMutation(() => {
this.transform(function(values) {
@@ -43,11 +44,11 @@ inherits(ArraySchema, MixedSchema, {
const value = MixedSchema.prototype._cast.call(this, _value, _opts);

//should ignore nulls here
if (!this._typeCheck(value) || !this._subType) return value;
if (!this._typeCheck(value) || !this.innerType) return value;

let isChanged = false;
const castArray = value.map((v, idx) => {
const castElement = this._subType.cast(v, {
const castElement = this.innerType.cast(v, {
..._opts,
path: makePath`${_opts.path}[${idx}]`,
});
@@ -65,7 +66,7 @@ inherits(ArraySchema, MixedSchema, {
let errors = [];
let sync = options.sync;
let path = options.path;
let subType = this._subType;
let innerType = this.innerType;
let endEarly = this._option('abortEarly', options);
let recursive = this._option('recursive', options);

@@ -76,7 +77,7 @@ inherits(ArraySchema, MixedSchema, {
.call(this, _value, options)
.catch(propagateErrors(endEarly, errors))
.then(value => {
if (!recursive || !subType || !this._typeCheck(value)) {
if (!recursive || !innerType || !this._typeCheck(value)) {
if (errors.length) throw errors[0];
return value;
}
@@ -95,7 +96,7 @@ inherits(ArraySchema, MixedSchema, {
originalValue: originalValue[idx],
};

if (subType.validate) return subType.validate(item, innerOptions);
if (innerType.validate) return innerType.validate(item, innerOptions);

return true;
});
@@ -126,6 +127,7 @@ inherits(ArraySchema, MixedSchema, {
);

next._subType = schema;
next.innerType = schema;

return next;
},
@@ -175,7 +177,7 @@ inherits(ArraySchema, MixedSchema, {

describe() {
let base = MixedSchema.prototype.describe.call(this);
if (this._subType) base.innerType = this._subType.describe();
if (this.innerType) base.innerType = this.innerType.describe();
return base;
},
});
2 changes: 1 addition & 1 deletion src/locale.js
Original file line number Diff line number Diff line change
@@ -54,7 +54,7 @@ export let date = {
export let boolean = {};

export let object = {
noUnknown: '${path} field cannot have keys not specified in the object shape',
noUnknown: '${path} field has unspecified keys: ${unknown}',
};

export let array = {
6 changes: 3 additions & 3 deletions src/mixed.js
Original file line number Diff line number Diff line change
@@ -72,6 +72,8 @@ export default function SchemaType(options = {}) {

if (has(options, 'default')) this._defaultDefault = options.default;

this.type = options.type || 'mixed';
// TODO: remove
this._type = options.type || 'mixed';
}

@@ -117,9 +119,7 @@ const proto = (SchemaType.prototype = {

if (schema._type !== this._type && this._type !== 'mixed')
throw new TypeError(
`You cannot \`concat()\` schema's of different types: ${
this._type
} and ${schema._type}`,
`You cannot \`concat()\` schema's of different types: ${this._type} and ${schema._type}`,
);

var next = prependDeep(schema.clone(), this);
6 changes: 5 additions & 1 deletion src/object.js
Original file line number Diff line number Diff line change
@@ -240,8 +240,12 @@ inherits(ObjectSchema, MixedSchema, {
exclusive: true,
message: message,
test(value) {
if (value == null) return true;
const unknownKeys = unknown(this.schema, value);
return (
value == null || !noAllow || unknown(this.schema, value).length === 0
!noAllow ||
unknownKeys.length === 0 ||
this.createError({ params: { unknown: unknownKeys.join(', ') } })
);
},
});
3 changes: 2 additions & 1 deletion src/string.js
Original file line number Diff line number Diff line change
@@ -76,9 +76,10 @@ inherits(StringSchema, MixedSchema, {
let name;

if (options) {
if (typeof options === 'string') message = options;
if (typeof options === 'object') {
({ excludeEmptyString, message, name } = options);
} else {
message = options;
}
}

56 changes: 24 additions & 32 deletions src/util/reach.js
Original file line number Diff line number Diff line change
@@ -1,58 +1,50 @@
import { forEach } from 'property-expr';
import has from 'lodash/has';

let trim = part => part.substr(0, part.length - 1).substr(1);

export function getIn(schema, path, value, context) {
export function getIn(schema, path, value, context = value) {
let parent, lastPart, lastPartDebug;

// if only one "value" arg then use it for both
context = context || value;

if (!path)
return {
parent,
parentPath: path,
schema,
};
// root path: ''
if (!path) return { parent, parentPath: path, schema };

forEach(path, (_part, isBracket, isArray) => {
let part = isBracket ? trim(_part) : _part;

if (isArray || has(schema, '_subType')) {
// we skipped an array: foo[].bar
let idx = isArray ? parseInt(part, 10) : 0;

schema = schema.resolve({ context, parent, value })._subType;
schema = schema.resolve({ context, parent, value });

if (value) {
if (isArray && idx >= value.length) {
throw new Error(
`Yup.reach cannot resolve an array item at index: ${_part}, in the path: ${path}. ` +
`because there is no value at that index. `,
);
}
if (schema.innerType) {
let idx = isArray ? parseInt(part, 10) : 0;

value = value[idx];
if (value && idx >= value.length) {
throw new Error(
`Yup.reach cannot resolve an array item at index: ${_part}, in the path: ${path}. ` +
`because there is no value at that index. `,
);
}
parent = value;
value = value && value[idx];
schema = schema.innerType;
}

// sometimes the array index part of a path doesn't exist: "nested.arr.child"
// in these cases the current part is the next schema and should be processed
// in this iteration. For cases where the index signature is included this
// check will fail and we'll handle the `child` part on the next iteration like normal
if (!isArray) {
schema = schema.resolve({ context, parent, value });

if (!has(schema, 'fields') || !has(schema.fields, part))
if (!schema.fields || !schema.fields[part])
throw new Error(
`The schema does not contain the path: ${path}. ` +
`(failed at: ${lastPartDebug} which is a type: "${schema._type}") `,
`(failed at: ${lastPartDebug} which is a type: "${schema._type}")`,
);

schema = schema.fields[part];

parent = value;
value = value && value[part];
lastPart = part;
lastPartDebug = isBracket ? '[' + _part + ']' : '.' + _part;
schema = schema.fields[part];
}

lastPart = part;
lastPartDebug = isBracket ? '[' + _part + ']' : '.' + _part;
});

return { schema, parent, parentPath: lastPart };
2 changes: 1 addition & 1 deletion test/object.js
Original file line number Diff line number Diff line change
@@ -325,7 +325,7 @@ describe('Object types', () => {
.validate({ extra: 'field' }, { strict: true })
.should.be.rejected()
.then(err => {
err.errors[0].should.be.a('string');
err.errors[0].should.be.a('string').that.include('extra');
}),
]);
});
Loading