vtils
Version:
一个面向业务的 JavaScript/TypeScript 实用程序库。
645 lines (639 loc) • 20.1 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import _createForOfIteratorHelperLoose from "@babel/runtime/helpers/esm/createForOfIteratorHelperLoose";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import { cloneDeepWith, has, toArray as _toArray, values } from "../../utils/index.js";
import Condition from "./Condition.js";
import Ref from "./Reference.js";
import { mixed as locale } from "./locale.js";
import createValidation from "./util/createValidation.js";
import isSchema from "./util/isSchema.js";
import prependDeep from "./util/prependDeep.js";
import printValue from "./util/printValue.js";
import { getIn } from "./util/reach.js";
import runTests from "./util/runTests.js";
var RefSet = /*#__PURE__*/function () {
function RefSet() {
this.list = new Set();
this.refs = new Map();
}
var _proto = RefSet.prototype;
_proto.describe = function describe() {
var description = [];
for (var _iterator = _createForOfIteratorHelperLoose(this.list), _step; !(_step = _iterator()).done;) {
var item = _step.value;
description.push(item);
}
for (var _iterator2 = _createForOfIteratorHelperLoose(this.refs), _step2; !(_step2 = _iterator2()).done;) {
var _step2$value = _step2.value,
ref = _step2$value[1];
description.push(ref.describe());
}
return description;
};
_proto.toArray = function toArray() {
return _toArray(this.list).concat(_toArray(this.refs.values()));
};
_proto.add = function add(value) {
Ref.isRef(value) ? this.refs.set(value.key, value) : this.list.add(value);
};
_proto.delete = function _delete(value) {
Ref.isRef(value) ? this.refs.delete(value.key) : this.list.delete(value);
};
_proto.has = function has(value, resolve) {
if (this.list.has(value)) return true;
var item,
values = this.refs.values();
while (item = values.next(), !item.done) if (resolve(item.value) === value) return true;
return false;
};
_proto.clone = function clone() {
var next = new RefSet();
next.list = new Set(this.list);
next.refs = new Map(this.refs);
return next;
};
_proto.merge = function merge(newItems, removeItems) {
var next = this.clone();
newItems.list.forEach(function (value) {
return next.add(value);
});
newItems.refs.forEach(function (value) {
return next.add(value);
});
removeItems.list.forEach(function (value) {
return next.delete(value);
});
removeItems.refs.forEach(function (value) {
return next.delete(value);
});
return next;
};
_createClass(RefSet, [{
key: "size",
get: function get() {
return this.list.size + this.refs.size;
}
}]);
return RefSet;
}();
export default function SchemaType(options) {
var _this = this;
if (options === void 0) {
options = {};
}
if (!(this instanceof SchemaType)) return typeof options === 'function' ? options(new SchemaType()) : new SchemaType();
this._deps = [];
this._conditions = [];
this._options = {
abortEarly: true,
recursive: true
};
this._exclusive = Object.create(null);
this._whitelist = new RefSet();
this._blacklist = new RefSet();
this.tests = [];
this.transforms = [];
this.withMutation(function () {
_this.typeError(locale.notType);
});
if (has(options, 'default')) this._defaultDefault = options.default;
this.type = options.type || 'mixed';
// TODO: remove
this._type = options.type || 'mixed';
}
var proto = SchemaType.prototype = {
__isYupSchema__: true,
constructor: SchemaType,
clone: function clone() {
var _this2 = this;
if (this._mutate) return this;
// if the nested value is a schema we can skip cloning, since
// they are already immutable
return cloneDeepWith(this, function (value, key) {
if (isSchema(value) && value !== _this2) return value;
// fix for ie11 when cloning Set and Map
if (key === '_whitelist' || key === '_blacklist') {
return value.clone();
}
});
},
label: function label(_label) {
var next = this.clone();
next._label = _label;
return next;
},
meta: function meta(obj) {
if (arguments.length === 0) return this._meta;
var next = this.clone();
next._meta = Object.assign(next._meta || {}, obj);
return next;
},
withMutation: function withMutation(fn) {
var before = this._mutate;
this._mutate = true;
var result = fn(this);
this._mutate = before;
return result;
},
concat: function concat(schema) {
if (!schema || schema === this) return this;
if (schema._type !== this._type && this._type !== 'mixed') throw new TypeError("You cannot `concat()` schema's of different types: " + this._type + " and " + schema._type);
var next = prependDeep(schema.clone(), this);
// new undefined default is overridden by old non-undefined one, revert
if (has(schema, '_default')) next._default = schema._default;
next.tests = this.tests;
next._exclusive = this._exclusive;
// manually merge the blacklist/whitelist (the other `schema` takes
// precedence in case of conflicts)
next._whitelist = this._whitelist.merge(schema._whitelist, schema._blacklist);
next._blacklist = this._blacklist.merge(schema._blacklist, schema._whitelist);
// manually add the new tests to ensure
// the deduping logic is consistent
next.withMutation(function (next) {
schema.tests.forEach(function (fn) {
next.test(fn.OPTIONS);
});
});
return next;
},
isType: function isType(v) {
if (this._nullable && v === null) return true;
if (this._allowEmptyString && v === '') return true;
return !this._typeCheck || this._typeCheck(v);
},
resolve: function resolve(options) {
var schema = this;
if (schema._conditions.length) {
var conditions = schema._conditions;
schema = schema.clone();
schema._conditions = [];
schema = conditions.reduce(function (schema, condition) {
return condition.resolve(schema, options);
}, schema);
schema = schema.resolve(options);
}
return schema;
},
/**
*
* @param {*} value
* @param {Object} options
* @param {*=} options.parent
* @param {*=} options.context
*/
cast: function cast(value, options) {
if (options === void 0) {
options = {};
}
var resolvedSchema = this.resolve(_extends({
value: value
}, options));
var result = resolvedSchema._cast(value, options);
if (value !== undefined && options.assert !== false && resolvedSchema.isType(result) !== true) {
var formattedValue = printValue(value);
var formattedResult = printValue(result);
throw new TypeError("The value of " + (options.path || 'field') + " could not be cast to a value " + ("that satisfies the schema type: \"" + resolvedSchema._type + "\". \n\n") + ("attempted value: " + formattedValue + " \n") + (formattedResult !== formattedValue ? "result of cast: " + formattedResult : ''));
}
return result;
},
_cast: function _cast(rawValue) {
var _this3 = this;
var value = rawValue === undefined ? rawValue : this.transforms.reduce(function (value, fn) {
return fn.call(_this3, value, rawValue);
}, rawValue);
if (value === undefined && has(this, '_default')) {
value = this.getDefault();
}
return value;
},
_validate: function _validate(_value, options, cb) {
var _this4 = this;
if (options === void 0) {
options = {};
}
var _options = options,
sync = _options.sync,
path = _options.path,
_options$from = _options.from,
from = _options$from === void 0 ? [] : _options$from,
_options$originalValu = _options.originalValue,
originalValue = _options$originalValu === void 0 ? _value : _options$originalValu,
_options$strict = _options.strict,
strict = _options$strict === void 0 ? this._options.strict : _options$strict,
_options$abortEarly = _options.abortEarly,
abortEarly = _options$abortEarly === void 0 ? this._options.abortEarly : _options$abortEarly;
var value = _value;
if (!strict) {
this._validating = true;
value = this._cast(value, _extends({
assert: false
}, options));
this._validating = false;
}
// value is cast, we can check if it meets type requirements
var args = {
value: value,
path: path,
options: options,
originalValue: originalValue,
schema: this,
label: this._label,
sync: sync,
from: from
};
var initialTests = [];
if (this._typeError) initialTests.push(this._typeError);
if (this._whitelistError) initialTests.push(this._whitelistError);
if (this._blacklistError) initialTests.push(this._blacklistError);
return runTests({
args: args,
value: value,
path: path,
sync: sync,
tests: initialTests,
endEarly: abortEarly
}, function (err) {
if (err) return void cb(err);
runTests({
tests: _this4.tests,
args: args,
path: path,
sync: sync,
value: value,
endEarly: abortEarly
}, cb);
});
},
validate: function validate(value, options, maybeCb) {
if (options === void 0) {
options = {};
}
if (!options.rootValue) options.rootValue = value;
var schema = this.resolve(_extends({}, options, {
value: value
}));
// callback case is for nested validations
return typeof maybeCb === 'function' ? schema._validate(value, options, maybeCb) : new Promise(function (resolve, reject) {
return schema._validate(value, options, function (err, value) {
if (err) reject(err);else resolve(value);
});
});
},
validateSync: function validateSync(value, options) {
if (options === void 0) {
options = {};
}
if (!options.rootValue) options.rootValue = value;
var schema = this.resolve(_extends({}, options, {
value: value
}));
var result;
schema._validate(value, _extends({}, options, {
sync: true
}), function (err, value) {
if (err) throw err;
result = value;
});
return result;
},
isValid: function isValid(value, options) {
return this.validate(value, options).then(function () {
return true;
}).catch(function (err) {
if (err.name === 'ValidationError') return false;
throw err;
});
},
isValidSync: function isValidSync(value, options) {
try {
this.validateSync(value, options);
return true;
} catch (err) {
if (err.name === 'ValidationError') return false;
throw err;
}
},
_getDefault: function _getDefault() {
var defaultValue = has(this, '_default') ? this._default : this._defaultDefault;
return typeof defaultValue === 'function' ? defaultValue.call(this) : cloneDeepWith(defaultValue);
},
getDefault: function getDefault(options) {
if (options === void 0) {
options = {};
}
var schema = this.resolve(options);
return schema._getDefault();
},
default: function _default(def) {
if (arguments.length === 0) {
console.warn('Calling `schema.default()` as a getter to retrieve a default is deprecated and will be removed in the next version. \n' + 'Use `schema.getDefault()` instead.');
return this._getDefault();
}
var next = this.clone();
next._default = def;
return next;
},
strict: function strict(isStrict) {
if (isStrict === void 0) {
isStrict = true;
}
var next = this.clone();
next._options.strict = isStrict;
return next;
},
_isPresent: function _isPresent(value) {
return value != null;
},
required: function required(message) {
if (message === void 0) {
message = locale.required;
}
return this.test({
message: message,
name: 'required',
exclusive: true,
test: function test(value) {
return this.schema._isPresent(value);
}
});
},
notRequired: function notRequired() {
var next = this.clone();
next.tests = next.tests.filter(function (test) {
return test.OPTIONS.name !== 'required';
});
return next;
},
nullable: function nullable(isNullable) {
if (isNullable === void 0) {
isNullable = true;
}
var next = this.clone();
next._nullable = isNullable;
return next;
},
allowEmptyString: function allowEmptyString() {
var next = this.clone();
next._allowEmptyString = true;
return next;
},
transform: function transform(fn) {
var next = this.clone();
next.transforms.push(fn);
return next;
},
/**
* Adds a test function to the schema's queue of tests.
* tests can be exclusive or non-exclusive.
*
* - exclusive tests, will replace any existing tests of the same name.
* - non-exclusive: can be stacked
*
* If a non-exclusive test is added to a schema with an exclusive test of the same name
* the exclusive test is removed and further tests of the same name will be stacked.
*
* If an exclusive test is added to a schema with non-exclusive tests of the same name
* the previous tests are removed and further tests of the same name will replace each other.
*/
test: function test() {
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var opts;
if (typeof args[0] === 'function') {
opts = {
test: args[0],
message: args[1]
};
} else if (args[0] instanceof RegExp) {
opts = {
test: function test(value) {
return args[0].test(value);
},
message: args[1]
};
}
if (!opts) {
if (args.length === 1) {
if (typeof args[0] === 'function') {
opts = {
test: args[0]
};
} else {
opts = args[0];
}
} else if (args.length === 2) {
opts = {
name: args[0],
test: args[1]
};
} else {
opts = {
name: args[0],
message: args[1],
test: args[2]
};
}
}
if (opts.message === undefined) opts.message = locale.default;
if (typeof opts.test !== 'function') throw new TypeError('`test` is a required parameters');
var next = this.clone();
var validate = createValidation(opts);
var isExclusive = opts.exclusive || opts.name && next._exclusive[opts.name] === true;
if (opts.exclusive && !opts.name) {
throw new TypeError('Exclusive tests must provide a unique `name` identifying the test');
}
next._exclusive[opts.name] = !!opts.exclusive;
next.tests = next.tests.filter(function (fn) {
if (fn.OPTIONS.name === opts.name) {
if (isExclusive) return false;
if (fn.OPTIONS.test === validate.OPTIONS.test) return false;
}
return true;
});
next.tests.push(validate);
return next;
},
when: function when(keys, options) {
if (arguments.length === 1) {
options = keys;
keys = '.';
}
var next = this.clone(),
deps = [].concat(keys).map(function (key) {
return new Ref(key);
});
deps.forEach(function (dep) {
if (dep.isSibling) next._deps.push(dep.key);
});
next._conditions.push(new Condition(deps, options));
return next;
},
typeError: function typeError(message) {
var next = this.clone();
next._typeError = createValidation({
message: message,
name: 'typeError',
test: function test(value) {
if (value !== undefined && !this.schema.isType(value)) return this.createError({
params: {
type: this.schema._type
}
});
return true;
}
});
return next;
},
oneOf: function oneOf(enums, message) {
if (message === void 0) {
message = locale.oneOf;
}
if (!Array.isArray(enums)) {
enums = values(enums);
}
var next = this.clone();
enums.forEach(function (val) {
next._whitelist.add(val);
next._blacklist.delete(val);
});
next._whitelistError = createValidation({
message: message,
name: 'oneOf',
test: function test(value) {
if (value === undefined) return true;
var valids = this.schema._whitelist;
return valids.has(value, this.resolve) ? true : this.createError({
params: {
values: valids.toArray().join(', ')
}
});
}
});
return next;
},
notOneOf: function notOneOf(enums, message) {
if (message === void 0) {
message = locale.notOneOf;
}
var next = this.clone();
enums.forEach(function (val) {
next._blacklist.add(val);
next._whitelist.delete(val);
});
next._blacklistError = createValidation({
message: message,
name: 'notOneOf',
test: function test(value) {
var invalids = this.schema._blacklist;
if (invalids.has(value, this.resolve)) return this.createError({
params: {
values: invalids.toArray().join(', ')
}
});
return true;
}
});
return next;
},
strip: function strip(_strip) {
if (_strip === void 0) {
_strip = true;
}
var next = this.clone();
next._strip = _strip;
return next;
},
_option: function _option(key, overrides) {
return has(overrides, key) ? overrides[key] : this._options[key];
},
describe: function describe() {
var next = this.clone();
var description = {
type: next._type,
meta: next._meta,
label: next._label,
tests: next.tests.map(function (fn) {
return {
name: fn.OPTIONS.name,
params: fn.OPTIONS.params
};
}).filter(function (n, idx, list) {
return list.findIndex(function (c) {
return c.name === n.name;
}) === idx;
})
};
if (next._whitelist.size) description.oneOf = next._whitelist.describe();
if (next._blacklist.size) description.notOneOf = next._blacklist.describe();
return description;
},
defined: function defined(message) {
if (message === void 0) {
message = locale.defined;
}
return this.test({
message: message,
name: 'defined',
exclusive: true,
test: function test(value) {
return value !== undefined;
}
});
},
// 新增
validatePlus: function validatePlus(data, options) {
return (this.type === 'object' ? this.validateInOrder(data, options) : this.validate(data, options)).then(function (data) {
return {
data: data
};
}).catch(function (error) {
return {
error: error,
data: data
};
});
},
validatePlusSync: function validatePlusSync(data, options) {
try {
var _data = this.type === 'object' ? this.validateInOrderSync(data, options) : this.validateSync(data, options);
return {
data: _data
};
} catch (error) {
return {
error: error,
data: data
};
}
}
};
var _loop = function _loop() {
var method = _arr[_i];
proto[method + "At"] = function (path, value, options) {
if (options === void 0) {
options = {};
}
if (!options.rootValue) options.rootValue = value;
var _getIn = getIn(this, path, value, options.context),
parent = _getIn.parent,
parentPath = _getIn.parentPath,
schema = _getIn.schema;
return schema[method](parent && parent[parentPath], _extends({}, options, {
parent: parent,
path: path
}));
};
};
for (var _i = 0, _arr = ['validate', 'validateSync']; _i < _arr.length; _i++) {
_loop();
}
for (var _i2 = 0, _arr2 = ['equals', 'is', 'enum']; _i2 < _arr2.length; _i2++) {
var alias = _arr2[_i2];
proto[alias] = proto.oneOf;
}
for (var _i3 = 0, _arr3 = ['not', 'nope']; _i3 < _arr3.length; _i3++) {
var _alias = _arr3[_i3];
proto[_alias] = proto.notOneOf;
}
proto.optional = proto.notRequired;