Skip to content

Commit

Permalink
perf: reduce joi resolver's size (#131)
Browse files Browse the repository at this point in the history
* perf: reduce Joi resolver's size

* perf: reduce Vest resolver's size
  • Loading branch information
jorisre authored Feb 6, 2021
1 parent 6e86b4f commit 3af7bd7
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 70 deletions.
6 changes: 4 additions & 2 deletions joi/src/__tests__/__fixtures__/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export const schema = Joi.object({
tags: Joi.array().items(Joi.string()).required(),
enabled: Joi.boolean().required(),
like: Joi.array()
.items(Joi.object({ id: Joi.number(), name: Joi.string().length(4) }))
.items(
Joi.object({ id: Joi.number(), name: Joi.string().length(4).regex(/a/) }),
)
.optional(),
});

Expand Down Expand Up @@ -51,7 +53,7 @@ export const invalidData = {
password: '___',
email: '',
birthYear: 'birthYear',
like: [{ id: 'z' }],
like: [{ id: 'z', name: 'r' }],
};

export const fields: Record<InternalFieldName, Field['_f']> = {
Expand Down
28 changes: 28 additions & 0 deletions joi/src/__tests__/__snapshots__/joi.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ Object {
"ref": undefined,
"type": "number.base",
},
"name": Object {
"message": "\\"like[0].name\\" length must be 4 characters long",
"ref": undefined,
"type": "string.length",
},
},
],
"password": Object {
Expand Down Expand Up @@ -80,6 +85,11 @@ Object {
"ref": undefined,
"type": "number.base",
},
"name": Object {
"message": "\\"like[0].name\\" length must be 4 characters long",
"ref": undefined,
"type": "string.length",
},
},
],
"password": Object {
Expand Down Expand Up @@ -145,6 +155,15 @@ Object {
"number.base": "\\"like[0].id\\" must be a number",
},
},
"name": Object {
"message": "\\"like[0].name\\" length must be 4 characters long",
"ref": undefined,
"type": "string.length",
"types": Object {
"string.length": "\\"like[0].name\\" length must be 4 characters long",
"string.pattern.base": "\\"like[0].name\\" with value \\"r\\" fails to match the required pattern: /a/",
},
},
},
],
"password": Object {
Expand Down Expand Up @@ -219,6 +238,15 @@ Object {
"number.base": "\\"like[0].id\\" must be a number",
},
},
"name": Object {
"message": "\\"like[0].name\\" length must be 4 characters long",
"ref": undefined,
"type": "string.length",
"types": Object {
"string.length": "\\"like[0].name\\" length must be 4 characters long",
"string.pattern.base": "\\"like[0].name\\" with value \\"r\\" fails to match the required pattern: /a/",
},
},
},
],
"password": Object {
Expand Down
113 changes: 46 additions & 67 deletions joi/src/joi.ts
Original file line number Diff line number Diff line change
@@ -1,84 +1,63 @@
import { appendErrors } from 'react-hook-form';
import { appendErrors, FieldError } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';
import * as Joi from 'joi';
import { convertArrayToPathName } from '@hookform/resolvers';
import type { ValidationError } from 'joi';
import { Resolver } from './types';

const parseErrorSchema = (
error: Joi.ValidationError,
error: ValidationError,
validateAllFieldCriteria: boolean,
) =>
Array.isArray(error.details)
? error.details.reduce(
(previous: Record<string, any>, { path, message = '', type }) => {
const currentPath = convertArrayToPathName(path);
error.details.length
? error.details.reduce<Record<string, FieldError>>((previous, error) => {
const _path = error.path.join('.');

return {
...previous,
...(path
? previous[currentPath] && validateAllFieldCriteria
? {
[currentPath]: appendErrors(
currentPath,
validateAllFieldCriteria,
previous,
type,
message,
),
}
: {
[currentPath]: previous[currentPath] || {
message,
type,
...(validateAllFieldCriteria
? {
types: { [type]: message || true },
}
: {}),
},
}
: {}),
};
},
{},
)
: [];
if (!previous[_path]) {
previous[_path] = { message: error.message, type: error.type };
}

if (validateAllFieldCriteria) {
previous[_path] = appendErrors(
_path,
validateAllFieldCriteria,
previous,
error.type,
error.message,
) as FieldError;
}

return previous;
}, {})
: {};

export const joiResolver: Resolver = (
schema,
schemaOptions = {
abortEarly: false,
},
{ mode } = { mode: 'async' },
) => async (values, context, { criteriaMode, fields }) => {
try {
let result;
if (mode === 'async') {
result = await schema.validateAsync(values, {
...schemaOptions,
context,
});
} else {
const { value, error } = schema.validate(values, {
...schemaOptions,
context,
});

if (error) {
throw error;
}
resolverOptions = {},
) => async (values, context, options) => {
const _schemaOptions = Object.assign(Object.assign({}, schemaOptions), {
context,
});

result = value;
let result: Record<string, any> = {};
if (resolverOptions.mode === 'sync') {
result = schema.validate(values, _schemaOptions);
} else {
try {
result.value = await schema.validateAsync(values, _schemaOptions);
} catch (e) {
result.error = e;
}

return {
values: result,
errors: {},
};
} catch (e) {
return {
values: {},
errors: toNestError(parseErrorSchema(e, criteriaMode === 'all'), fields),
};
}

return {
values: result.error ? {} : result.value,
errors: result.error
? toNestError(
parseErrorSchema(result.error, options.criteriaMode === 'all'),
options.fields,
)
: {},
};
};
2 changes: 1 addition & 1 deletion joi/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { AsyncValidationOptions, Schema } from 'joi';
export type Resolver = <T extends Schema>(
schema: T,
schemaOptions?: AsyncValidationOptions,
factoryOptions?: { mode: 'async' | 'sync' },
factoryOptions?: { mode?: 'async' | 'sync' },
) => <TFieldValues extends FieldValues, TContext>(
values: UnpackNestedValue<TFieldValues>,
context: TContext | undefined,
Expand Down

0 comments on commit 3af7bd7

Please sign in to comment.