diff --git a/README.md b/README.md index 186fd90..de49a6c 100644 --- a/README.md +++ b/README.md @@ -369,7 +369,28 @@ const v = new FastestValidator({ } }); ``` +# Label Option +You can use label names in error messages instead of property names. +```js +const schema = { + email: { type: "email", label: "Email Address" }, +}; +const check = v.compile(schema); + +console.log(check({ email: "notAnEmail" })); +/* Returns +[ + { + type: 'email', + message: "The 'Email Address' field must be a valid e-mail.", + field: 'email', + actual: 'notAnEmail', + label: 'Email Address' + } +] +*/ +``` # Built-in validators ## `any` diff --git a/lib/rules/object.js b/lib/rules/object.js index fe93f37..0b87b0c 100644 --- a/lib/rules/object.js +++ b/lib/rules/object.js @@ -56,13 +56,16 @@ module.exports = function ({ schema, messages }, path, context) { const safePropName = `parentObj${safeSubName}`; const newPath = (path ? path + "." : "") + property; + const labelName = subSchema[property].label; + const label = labelName ? `'${escapeEvalString(labelName)}'` : undefined; + sourceCode.push(`\n// Field: ${escapeEvalString(newPath)}`); sourceCode.push(`field = parentField ? parentField + "${safeSubName}" : "${name}";`); sourceCode.push(`value = ${safePropName};`); - + sourceCode.push(`label = ${label}`); const rule = this.getRuleFromSchema(subSchema[property]); const innerSource = ` - ${safePropName} = ${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, parentObj, errors, context); + ${safePropName} = ${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, parentObj, errors, context, label); `; sourceCode.push(this.compileRule(rule, context, newPath, innerSource, safePropName)); if (this.opts.haltOnFirstError === true) { diff --git a/lib/validator.js b/lib/validator.js index 380425b..d39b697 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -201,20 +201,21 @@ class Validator { "var errors = [];", "var field;", "var parent = null;", + `var label = ${schema.label ? "\"" + schema.label + "\"" : "null"};` ]; const rule = this.getRuleFromSchema(schema); - sourceCode.push(this.compileRule(rule, context, null, `${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, null, errors, context);`, "value")); + sourceCode.push(this.compileRule(rule, context, null, `${context.async ? "await " : ""}context.fn[%%INDEX%%](value, field, null, errors, context, label);`, "value")); sourceCode.push("if (errors.length) {"); sourceCode.push(` return errors.map(err => { if (err.message) { - err.message = context.utils.replace(err.message, /\\{field\\}/g, err.field); + err.message = context.utils.replace(err.message, /\\{field\\}/g, err.label || err.field); err.message = context.utils.replace(err.message, /\\{expected\\}/g, err.expected); err.message = context.utils.replace(err.message, /\\{actual\\}/g, err.actual); } - + if(!err.label) delete err.label return err; }); `); @@ -282,7 +283,7 @@ class Validator { const res = rule.ruleFunction.call(this, rule, path, context); res.source = res.source.replace(/%%INDEX%%/g, rule.index); const FnClass = context.async ? AsyncFunction : Function; - const fn = new FnClass("value", "field", "parent", "errors", "context", res.source); + const fn = new FnClass("value", "field", "parent", "errors", "context", "label", res.source); context.fn[rule.index] = fn.bind(this); sourceCode.push(this.wrapRequiredCheckSourceCode(rule, innerSrc.replace(/%%INDEX%%/g, rule.index), context, resVar)); sourceCode.push(this.makeCustomValidator({vName: resVar, path: customPath, schema: rule.schema, context, messages: rule.messages, ruleIndex: rule.index})); @@ -380,6 +381,7 @@ class Validator { else o.field = "field"; if (expected != null) o.expected = expected; if (actual != null) o.actual = actual; + o.label = "label"; const s = Object.keys(o) .map(key => `${key}: ${o[key]}`) diff --git a/test/typescript/validator.spec.ts b/test/typescript/validator.spec.ts index c056da9..843d78e 100644 --- a/test/typescript/validator.spec.ts +++ b/test/typescript/validator.spec.ts @@ -229,10 +229,10 @@ describe('TypeScript Definitions', () => { it('should generate an error creation code', () => { expect(v.makeError({ type: 'required', messages: v.messages })). - toBe('errors.push({ type: "required", message: "The \'{field}\' field is required.", field: field });'); + toBe('errors.push({ type: "required", message: "The \'{field}\' field is required.", field: field, label: label });'); expect(v.makeError({ type: 'stringMin', field: 'firstName', expected: 6, actual: 3, messages: v.messages })). toBe( - 'errors.push({ type: "stringMin", message: "The \'{field}\' field length must be greater than or equal to {expected} characters long.", field: "firstName", expected: 6, actual: 3 });'); + 'errors.push({ type: "stringMin", message: "The \'{field}\' field length must be greater than or equal to {expected} characters long.", field: "firstName", expected: 6, actual: 3, label: label });'); }); }); diff --git a/test/validator.spec.js b/test/validator.spec.js index c635b9e..c6ed2b1 100644 --- a/test/validator.spec.js +++ b/test/validator.spec.js @@ -294,8 +294,8 @@ describe("Test makeError", () => { const v = new Validator(); it("should generate an error creation code", () => { - expect(v.makeError({ type: "required", messages: v.messages })).toBe("errors.push({ type: \"required\", message: \"The '{field}' field is required.\", field: field });"); - expect(v.makeError({ type: "stringMin", field: "firstName", expected: 6, actual: 3, messages: v.messages })).toBe("errors.push({ type: \"stringMin\", message: \"The '{field}' field length must be greater than or equal to {expected} characters long.\", field: \"firstName\", expected: 6, actual: 3 });"); + expect(v.makeError({ type: "required", messages: v.messages })).toBe("errors.push({ type: \"required\", message: \"The '{field}' field is required.\", field: field, label: label });"); + expect(v.makeError({ type: "stringMin", field: "firstName", expected: 6, actual: 3, messages: v.messages })).toBe("errors.push({ type: \"stringMin\", message: \"The '{field}' field length must be greater than or equal to {expected} characters long.\", field: \"firstName\", expected: 6, actual: 3, label: label });"); }); }); @@ -340,6 +340,22 @@ describe("Test compile (integration test)", () => { }); + describe("Test label in error message instead of property names", () => { + const v = new Validator(); + const schema = { + email: { type: "email", label: "Email Address" }, + }; + + let check = v.compile(schema); + + it("Should return message with label value", () => { + let res = check({}); + + expect(res[0].label).toBe(schema.email.label); + expect(res[0].message).toBe("The 'Email Address' field is required."); + }); + }); + describe("Test check generator with wrong obj and haltOnFirstError", () => { const v = new Validator({ haltOnFirstError: true });