diff --git a/.gitignore b/.gitignore index bbde1f5..b839177 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules /package .env .env.* +.pnpm-debug.log diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a0da66..3368deb 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "javascript.preferences.importModuleSpecifierEnding": "js", - "typescript.preferences.importModuleSpecifierEnding": "js" + "typescript.preferences.importModuleSpecifierEnding": "js", + "editor.defaultFormatter": "esbenp.prettier-vscode" } diff --git a/src/docs/6_Validators.md b/src/docs/6_Validators.md index af25477..c331b2d 100644 --- a/src/docs/6_Validators.md +++ b/src/docs/6_Validators.md @@ -5,7 +5,8 @@ filename: 6_Validators.md ## Validators - validators now need to be called directly, thus providing type safe auto-completion -- validators now return a function that return an object `{ valid: boolean, name: string = 'validator_name' }` +- validators now return a function that return an object `{ valid: boolean, name: string = 'validator_name' } +- All built-in validators can be passed an additional parameter with a custom error name, e.g. `required('field is required')` or `between(0, 10, 'not between 1 and 10')` Check [custom validators](#custom-validator) for more info diff --git a/src/lib/validators/between.ts b/src/lib/validators/between.ts index a9f6370..455542b 100644 --- a/src/lib/validators/between.ts +++ b/src/lib/validators/between.ts @@ -1,8 +1,8 @@ import type { Validator } from './validator.js'; -export function between(min: number, max: number): Validator { +export function between(min: number, max: number, name = 'between'): Validator { return (value: any) => { const val = isNaN(value) ? value.length : parseFloat(value); - return { valid: val >= min && val <= max, name: 'between' }; + return { valid: val >= min && val <= max, name }; }; } diff --git a/src/lib/validators/email.ts b/src/lib/validators/email.ts index f7c4f1c..5107a33 100644 --- a/src/lib/validators/email.ts +++ b/src/lib/validators/email.ts @@ -1,8 +1,8 @@ import type { Validator } from './validator.js'; -export function email(): Validator { +export function email(name = 'not_an_email'): Validator { return (value: any) => { const regex = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/; - return { valid: Boolean(value) && regex.test(value), name: 'not_an_email' }; + return { valid: Boolean(value) && regex.test(value), name }; }; } diff --git a/src/lib/validators/matchField.ts b/src/lib/validators/matchField.ts index b3d6705..dc10096 100644 --- a/src/lib/validators/matchField.ts +++ b/src/lib/validators/matchField.ts @@ -2,8 +2,8 @@ import type { Field } from '$lib/types'; import type { Readable } from 'svelte/store'; import { get } from 'svelte/store'; -export function matchField(store: Readable>) { +export function matchField(store: Readable>, name = 'match_field') { return (value) => { - return { valid: get(store).value === value, name: 'match_field' }; + return { valid: get(store).value === value, name }; }; } diff --git a/src/lib/validators/max.ts b/src/lib/validators/max.ts index d77e103..fb8e83e 100644 --- a/src/lib/validators/max.ts +++ b/src/lib/validators/max.ts @@ -1,9 +1,9 @@ import type { Validator } from './validator.js'; -export function max(n: number): Validator { +export function max(n: number, name = 'max'): Validator { return (value: any) => { const val = typeof value === 'string' ? value.length : isNaN(value) ? 0 : parseFloat(value); - return { valid: val <= n, name: 'max' }; + return { valid: val <= n, name }; }; } diff --git a/src/lib/validators/min.ts b/src/lib/validators/min.ts index d4bf1be..3977678 100644 --- a/src/lib/validators/min.ts +++ b/src/lib/validators/min.ts @@ -1,8 +1,8 @@ import type { Validator } from './validator.js'; -export function min(n: number): Validator { +export function min(n: number, name = 'min'): Validator { return (value: any) => { const val = isNaN(value) ? value.length : parseFloat(value); - return { valid: val >= n, name: 'min' }; + return { valid: val >= n, name }; }; } diff --git a/src/lib/validators/pattern.ts b/src/lib/validators/pattern.ts index 995f71d..d09b172 100644 --- a/src/lib/validators/pattern.ts +++ b/src/lib/validators/pattern.ts @@ -1,11 +1,11 @@ import type { Validator } from './validator.js'; -export function pattern(pattern: RegExp): Validator { +export function pattern(pattern: RegExp, name = 'pattern'): Validator { return (value: any) => { if (value === null || value === undefined) { return { valid: false, name: 'pattern' }; } - return { valid: pattern.test(value), name: 'pattern' }; + return { valid: pattern.test(value), name }; }; } diff --git a/src/lib/validators/required.ts b/src/lib/validators/required.ts index b1b2fcc..ef23e6a 100644 --- a/src/lib/validators/required.ts +++ b/src/lib/validators/required.ts @@ -1,6 +1,6 @@ import type { Validator } from './validator.js'; -export function required(): Validator { +export function required(name = 'required'): Validator { return (val: string) => { let valid = true; if (val === undefined || val === null) valid = false; @@ -11,6 +11,6 @@ export function required(): Validator { valid = tmp.length > 0; } - return { valid, name: 'required' }; + return { valid, name }; }; } diff --git a/src/lib/validators/url.ts b/src/lib/validators/url.ts index 94eed31..85d1c4d 100644 --- a/src/lib/validators/url.ts +++ b/src/lib/validators/url.ts @@ -1,7 +1,7 @@ import type { Validator } from './validator.js'; -export function url(): Validator { +export function url(name = 'url'): Validator { const regex = /(https?|ftp|git|svn):\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-z]{2,63}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/i; - return (value: string) => ({ valid: regex.test(value), name: 'url' }); + return (value: string) => ({ valid: regex.test(value), name }); } diff --git a/src/tests/validators/customErrorName.test.ts b/src/tests/validators/customErrorName.test.ts new file mode 100644 index 0000000..07ab071 --- /dev/null +++ b/src/tests/validators/customErrorName.test.ts @@ -0,0 +1,34 @@ +import cases from 'jest-in-case'; +import * as v from '$lib/validators'; +import { field } from '$lib/field'; +import type { FieldValidation } from '$lib'; + +const valueOfFieldToMatchAgainst = 10; +const fieldToMatchAgainst = field('fieldToMatchAgainst', valueOfFieldToMatchAgainst); + +cases( + 'max(max)', + (opts) => { + const result = opts.validator( + ...(opts.args || []), + 'custom message' + )(opts.invalidValue) as FieldValidation; + + expect(result.valid).toBe(false); + expect(result.name).toBe('custom message'); + }, + { + between: _(v.between, 50, [0, 10]), + email: _(v.email, 0), + matchField: _(v.matchField, valueOfFieldToMatchAgainst + 10, [fieldToMatchAgainst]), + max: _(v.max, 15, [10]), + min: _(v.min, 5, [10]), + pattern: _(v.pattern, 'foo', [/bar/]), + required: _(v.required, null), + url: _(v.url, 5) + } +); + +function _(validator, invalidValue, args = []) { + return { validator, invalidValue, args }; +}