UNPKG

validlyjs

Version:

ValidlyJS is a lightweight, type-safe validation library inspired by Laravel's validation syntax

171 lines 6.51 kB
import { parseRules } from "../parsers/index.js"; import { getRuleHandler } from "../rules/index.js"; import { defaultConfig, setLocale, getConfig } from "../config.js"; import { prepareValue } from "../utils/common.js"; import { DATA_TYPES } from "../utils/typeCheck.js"; export class Validator { rules; config; constructor(schema, config) { this.rules = new Map(); this.config = { ...defaultConfig, schema, ...config }; Object.entries(schema).forEach(([field, ruleDef]) => { const rules = parseRules(ruleDef); const dataTypeRules = rules.filter((rule) => DATA_TYPES.includes(rule.name) && !rule.custom); if (dataTypeRules.length > 1) { throw new Error(`Field "${field}" cannot have more than one data type rule (string, number, boolean, array, date, file or object).`); } this.rules.set(field, rules); }); } setLocale(locale) { setLocale(locale); this.config = { ...this.config, ...getConfig() }; return this; } updateCleanData(field, value, cleanData) { const parts = field.split('.'); let target = cleanData; for (let i = 0; i < parts.length - 1; i++) { const key = parts[i]; if (!target[key]) { if (!isNaN(Number(parts[i + 1]))) { target[key] = []; } else { target[key] = {}; } } target = target[key]; } target[parts[parts.length - 1]] = value; } async validateField(field, rules, data, cleanData, errors, isAsync = false) { const parts = field.split('.'); let currentObj = data; let value = null; let isValid = true; for (let i = 0; i < parts.length; i++) { const part = parts[i]; if (currentObj === undefined || currentObj === null) { isValid = false; break; } if (i === parts.length - 1) { value = currentObj[part]; } else { currentObj = currentObj[part]; } } if (!isValid) { errors.set(field, ["The specified path does not exist"]); return; } const isNullable = rules.some((rule) => rule.name === "nullable"); const isRequired = rules.some((rule) => rule.name === "required"); if (isNullable && (value === null || value === undefined)) { this.updateCleanData(field, value, cleanData); return; } if (isRequired && (value === null || value === undefined || value === "")) { const handler = getRuleHandler("required"); const message = handler.message([], { value, data, field, config: this.config, schema: this.config.schema, formatMessage: this.formatMessage.bind(this), }); errors.set(field, [message]); return; } const preparedValue = prepareValue(value, this.config); const context = { value: preparedValue, data, field, config: this.config, schema: this.config.schema, formatMessage: this.formatMessage.bind(this), }; const dataType = rules.find((rule) => DATA_TYPES.includes(rule.name))?.name; for (const rule of rules) { if (rule.name === "nullable") continue; try { const handler = getRuleHandler(rule.name, dataType); let validationResult; if (isAsync) { validationResult = await handler.validate(preparedValue, rule.params, context); } else { validationResult = handler.validate(preparedValue, rule.params, context); } if (!validationResult) { const message = handler.message(rule.params, context); const fieldErrors = errors.get(field) || []; fieldErrors.push(message); errors.set(field, fieldErrors); if (this.config.bail) break; } } catch (error) { console.error(`Validation error for rule ${rule.name} on field ${field}: `, error.message); } } if (value !== undefined) { this.updateCleanData(field, value, cleanData); } } validate(data) { const errors = new Map(); const cleanData = {}; for (const [field, rules] of this.rules.entries()) { this.validateField(field, rules, data, cleanData, errors, false); } return { isValid: errors.size === 0, data: cleanData, errors: Object.fromEntries(errors), }; } async validateAsync(data) { const errors = new Map(); const cleanData = {}; for (const [field, rules] of this.rules.entries()) { await this.validateField(field, rules, data, cleanData, errors, true); } return { isValid: errors.size === 0, data: cleanData, errors: Object.fromEntries(errors), }; } formatMessage(params, defaultMessage) { if (typeof defaultMessage === "string") { return this.replaceMessageParams(defaultMessage, params); } if (typeof defaultMessage === "object") { console.log(params, defaultMessage); const [ruleName, subKey] = Object.keys(params)[0].split("."); if (ruleName && subKey && defaultMessage[ruleName]) { const message = defaultMessage[ruleName]; if (typeof message === "string") { return this.replaceMessageParams(message, params); } if (typeof message === "object" && message[subKey]) { return this.replaceMessageParams(message[subKey], params); } } } return "Validation error"; } replaceMessageParams(message, params) { return message.replace(/:(\w+)/g, (_, key) => params[key] || ""); } } //# sourceMappingURL=Validator.js.map