UNPKG

vtils

Version:

一个面向业务的 JavaScript/TypeScript 实用程序库。

645 lines (639 loc) 20.1 kB
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;