validateUtil.js 7.3 KB

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