UNPKG

@boomerang-io/utils

Version:

A library of reusable utilities and hooks for React webapps on the Boomerang platform.

315 lines (234 loc) 13.2 kB
"use strict"; function _typeof(obj) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }, _typeof(obj); } Object.defineProperty(exports, "__esModule", { value: true }); exports.transform = transform; exports.transformAll = transformAll; exports.transformObject = transformObject; var yup = _interopRequireWildcard(require("yup")); var _customValidators = require("./customValidators"); function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function _toArray(arr) { return _arrayWithHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableRest(); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var ValidationError = /*#__PURE__*/_createClass(function ValidationError(message) { _classCallCheck(this, ValidationError); this.message = message; }); /** * Searches for {substring} in {string}. * If found, returns the {string}, sliced after substring. * * @param {string} string - String to be sliced. * @param {string} substring - String to search for. * @returns {string|null} Null if no match found. */ function getSubString(string, substring) { if (!string) return null; var testedIndex = string.indexOf(substring); if (testedIndex > -1) { return string.slice(testedIndex + substring.length); } return null; } /** * Returns a function from yup, by passing in a function name from our schema. * @param {string} functionName - The string to search for a function. * @param {Object} previousInstance - Object from previous validator result or yup itself. * @returns {Function} Either the found yup function or the default validator. */ function getYupFunction(functionName) { var previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; // Make sure we're dealing with a string if (functionName instanceof Array) { functionName = functionName[0]; } // Attempt to retrieve any custom validators first var customValidator = (0, _customValidators.getCustomValidator)(functionName); if (customValidator) { return customValidator; } var yupName = getSubString(functionName, "yup."); if (yupName && previousInstance[yupName] instanceof Function) { return previousInstance[yupName].bind(previousInstance); } if (yupName && yup[yupName] instanceof Function) { return yup[yupName].bind(yup); } throw new ValidationError("Could not find validator " + functionName); } /** * Here we check to see if a passed array could be a prefix notation function. * @param {Array} item - Item to be checked. * @param {any} item.functionName - We'll check this, and perhaps it's a prefix function name. * @returns {boolean} True if we are actually looking at prefix notation. */ function isPrefixNotation(_ref) { var _ref2 = _slicedToArray(_ref, 1), functionName = _ref2[0]; if (functionName instanceof Array) { if (isPrefixNotation(functionName)) return true; } if (typeof functionName !== "string") return false; if (functionName.indexOf("yup.") < 0) return false; return true; } /** * Ensure that the argument passed is an array. * @param {Any} maybeArray - To be checked. * @returns {Array} forced to array. */ function ensureArray(maybeArray) { if (maybeArray instanceof Array === false) { return [maybeArray]; } return maybeArray; } /** * Converts an array of ['yup.number'] to yup.number(). * @param {[Any]} arrayArgument - The validation array. * @param {Object} previousInstance - The result of a call to yup.number() * i.e. an object schema validation set * @returns {Function} generated yup validator */ function convertArray(arrayArgument) { var previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; var _ensureArray = ensureArray(arrayArgument), _ensureArray2 = _toArray(_ensureArray), functionName = _ensureArray2[0], argsToPass = _ensureArray2.slice(1); // Handle the case when we have a previous instance // but we don't want to use it for this transformation // [['yup.array'], ['yup.of', [['yup.object'], ['yup.shape'] ...]]] if (functionName instanceof Array) { return transformArray(arrayArgument); } var gotFunc = getYupFunction(functionName, previousInstance, arrayArgument); // Here we'll actually call the function // This might be something like yup.number().min(5) // We could be passing different types of arguments here // so we'll try to transform them before calling the function // yup.object().shape({ test: yup.string()}) should also be transformed var convertedArguments = transformAll(argsToPass, previousInstance); // Handle the case when we've got an array of empty elements if (convertedArguments instanceof Array) { if (convertedArguments.filter(function (i) { return i; }).length < 1) { return gotFunc(); } // Spread the array over the function return gotFunc.apply(void 0, _toConsumableArray(convertedArguments)); } // Handle the case when we're passing another validator return gotFunc(convertedArguments); } /** * Transforms an array JSON schema to yup array schema. * * @param {Array} jsonArray - array in JSON to be transformed. * @returns {Array} Array with same keys, but values as yup validators. */ function transformArray(jsonArray) { var previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; var toReturn = convertArray(jsonArray[0]); jsonArray.slice(1).forEach(function (item) { // Found an array, move to prefix extraction if (item instanceof Array) { toReturn = convertArray(item, toReturn); return; } // Found an object, move to object extraction if (item instanceof Object) { toReturn = transformObject(item, previousInstance); return; } // Handle an edge case where we have something like // [['yup.ref', 'linkName'], 'custom error'], and we don't want // to consume 'custom error as a variable yet' if (toReturn instanceof Array) { toReturn = toReturn.concat(item); return; } toReturn = [toReturn, item]; }); return toReturn; } /** * Transforms an object JSON schema to yup object schema. * * @param {Object} jsonObject - Object in JSON to be transformed. * @returns {Object} Object with same keys, but values as yup validators. */ function transformObject(jsonObject) { var previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; var toReturn = {}; Object.entries(jsonObject).forEach(function (_ref3) { var _ref4 = _slicedToArray(_ref3, 2), key = _ref4[0], value = _ref4[1]; // Found an array move to array extraction if (value instanceof Array) { toReturn[key] = transformArray(value, previousInstance); return; } // Found an object recursive extraction if (value instanceof Object) { toReturn[key] = transformObject(value, previousInstance); return; } toReturn[key] = value; }); return toReturn; } /** * Steps into arrays and objects and resolve the items inside to yup validators. * @param {Any} jsonObjectOrArray - Object to be transformed. * @returns {yup.Validator} */ function transformAll(jsonObjectOrArray) { var previousInstance = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : yup; // We're dealing with an array // This could be a prefix notation function // If so, we'll call the converter if (jsonObjectOrArray instanceof Array) { if (isPrefixNotation(jsonObjectOrArray)) { return transformArray(jsonObjectOrArray, previousInstance); } return jsonObjectOrArray.map(function (i) { return transformAll(i, previousInstance); }); } // If we're dealing with an object // we should check each of the values for that object. // Some of them may also be prefix notation functiosn if (jsonObjectOrArray instanceof Object) { return transformObject(jsonObjectOrArray, previousInstance); } // No case here, just return anything else return jsonObjectOrArray; } /** * Can transform arrays or an object into a single validator. * This should be your initial entrypoint. * * @param {Any} jsonObjectOrArray - Object to be transformed. * @returns {yup.Validator} */ function transform(jsonObjectOrArray) { try { if (jsonObjectOrArray instanceof Object) { return transformAll([// build a custom validator which takes an object as parameter // If we don't do this, we'll get back an object of validators ["yup.object"], ["yup.required"], ["yup.shape", jsonObjectOrArray]]); } // No case here, just return anything else return transformAll(jsonObjectOrArray); } catch (error) { if (error instanceof ValidationError) { throw new Error("Could not validate " + JSON.stringify(jsonObjectOrArray, null, 4) + "\n".concat(error.message)); } throw error; } }