From 50b49dc9a8bc28d4e0537037a6cfe0720d1472bb Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Sun, 14 Jun 2020 01:05:37 +0200 Subject: [PATCH] Add support for callIfNull --- src/compose-rules.js | 110 ++++++++++++++++++++++++++----------------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/src/compose-rules.js b/src/compose-rules.js index 86b3b99..0ae37f9 100644 --- a/src/compose-rules.js +++ b/src/compose-rules.js @@ -17,61 +17,85 @@ module.exports.compose = function composeValidators(validators) { let isRequired = validators.some((rule) => isRequiredMarker(rule)); let nonMarkerRules = validators.filter((rule) => !isRequiredMarker(rule)); + function callRule({ rule, value, context }) { + try { + let result = isCombinator(rule) + ? rule.callback(value, applyValidators, context) + : rule(value, context); + + if (result !== undefined) { + if (isValidationResult(result)) { + if (Array.isArray(result.errors)) { + let nonValidationErrors = result.errors.filter((error) => !matchValidationError(error)); + + if (nonValidationErrors.length === 0) { + return { + errors: result.errors, + newValue: result.newValue + }; + } else { + // We should never reach this point, but it could possibly occur if someone erroneously includes non-ValidationError errors in a validationResult. Note that this is a last-ditch handler, and so we only throw the first non-ValidationError error and let the user sort out the rest, if any. + throw nonValidationErrors[0]; + } + } else { + throw new Error(`The 'errors' in a validationResult must be an array`); + } + } else if (result instanceof Error) { + // We could interpret returned Errors as either values or a throw substitute. Let's wait for users to file issues, so that we know what people *actually* need here. + throw new Error(`It is currently not allowed to return an Error object from a validator. If you have a reason to need this, please file a bug!`); + } else { + return { + errors: [], + newValue: result + }; + } + } else { + return { + errors: [], + newValue: undefined + }; + } + } catch (error) { + if (matchValidationError(error)) { + return { + errors: [ error ], + newValue: undefined + }; + } else { + throw error; + } + } + } + return function composedValidator(value, context) { if (isRequired && value == null) { return validationResult({ errors: [ new ValidationError(`Required value is missing`) ], newValue: value }); - } else if (value != null) { + } else { let lastValue = value; let errors = []; for (let rule of nonMarkerRules) { - try { - let result = isCombinator(rule) - ? rule.callback(lastValue, applyValidators, context) - : rule(lastValue, context); - - if (result !== undefined) { - let transformedValue; - - if (isValidationResult(result)) { - if (Array.isArray(result.errors)) { - let nonValidationErrors = result.errors.filter((error) => !matchValidationError(error)); - - if (nonValidationErrors.length === 0) { - errors = result.errors; - transformedValue = result.newValue; - } else { - // We should never reach this point, but it could possibly occur if someone erroneously includes non-ValidationError errors in a validationResult. Note that this is a last-ditch handler, and so we only throw the first non-ValidationError error and let the user sort out the rest, if any. - throw result.errors[0]; - } - } else { - throw new Error(`The 'errors' in a validationResult must be an array`); - } - } else if (result instanceof Error) { - // We could interpret returned Errors as either values or a throw substitute. Let's wait for users to file issues, so that we know what people *actually* need here. - throw new Error(`It is currently not allowed to return an Error object from a validator. If you have a reason to need this, please file a bug!`); - } else { - transformedValue = result; - } - - if (transformedValue != null) { - lastValue = transformedValue; - } + if (value != null || rule.callIfNull === true) { + let result = callRule({ + rule: rule, + value: lastValue, + context: context + }); + + if (result.newValue != null) { + lastValue = result.newValue; } - } catch (error) { - if (matchValidationError(error)) { - errors = [ error ]; - } else { - throw error; + + if (result.errors.length > 0) { + errors = result.errors; + break; } - } - - if (errors.length > 0) { - break; + } else { + continue; } } @@ -82,8 +106,6 @@ module.exports.compose = function composeValidators(validators) { ? lastValue : undefined }); - } else { - return validationResult({ newValue: undefined }); } }; };