UNPKG

silvie

Version:

Typescript Back-end Framework

205 lines (185 loc) 7.17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _rule = require("./rule"); var _messages = _interopRequireDefault(require("./messages")); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } class Validator { static findData(data, path, traversed = []) { // Iterate over all path parts for (let index = 0; index < path.length; index++) { const part = path[index]; // If the current part is a wildcard if (data !== undefined && data !== null && part === '*') { // And it is the last part if (index === path.length - 1) { // Return an array of all items in the current path return Object.keys(data).map(key => { return { path: traversed.concat(key), value: data[key] }; }); } // If it is not the last part, // run this function on all items in the current path, // and return a flattened array of its results return Object.keys(data).map(key => { return Validator.findData(data[key], path.slice(index + 1), traversed.concat(key)); }).flat(); } // If this path part is in the current data section if (data !== undefined && data !== null && part in data) { // Get its value const value = data[part]; // If it is the last part, return the found value if (index === path.length - 1) { return [{ path: traversed.concat(part), value }]; } // If it is not the last part, run the function in on the found value return Validator.findData(value, path.slice(index + 1), path.slice(0, index + 1)); } } // Return an empty array, if none of above worked return []; } static parseRules(rules) { const parsedRules = {}; // Iterate over rule keys Object.keys(rules).forEach(key => { // Create an empty array for that key parsedRules[key] = []; // Split the rules with '|' rules[key].split('|').forEach(ruleStr => { // Get rule name and parameters by splitting with ':' const [name, ...restParams] = ruleStr.split(':'); const paramsStr = restParams.join(':'); // Get rule const rule = _rule.validationRules[name]; // If there is no rule throw an error if (!rule) { throw new Error(`Validation rule '${name}' not exists.`); } // Get parameters by splitting them with ',' const params = paramsStr ? paramsStr.split(',').map(param => param.trim()) : []; // Add the rule to the array parsedRules[key].push({ name, handler: rule.validate, messenger: rule.messenger, params }); }); }); return parsedRules; } placeErrorMessage(path, messages) { let current = this.errors; path.forEach((part, index) => { if (index < path.length - 1) { if (!(part in current)) { current[part] = {}; } current = current[part]; } else { current[part] = messages; } }); } executeRules(fieldName, exactPath, value, rules) { const messages = []; // Iterate over matching rules for (let ruleIndex = 0; ruleIndex < rules.length; ruleIndex++) { const rule = rules[ruleIndex]; // Run the rule handler const validationResult = rule.handler(this, exactPath, value, ...rule.params); // If it is undefined, Break with no errors if (validationResult === undefined) { break; } // If it is not true, Error message if (validationResult !== true) { // Find raw error message const rawMessage = rule.messenger && rule.messenger(validationResult) || this.messages[`${exactPath}:${rule.name}`] || this.messages[`${fieldName}:${rule.name}`] || _messages.default[rule.name]; if (rawMessage) { const rawMessages = []; // Replace placeholders if (typeof rawMessage === 'string') { rawMessages.push(rawMessage); } else if (rawMessage instanceof Array) { rawMessages.push(...rawMessage); } rawMessages.forEach(msg => { const message = msg.replace(/:path/g, exactPath).replace(/:field/g, fieldName).replace(/:name/g, exactPath.split('.').pop()).replace(/:params/g, rule.params.join(', ')).replace(/:(\d+)/g, (match, param) => rule.params[param] || ''); messages.push(message); }); } else { throw new Error(`Could not find a message for '${rule.name}' at '${exactPath}'`); } } // If it is null, Break after error if (validationResult === null) { break; } } return messages; } validate() { this.errors = {}; this.hasErrors = false; Object.keys(this.rules).forEach(fieldName => { const rules = this.rules[fieldName]; const pathParts = fieldName.split('.'); const results = Validator.findData(this.data, pathParts); if (results.length === 0) { results.push({ path: pathParts, value: undefined }); } // Iterate over found data for (let resultIndex = 0; resultIndex < results.length; resultIndex++) { // Get exact path and value of that item const { path, value } = results[resultIndex]; const exactPath = path.join('.'); const messages = this.executeRules(fieldName, exactPath, value, rules); if (messages.length > 0) { if (!this.hasErrors) { this.hasErrors = true; } if (this.generateNestedErrors) { this.placeErrorMessage(path, messages); } else { this.errors[exactPath] = messages; } } } }); } constructor(data, rules, messages, generateNestedErrors = true) { _defineProperty(this, "data", void 0); _defineProperty(this, "rules", void 0); _defineProperty(this, "messages", void 0); _defineProperty(this, "errors", void 0); _defineProperty(this, "hasErrors", false); _defineProperty(this, "generateNestedErrors", true); this.data = data; this.rules = Validator.parseRules(rules); this.messages = messages || {}; this.errors = {}; this.generateNestedErrors = generateNestedErrors; this.validate(); } } exports.default = Validator;