UNPKG

vtils

Version:

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

358 lines (354 loc) 12.3 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; exports.__esModule = true; exports.default = ObjectSchema; var _createForOfIteratorHelperLoose2 = _interopRequireDefault(require("@babel/runtime/helpers/createForOfIteratorHelperLoose")); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _utils = require("../../utils"); var _propertyExpr = require("property-expr"); var _locale = require("./locale.js"); var _mixed = _interopRequireDefault(require("./mixed")); var _inherits = _interopRequireDefault(require("./util/inherits")); var _reach = _interopRequireDefault(require("./util/reach")); var _runTests = _interopRequireDefault(require("./util/runTests")); var _sortByKeyOrder = _interopRequireDefault(require("./util/sortByKeyOrder")); var _sortFields = _interopRequireDefault(require("./util/sortFields")); var isObject = function isObject(obj) { return Object.prototype.toString.call(obj) === '[object Object]'; }; function unknown(ctx, value) { var known = Object.keys(ctx.fields); return Object.keys(value).filter(function (key) { return known.indexOf(key) === -1; }); } function ObjectSchema(spec) { var _this2 = this; if (!(this instanceof ObjectSchema)) return typeof spec === 'function' ? spec(new ObjectSchema()) : new ObjectSchema(spec); _mixed.default.call(this, { type: 'object', default: function _default() { var _this = this; if (!this._nodes.length) return undefined; var dft = {}; this._nodes.forEach(function (key) { dft[key] = _this.fields[key].default ? _this.fields[key].getDefault() : undefined; }); return dft; } }); this.fields = Object.create(null); this._sortErrors = (0, _sortByKeyOrder.default)([]); this._nodes = []; this._excludedEdges = []; this.withMutation(function () { _this2.transform(function coerce(value) { if (typeof value === 'string') { try { value = JSON.parse(value); } catch (err) { value = null; } } if (this.isType(value)) return value; return null; }); if (spec) { _this2.shape(spec); } }); } (0, _inherits.default)(ObjectSchema, _mixed.default, { _typeCheck: function _typeCheck(value) { return isObject(value) || typeof value === 'function'; }, _cast: function _cast(_value, options) { var _this3 = this; if (options === void 0) { options = {}; } var value = _mixed.default.prototype._cast.call(this, _value); //should ignore nulls here if (value === undefined) return this.getDefault(); if (!this._typeCheck(value)) return value; var fields = this.fields; var strip = this._option('stripUnknown', options) === true; var props = this._nodes.concat(Object.keys(value).filter(function (v) { return _this3._nodes.indexOf(v) === -1; })); var intermediateValue = {}; // is filled during the transform below var innerOptions = (0, _extends2.default)({}, options, { parent: intermediateValue, __validating: options.__validating || false }); var isChanged = false; for (var _iterator = (0, _createForOfIteratorHelperLoose2.default)(props), _step; !(_step = _iterator()).done;) { var prop = _step.value; var field = fields[prop]; var exists = (0, _utils.has)(value, prop); if (field) { var fieldValue = void 0; var strict = field._options && field._options.strict; // safe to mutate since this is fired in sequence innerOptions.path = (options.path ? options.path + "." : '') + prop; innerOptions.value = value[prop]; field = field.resolve(innerOptions); if (field._strip === true) { isChanged = isChanged || prop in value; continue; } fieldValue = !options.__validating || !strict ? field.cast(value[prop], innerOptions) : value[prop]; if (fieldValue !== undefined) { intermediateValue[prop] = fieldValue; } } else if (exists && !strip) { intermediateValue[prop] = value[prop]; } if (intermediateValue[prop] !== value[prop]) { isChanged = true; } } return isChanged ? intermediateValue : value; }, /** * @typedef {Object} Ancestor * @property {Object} schema - a string property of SpecialType * @property {*} value - a number property of SpecialType */ /** * * @param {*} _value * @param {Object} opts * @param {string=} opts.path * @param {*=} opts.parent * @param {Object=} opts.context * @param {boolean=} opts.sync * @param {boolean=} opts.stripUnknown * @param {boolean=} opts.strict * @param {boolean=} opts.recursive * @param {boolean=} opts.abortEarly * @param {boolean=} opts.__validating * @param {Object=} opts.originalValue * @param {Ancestor[]=} opts.from * @param {Object} [opts.from] * @param {Function} callback */ _validate: function _validate(_value, opts, callback) { var _this4 = this; if (opts === void 0) { opts = {}; } var errors = []; var _opts = opts, sync = _opts.sync, _opts$from = _opts.from, from = _opts$from === void 0 ? [] : _opts$from, _opts$originalValue = _opts.originalValue, originalValue = _opts$originalValue === void 0 ? _value : _opts$originalValue, _opts$abortEarly = _opts.abortEarly, abortEarly = _opts$abortEarly === void 0 ? this._options.abortEarly : _opts$abortEarly, _opts$recursive = _opts.recursive, recursive = _opts$recursive === void 0 ? this._options.recursive : _opts$recursive; from = [{ schema: this, value: originalValue }].concat(from); // this flag is needed for handling `strict` correctly in the context of // validation vs just casting. e.g strict() on a field is only used when validating opts.__validating = true; opts.originalValue = originalValue; opts.from = from; _mixed.default.prototype._validate.call(this, _value, opts, function (err, value) { if (err) { if (abortEarly) return void callback(err); errors.push(err); value = err.value; } if (!recursive || !isObject(value)) { callback(errors[0] || null, value); return; } originalValue = originalValue || value; var tests = _this4._nodes.map(function (key) { return function (_, cb) { var path = key.indexOf('.') === -1 ? (opts.path ? opts.path + "." : '') + key : (opts.path || '') + "[\"" + key + "\"]"; var field = _this4.fields[key]; if (field && field.validate) { field.validate(value[key], (0, _extends2.default)({}, opts, { path: path, from: from, // inner fields are always strict: // 1. this isn't strict so the casting will also have cast inner values // 2. this is strict in which case the nested values weren't cast either strict: true, parent: value, originalValue: originalValue[key] }), cb); return; } cb(null); }; }); (0, _runTests.default)({ sync: sync, tests: tests, value: value, errors: errors, endEarly: abortEarly, sort: _this4._sortErrors, path: opts.path }, callback); }); }, concat: function concat(schema) { var next = _mixed.default.prototype.concat.call(this, schema); next._nodes = (0, _sortFields.default)(next.fields, next._excludedEdges); return next; }, shape: function shape(schema, excludes) { if (excludes === void 0) { excludes = []; } var next = this.clone(); var fields = Object.assign(next.fields, schema); next.fields = fields; next._sortErrors = (0, _sortByKeyOrder.default)(Object.keys(fields)); if (excludes.length) { if (!Array.isArray(excludes[0])) excludes = [excludes]; var keys = excludes.map(function (_ref) { var first = _ref[0], second = _ref[1]; return first + "-" + second; }); next._excludedEdges = next._excludedEdges.concat(keys); } next._nodes = (0, _sortFields.default)(fields, next._excludedEdges); return next; }, pick: function pick(keys) { var picked = {}; for (var _iterator2 = (0, _createForOfIteratorHelperLoose2.default)(keys), _step2; !(_step2 = _iterator2()).done;) { var key = _step2.value; if (this.fields[key]) picked[key] = this.fields[key]; } return this.clone().withMutation(function (next) { next.fields = {}; return next.shape(picked); }); }, omit: function omit(keys) { var next = this.clone(); var fields = next.fields; next.fields = {}; for (var _iterator3 = (0, _createForOfIteratorHelperLoose2.default)(keys), _step3; !(_step3 = _iterator3()).done;) { var key = _step3.value; delete fields[key]; } return next.withMutation(function (next) { return next.shape(fields); }); }, from: function from(_from, to, alias) { var fromGetter = (0, _propertyExpr.getter)(_from, true); return this.transform(function (obj) { if (obj == null) return obj; var newObj = obj; if ((0, _utils.has)(obj, _from)) { newObj = (0, _extends2.default)({}, obj); if (!alias) delete newObj[_from]; newObj[to] = fromGetter(obj); } return newObj; }); }, noUnknown: function noUnknown(noAllow, message) { if (noAllow === void 0) { noAllow = true; } if (message === void 0) { message = _locale.object.noUnknown; } if (typeof noAllow === 'string') { message = noAllow; noAllow = true; } var next = this.test({ name: 'noUnknown', exclusive: true, message: message, test: function test(value) { if (value == null) return true; var unknownKeys = unknown(this.schema, value); return !noAllow || unknownKeys.length === 0 || this.createError({ params: { unknown: unknownKeys.join(', ') } }); } }); next._options.stripUnknown = noAllow; return next; }, unknown: function unknown(allow, message) { if (allow === void 0) { allow = true; } if (message === void 0) { message = _locale.object.noUnknown; } return this.noUnknown(!allow, message); }, transformKeys: function transformKeys(fn) { return this.transform(function (obj) { return obj && (0, _utils.mapKeys)(obj, function (_, key) { return fn(key); }); }); }, camelCase: function camelCase() { return this.transformKeys(_utils.camelCase); }, snakeCase: function snakeCase() { return this.transformKeys(_utils.snakeCase); }, constantCase: function constantCase() { return this.transformKeys(function (key) { return (0, _utils.snakeCase)(key).toUpperCase(); }); }, describe: function describe() { var base = _mixed.default.prototype.describe.call(this); base.fields = (0, _utils.mapValues)(this.fields, function (value) { return value.describe(); }); return base; }, // 新增 validateInOrder: function validateInOrder(data, options) { var _this5 = this; return Object.keys(data).reduce(function (prev, key) { return prev.then(function () { var schema; try { schema = (0, _reach.default)(_this5, key); } catch (e) {} return schema ? _this5.validateAt(key, data, options) : undefined; }); }, Promise.resolve()).then(function () { return _this5.cast(data); }); }, validateInOrderSync: function validateInOrderSync(data, options) { for (var _i = 0, _Object$keys = Object.keys(data); _i < _Object$keys.length; _i++) { var key = _Object$keys[_i]; var schema = void 0; try { schema = (0, _reach.default)(this, key); } catch (e) {} if (schema) { this.validateSyncAt(key, data, options); } } return this.cast(data); } });