Skip to content

Commit

Permalink
feature(frontend): reusable zod validation schemas
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastien-comeau committed Jan 17, 2025
1 parent f336627 commit 3526b39
Show file tree
Hide file tree
Showing 18 changed files with 1,598 additions and 2 deletions.
2 changes: 0 additions & 2 deletions frontend/.vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
// clsx and cn
["clsx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
["cn\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"],
// Plain Javascript Object
":\\s*?[\"'`]([^\"'`]*).*?,",
// JavaScript string variable with keywords
[
"(?:\\b(?:const|let|var)\\s+)?[\\w$_]*(?:[Ss]tyles|[Cc]lasses|[Cc]lassnames)[\\w\\d]*\\s*(?:=|\\+=)\\s*['\"]([^'\"]*)['\"]"
Expand Down
87 changes: 87 additions & 0 deletions frontend/app/.server/validation/email-address-validation-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import isEmail from 'validator/es/lib/isEmail';
import type { z } from 'zod';

import { createStringValidationSchema } from '~/.server/validation/string-validation-schema';

/**
* Interface defining customizable error messages for email address validation schema.
*/
export interface EmailAddressValidationSchemaErrorMessages {
/**
* Error message for when the email address format is invalid.
* @default 'Email address format is invalid.'
*/
format_error?: string;

/**
* Error message for when the email address is not a string.
* @default 'Email address must be a string.'
*/
invalid_type_error?: string;

/**
* Error message for when the email address exceeds the maximum length.
* @default 'Email address must be less than or equal to {maximum} characters
*/
max_length_error?: string;

/**
* Error message for when the email address is required.
* @default 'Email address is required.'
*/
required_error?: string;
}

/**
* Configuration options for email address validation, including maximum length and error messages.
*/
export interface EmailAddressValidationSchemaOptions {
errorMessages?: EmailAddressValidationSchemaErrorMessages;
maxLength?: number;
}

const DEFAULT_MESSAGES = {
format_error: 'Email address format is invalid.',
invalid_type_error: 'Email address must be a string.',
max_length_error: 'Email address must be less than or equal to {maximum} characters.',
required_error: 'Email address is required.',
} as const satisfies Required<EmailAddressValidationSchemaErrorMessages>;

/**
* Creates a Zod schema for validating names with customizable options.
*
* @param options - Configuration options for validation.
* @returns A Zod schema for validating names.
*/
export function createEmailAddressValidationSchema(
options: EmailAddressValidationSchemaOptions = {},
): z.ZodEffects<z.ZodString> {
const defaultMaxEmailLength = 254; // matches validator.js

const { errorMessages = {}, maxLength = defaultMaxEmailLength } = options;

const messages: Required<EmailAddressValidationSchemaErrorMessages> = {
...DEFAULT_MESSAGES,
...errorMessages,
};

return createStringValidationSchema({
errorMessages: {
invalid_type_error: messages.invalid_type_error,
max_length_error: messages.max_length_error,
min_length_error: messages.required_error,
required_error: messages.required_error,
},
minLength: 1,
maxLength: Math.min(defaultMaxEmailLength, maxLength),
}).superRefine((email, ctx) => {
if (!email) return;
if (isEmail(email)) return;

ctx.addIssue({
code: 'custom',
message: messages.format_error,
fatal: true,
});
});
}
72 changes: 72 additions & 0 deletions frontend/app/.server/validation/first-name-validation-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { z } from 'zod';

import { createNameValidationSchema } from '~/.server/validation/name-validation-schema';

/**
* Interface defining customizable error messages for firstName validation schema.
*/
export interface FirstNameValidationSchemaErrorMessages {
/**
* Error message for when the first name contains digits.
* @default 'First name must not contain any digits.'
*/
format_error?: string;

/**
* Error message for when the first name is not a string.
* @default 'First name must be a string.'
*/
invalid_type_error?: string;

/**
* Error message for when the first name exceeds the maximum length.
* @default 'First name must contain at most {maximum} characters.'
*/
max_length_error?: string;

/**
* Error message for when the first name is required.
* @default 'First name is required.'
*/
required_error?: string;
}

/**
* Configuration options for firstName validation, including maximum length and error messages.
*/
export interface FirstNameValidationSchemaOptions {
errorMessages?: FirstNameValidationSchemaErrorMessages;
maxLength?: number;
}

const DEFAULT_MESSAGES = {
format_error: 'First name must not contain any digits.',
invalid_type_error: 'First name must be a string.',
max_length_error: 'First name must contain at most {maximum} characters.',
required_error: 'First name is required.',
} as const satisfies Required<FirstNameValidationSchemaErrorMessages>;

/**
* Creates a Zod schema for validating firstNames with customizable options.
*
* @param options - Configuration options for validation.
* @returns A Zod schema for validating firstNames.
*/
export function createFirstNameValidationSchema(options: FirstNameValidationSchemaOptions = {}): z.ZodString {
const { errorMessages = {}, maxLength = 100 } = options;

const messages: Required<FirstNameValidationSchemaErrorMessages> = {
...DEFAULT_MESSAGES,
...errorMessages,
};

return createNameValidationSchema({
errorMessages: {
format_error: messages.format_error,
invalid_type_error: messages.invalid_type_error,
max_length_error: messages.max_length_error,
required_error: messages.required_error,
},
maxLength,
});
}
5 changes: 5 additions & 0 deletions frontend/app/.server/validation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './email-address-validation-schema';
export * from './first-name-validation-schema';
export * from './last-name-validation-schema';
export * from './name-validation-schema';
export * from './string-validation-schema';
72 changes: 72 additions & 0 deletions frontend/app/.server/validation/last-name-validation-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { z } from 'zod';

import { createNameValidationSchema } from '~/.server/validation/name-validation-schema';

/**
* Interface defining customizable error messages for last name validation schema.
*/
export interface LastNameValidationSchemaErrorMessages {
/**
* Error message for when the last name contains digits.
* @default 'Last name must not contain any digits.'
*/
format_error?: string;

/**
* Error message for when the last name is not a string.
* @default 'Last name must be a string.'
*/
invalid_type_error?: string;

/**
* Error message for when the last name exceeds the maximum length.
* @default 'Last name must contain at most {maximum} characters.'
*/
max_length_error?: string;

/**
* Error message for when the last name is required.
* @default 'Last name is required.'
*/
required_error?: string;
}

/**
* Configuration options for last name validation, including maximum length and error messages.
*/
export interface LastNameValidationSchemaOptions {
errorMessages?: LastNameValidationSchemaErrorMessages;
maxLength?: number;
}

const DEFAULT_MESSAGES = {
format_error: 'Last name must not contain any digits.',
invalid_type_error: 'Last name must be a string.',
max_length_error: 'Last name must contain at most {maximum} characters.',
required_error: 'Last name is required.',
} as const satisfies Required<LastNameValidationSchemaErrorMessages>;

/**
* Creates a Zod schema for validating last names with customizable options.
*
* @param options - Configuration options for validation.
* @returns A Zod schema for validating last names.
*/
export function createLastNameValidationSchema(options: LastNameValidationSchemaOptions = {}): z.ZodString {
const { errorMessages = {}, maxLength = 100 } = options;

const messages: Required<LastNameValidationSchemaErrorMessages> = {
...DEFAULT_MESSAGES,
...errorMessages,
};

return createNameValidationSchema({
errorMessages: {
format_error: messages.format_error,
invalid_type_error: messages.invalid_type_error,
max_length_error: messages.max_length_error,
required_error: messages.required_error,
},
maxLength,
});
}
56 changes: 56 additions & 0 deletions frontend/app/.server/validation/name-validation-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import type { z } from 'zod';

import { createStringValidationSchema } from '~/.server/validation/string-validation-schema';

/**
* Interface defining customizable error messages for name validation schema.
*/
export interface NameValidationSchemaErrorMessages {
format_error?: string;
invalid_type_error?: string;
max_length_error?: string;
required_error?: string;
}

/**
* Configuration options for name validation, including maximum length and error messages.
*/
export interface NameValidationSchemaOptions {
errorMessages?: NameValidationSchemaErrorMessages;
maxLength?: number;
}

const DEFAULT_MESSAGES = {
format_error: 'Name must not contain any digits.',
invalid_type_error: 'Name must be a string.',
max_length_error: 'Name must contain at most {maximum} characters.',
required_error: 'Name is required.',
} as const satisfies Required<NameValidationSchemaErrorMessages>;

/**
* Creates a Zod schema for validating names with customizable options.
*
* @param options - Configuration options for validation.
* @returns A Zod schema for validating names.
*/
export function createNameValidationSchema(options: NameValidationSchemaOptions = {}): z.ZodString {
const { errorMessages = {}, maxLength = 100 } = options;

const messages: Required<NameValidationSchemaErrorMessages> = {
...DEFAULT_MESSAGES,
...errorMessages,
};

return createStringValidationSchema({
errorMessages: {
format_error: messages.format_error,
invalid_type_error: messages.invalid_type_error,
max_length_error: messages.max_length_error,
min_length_error: messages.required_error,
required_error: messages.required_error,
},
format: 'non-digit',
minLength: 1,
maxLength,
});
}
Loading

0 comments on commit 3526b39

Please sign in to comment.