validateUtil.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.validateRules = validateRules;
  7. var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
  8. var _asyncValidator = _interopRequireDefault(require("async-validator"));
  9. var _vue = require("vue");
  10. var _warning = require("../../vc-util/warning");
  11. var _valueUtil = require("./valueUtil");
  12. var _messages = require("./messages");
  13. var _propsUtil = require("../../_util/props-util");
  14. var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) {
  16. return value instanceof P ? value : new P(function (resolve) {
  17. resolve(value);
  18. });
  19. }
  20. return new (P || (P = Promise))(function (resolve, reject) {
  21. function fulfilled(value) {
  22. try {
  23. step(generator.next(value));
  24. } catch (e) {
  25. reject(e);
  26. }
  27. }
  28. function rejected(value) {
  29. try {
  30. step(generator["throw"](value));
  31. } catch (e) {
  32. reject(e);
  33. }
  34. }
  35. function step(result) {
  36. result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
  37. }
  38. step((generator = generator.apply(thisArg, _arguments || [])).next());
  39. });
  40. };
  41. // Remove incorrect original ts define
  42. const AsyncValidator = _asyncValidator.default;
  43. /**
  44. * Replace with template.
  45. * `I'm ${name}` + { name: 'bamboo' } = I'm bamboo
  46. */
  47. function replaceMessage(template, kv) {
  48. return template.replace(/\$\{\w+\}/g, str => {
  49. const key = str.slice(2, -1);
  50. return kv[key];
  51. });
  52. }
  53. function validateRule(name, value, rule, options, messageVariables) {
  54. return __awaiter(this, void 0, void 0, function* () {
  55. const cloneRule = (0, _extends2.default)({}, rule);
  56. // Bug of `async-validator`
  57. delete cloneRule.ruleIndex;
  58. delete cloneRule.trigger;
  59. // We should special handle array validate
  60. let subRuleField = null;
  61. if (cloneRule && cloneRule.type === 'array' && cloneRule.defaultField) {
  62. subRuleField = cloneRule.defaultField;
  63. delete cloneRule.defaultField;
  64. }
  65. const validator = new AsyncValidator({
  66. [name]: [cloneRule]
  67. });
  68. const messages = (0, _valueUtil.setValues)({}, _messages.defaultValidateMessages, options.validateMessages);
  69. validator.messages(messages);
  70. let result = [];
  71. try {
  72. yield Promise.resolve(validator.validate({
  73. [name]: value
  74. }, (0, _extends2.default)({}, options)));
  75. } catch (errObj) {
  76. if (errObj.errors) {
  77. result = errObj.errors.map((_ref, index) => {
  78. let {
  79. message
  80. } = _ref;
  81. return (
  82. // Wrap VueNode with `key`
  83. (0, _propsUtil.isValidElement)(message) ? (0, _vue.cloneVNode)(message, {
  84. key: `error_${index}`
  85. }) : message
  86. );
  87. });
  88. } else {
  89. console.error(errObj);
  90. result = [messages.default()];
  91. }
  92. }
  93. if (!result.length && subRuleField) {
  94. const subResults = yield Promise.all(value.map((subValue, i) => validateRule(`${name}.${i}`, subValue, subRuleField, options, messageVariables)));
  95. return subResults.reduce((prev, errors) => [...prev, ...errors], []);
  96. }
  97. // Replace message with variables
  98. const kv = (0, _extends2.default)((0, _extends2.default)((0, _extends2.default)({}, rule), {
  99. name,
  100. enum: (rule.enum || []).join(', ')
  101. }), messageVariables);
  102. const fillVariableResult = result.map(error => {
  103. if (typeof error === 'string') {
  104. return replaceMessage(error, kv);
  105. }
  106. return error;
  107. });
  108. return fillVariableResult;
  109. });
  110. }
  111. /**
  112. * We use `async-validator` to validate the value.
  113. * But only check one value in a time to avoid namePath validate issue.
  114. */
  115. function validateRules(namePath, value, rules, options, validateFirst, messageVariables) {
  116. const name = namePath.join('.');
  117. // Fill rule with context
  118. const filledRules = rules.map((currentRule, ruleIndex) => {
  119. const originValidatorFunc = currentRule.validator;
  120. const cloneRule = (0, _extends2.default)((0, _extends2.default)({}, currentRule), {
  121. ruleIndex
  122. });
  123. // Replace validator if needed
  124. if (originValidatorFunc) {
  125. cloneRule.validator = (rule, val, callback) => {
  126. let hasPromise = false;
  127. // Wrap callback only accept when promise not provided
  128. const wrappedCallback = function () {
  129. for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
  130. args[_key] = arguments[_key];
  131. }
  132. // Wait a tick to make sure return type is a promise
  133. Promise.resolve().then(() => {
  134. (0, _warning.warning)(!hasPromise, 'Your validator function has already return a promise. `callback` will be ignored.');
  135. if (!hasPromise) {
  136. callback(...args);
  137. }
  138. });
  139. };
  140. // Get promise
  141. const promise = originValidatorFunc(rule, val, wrappedCallback);
  142. hasPromise = promise && typeof promise.then === 'function' && typeof promise.catch === 'function';
  143. /**
  144. * 1. Use promise as the first priority.
  145. * 2. If promise not exist, use callback with warning instead
  146. */
  147. (0, _warning.warning)(hasPromise, '`callback` is deprecated. Please return a promise instead.');
  148. if (hasPromise) {
  149. promise.then(() => {
  150. callback();
  151. }).catch(err => {
  152. callback(err || ' ');
  153. });
  154. }
  155. };
  156. }
  157. return cloneRule;
  158. }).sort((_ref2, _ref3) => {
  159. let {
  160. warningOnly: w1,
  161. ruleIndex: i1
  162. } = _ref2;
  163. let {
  164. warningOnly: w2,
  165. ruleIndex: i2
  166. } = _ref3;
  167. if (!!w1 === !!w2) {
  168. // Let keep origin order
  169. return i1 - i2;
  170. }
  171. if (w1) {
  172. return 1;
  173. }
  174. return -1;
  175. });
  176. // Do validate rules
  177. let summaryPromise;
  178. if (validateFirst === true) {
  179. // >>>>> Validate by serialization
  180. summaryPromise = new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
  181. /* eslint-disable no-await-in-loop */
  182. for (let i = 0; i < filledRules.length; i += 1) {
  183. const rule = filledRules[i];
  184. const errors = yield validateRule(name, value, rule, options, messageVariables);
  185. if (errors.length) {
  186. reject([{
  187. errors,
  188. rule
  189. }]);
  190. return;
  191. }
  192. }
  193. /* eslint-enable */
  194. resolve([]);
  195. }));
  196. } else {
  197. // >>>>> Validate by parallel
  198. const rulePromises = filledRules.map(rule => validateRule(name, value, rule, options, messageVariables).then(errors => ({
  199. errors,
  200. rule
  201. })));
  202. summaryPromise = (validateFirst ? finishOnFirstFailed(rulePromises) : finishOnAllFailed(rulePromises)).then(errors => {
  203. // Always change to rejection for Field to catch
  204. return Promise.reject(errors);
  205. });
  206. }
  207. // Internal catch error to avoid console error log.
  208. summaryPromise.catch(e => e);
  209. return summaryPromise;
  210. }
  211. function finishOnAllFailed(rulePromises) {
  212. return __awaiter(this, void 0, void 0, function* () {
  213. return Promise.all(rulePromises).then(errorsList => {
  214. const errors = [].concat(...errorsList);
  215. return errors;
  216. });
  217. });
  218. }
  219. function finishOnFirstFailed(rulePromises) {
  220. return __awaiter(this, void 0, void 0, function* () {
  221. let count = 0;
  222. return new Promise(resolve => {
  223. rulePromises.forEach(promise => {
  224. promise.then(ruleError => {
  225. if (ruleError.errors.length) {
  226. resolve([ruleError]);
  227. }
  228. count += 1;
  229. if (count === rulePromises.length) {
  230. resolve([]);
  231. }
  232. });
  233. });
  234. });
  235. });
  236. }