Skip to content

Commit

Permalink
fix(zodResolver): improve unionErrors parsing (#168)
Browse files Browse the repository at this point in the history
* fix(zodresolver): improve unionErrors parsin

* refactor: format code with eslint/prettier
  • Loading branch information
jorisre authored May 16, 2021
1 parent d88bc94 commit f67a10f
Show file tree
Hide file tree
Showing 17 changed files with 364 additions and 309 deletions.
5 changes: 1 addition & 4 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
extends: ['plugin:@typescript-eslint/recommended', 'prettier'],
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ TypeScript-first schema validation with static type inference
```tsx
import React from 'react';
import { useForm } from 'react-hook-form';
import { computedTypesResolver } from '@hookform/resolvers/zod';
import { computedTypesResolver } from '@hookform/resolvers/computed-types';
import Schema, { number, string } from 'computed-types';

const schema = Schema({
Expand Down
40 changes: 19 additions & 21 deletions class-validator/src/class-validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,22 @@ const parseErrors = (
}, parsedErrors);
};

export const classValidatorResolver: Resolver = (
schema,
schemaOptions = {},
resolverOptions = {},
) => async (values, _, options) => {
const user = plainToClass(schema, values);

const rawErrors = await (resolverOptions.mode === 'sync'
? validateSync
: validate)(user, schemaOptions);

return rawErrors.length
? {
values: {},
errors: toNestError(
parseErrors(rawErrors, options.criteriaMode === 'all'),
options.fields,
),
}
: { values, errors: {} };
};
export const classValidatorResolver: Resolver =
(schema, schemaOptions = {}, resolverOptions = {}) =>
async (values, _, options) => {
const user = plainToClass(schema, values);

const rawErrors = await (resolverOptions.mode === 'sync'
? validateSync
: validate)(user, schemaOptions);

return rawErrors.length
? {
values: {},
errors: toNestError(
parseErrors(rawErrors, options.criteriaMode === 'all'),
options.fields,
),
}
: { values, errors: {} };
};
31 changes: 14 additions & 17 deletions computed-types/src/computed-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,17 @@ const parseErrorSchema = (
}, parsedErrors);
};

export const computedTypesResolver: Resolver = (schema) => async (
values,
_,
options,
) => {
try {
return {
errors: {},
values: await schema(values),
};
} catch (error) {
return {
values: {},
errors: toNestError(parseErrorSchema(error), options.fields),
};
}
};
export const computedTypesResolver: Resolver =
(schema) => async (values, _, options) => {
try {
return {
errors: {},
values: await schema(values),
};
} catch (error) {
return {
values: {},
errors: toNestError(parseErrorSchema(error), options.fields),
};
}
};
26 changes: 13 additions & 13 deletions io-ts/src/errorsToRecord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const formatError = (e: t.ValidationError): FieldErrorWithPath => {
// this is almost the same function like Semigroup.getObjectSemigroup but reversed
// in order to get the first error
const getObjectSemigroup = <
A extends Record<string, unknown> = never
A extends Record<string, unknown> = never,
>(): SemiGroup.Semigroup<A> => ({
concat: (first, second) => Object.assign({}, second, first),
});
Expand Down Expand Up @@ -106,10 +106,10 @@ const concatToMultipleErrors = (
errors: ReadonlyArray<FieldErrorWithPath>,
): ErrorObject =>
pipe(
ReadonlyRecord.fromFoldableMap(
appendSeveralErrors,
ReadonlyArray.Foldable,
)(errors, (error) => [error.path, error]),
ReadonlyRecord.fromFoldableMap(appendSeveralErrors, ReadonlyArray.Foldable)(
errors,
(error) => [error.path, error],
),
ReadonlyRecord.map((errorWithPath) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { path, ...error } = errorWithPath;
Expand All @@ -118,14 +118,14 @@ const concatToMultipleErrors = (
}),
);

const errorsToRecord = (validateAllFieldCriteria: boolean) => (
validationErrors: ReadonlyArray<ValidationError>,
): ErrorObject => {
const concat = validateAllFieldCriteria
? concatToMultipleErrors
: concatToSingleError;
const errorsToRecord =
(validateAllFieldCriteria: boolean) =>
(validationErrors: ReadonlyArray<ValidationError>): ErrorObject => {
const concat = validateAllFieldCriteria
? concatToMultipleErrors
: concatToSingleError;

return pipe(validationErrors, ReadonlyArray.map(formatError), concat);
};
return pipe(validationErrors, ReadonlyArray.map(formatError), concat);
};

export default errorsToRecord;
58 changes: 30 additions & 28 deletions joi/src/joi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,35 +34,37 @@ const parseErrorSchema = (
}, {})
: {};

export const joiResolver: Resolver = (
schema,
schemaOptions = {
abortEarly: false,
},
resolverOptions = {},
) => async (values, context, options) => {
const _schemaOptions = Object.assign({}, schemaOptions, {
context,
});
export const joiResolver: Resolver =
(
schema,
schemaOptions = {
abortEarly: false,
},
resolverOptions = {},
) =>
async (values, context, options) => {
const _schemaOptions = Object.assign({}, schemaOptions, {
context,
});

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;
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.error ? {} : result.value,
errors: result.error
? toNestError(
parseErrorSchema(result.error, options.criteriaMode === 'all'),
options.fields,
)
: {},
return {
values: result.error ? {} : result.value,
errors: result.error
? toNestError(
parseErrorSchema(result.error, options.criteriaMode === 'all'),
options.fields,
)
: {},
};
};
};
28 changes: 15 additions & 13 deletions nope/src/nope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,19 @@ const parseErrors = (
}, parsedErrors);
};

export const nopeResolver: Resolver = (
schema,
schemaOptions = {
abortEarly: false,
},
) => (values, context, options) => {
const result = schema.validate(values, context, schemaOptions) as
| ShapeErrors
| undefined;
export const nopeResolver: Resolver =
(
schema,
schemaOptions = {
abortEarly: false,
},
) =>
(values, context, options) => {
const result = schema.validate(values, context, schemaOptions) as
| ShapeErrors
| undefined;

return result
? { values: {}, errors: toNestError(parseErrors(result), options.fields) }
: { values, errors: {} };
};
return result
? { values: {}, errors: toNestError(parseErrors(result), options.fields) }
: { values, errors: {} };
};
19 changes: 9 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,18 @@
"homepage": "https://react-hook-form.com",
"devDependencies": {
"@testing-library/jest-dom": "^5.12.0",
"@testing-library/react": "^11.2.6",
"@testing-library/user-event": "^13.1.8",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^13.1.9",
"@types/jest": "^26.0.23",
"@types/react": "^17.0.5",
"@typescript-eslint/eslint-plugin": "^4.22.1",
"@typescript-eslint/parser": "^4.22.1",
"check-export-map": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/parser": "^4.23.0",
"check-export-map": "^1.1.1",
"class-transformer": "^0.4.0",
"class-validator": "^0.13.1",
"computed-types": "^1.6.0",
"eslint": "^7.26.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
"fp-ts": "^2.10.5",
"husky": "^6.0.0",
"io-ts": "^2.0.0",
Expand All @@ -175,19 +174,19 @@
"joi": "^17.4.0",
"lint-staged": "^11.0.0",
"microbundle": "^0.13.0",
"monocle-ts": "^2.3.9",
"monocle-ts": "^2.3.10",
"newtype-ts": "^0.3.4",
"nope-validator": "^1.0.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.2.1",
"prettier": "^2.3.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-hook-form": "7.0.0",
"react-hook-form": "7.6.0",
"reflect-metadata": "^0.1.13",
"superstruct": "^0.15.2",
"ts-jest": "^26.5.6",
"typescript": "^4.2.4",
"vest": "^3.1.2",
"vest": "^3.2.1",
"yup": "^0.32.9",
"zod": "^1.11.17"
},
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/toNestObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test('transforms flat object to nested object', () => {
'n.test': { type: 'rd', message: 'third message' },
};

const fields = ({
const fields = {
name: {
ref: 'nameRef',
},
Expand All @@ -20,7 +20,7 @@ test('transforms flat object to nested object', () => {
unused: {
ref: 'unusedRef',
},
} as any) as Record<InternalFieldName, Field['_f']>;
} as any as Record<InternalFieldName, Field['_f']>;

expect(toNestError(flatObject, fields)).toMatchInlineSnapshot(`
Object {
Expand Down
6 changes: 4 additions & 2 deletions superstruct/src/__tests__/__fixtures__/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ import {
boolean,
} from 'superstruct';

const Password = define('Password', (value, ctx) =>
value === ctx.branch[0].password);
const Password = define(
'Password',
(value, ctx) => value === ctx.branch[0].password,
);

export const schema = object({
username: size(pattern(string(), /^\w+$/), 3, 30),
Expand Down
21 changes: 9 additions & 12 deletions superstruct/src/superstruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@ const parseErrorSchema = (error: StructError) =>
{},
);

export const superstructResolver: Resolver = (schema, resolverOptions) => (
values,
_,
options,
) => {
const result = validate(values, schema, resolverOptions);
export const superstructResolver: Resolver =
(schema, resolverOptions) => (values, _, options) => {
const result = validate(values, schema, resolverOptions);

return {
values: result[1] || {},
errors: result[0]
? toNestError(parseErrorSchema(result[0]), options.fields)
: {},
return {
values: result[1] || {},
errors: result[0]
? toNestError(parseErrorSchema(result[0]), options.fields)
: {},
};
};
};
39 changes: 20 additions & 19 deletions vest/src/vest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ const parseErrorSchema = (
return errors;
};

export const vestResolver: Resolver = (
schema,
_,
resolverOptions = {},
) => async (values, _context, options) => {
const result =
resolverOptions.mode === 'sync'
? schema(values)
: await promisify(schema)(values);
export const vestResolver: Resolver =
(schema, _, resolverOptions = {}) =>
async (values, _context, options) => {
const result =
resolverOptions.mode === 'sync'
? schema(values)
: await promisify(schema)(values);

return result.hasErrors()
? {
values: {},
errors: toNestError(
parseErrorSchema(result.getErrors(), options.criteriaMode === 'all'),
options.fields,
),
}
: { values, errors: {} };
};
return result.hasErrors()
? {
values: {},
errors: toNestError(
parseErrorSchema(
result.getErrors(),
options.criteriaMode === 'all',
),
options.fields,
),
}
: { values, errors: {} };
};
Loading

0 comments on commit f67a10f

Please sign in to comment.