From f67a10facd07f6404d312d1f98ecdb53431e77b9 Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 16 May 2021 19:07:31 +0200 Subject: [PATCH] fix(zodResolver): improve unionErrors parsing (#168) * fix(zodresolver): improve unionErrors parsin * refactor: format code with eslint/prettier --- .eslintrc.js | 5 +- README.md | 2 +- class-validator/src/class-validator.ts | 40 ++-- computed-types/src/computed-types.ts | 31 ++-- io-ts/src/errorsToRecord.ts | 26 +-- joi/src/joi.ts | 58 +++--- nope/src/nope.ts | 28 +-- package.json | 19 +- src/__tests__/toNestObject.ts | 4 +- .../src/__tests__/__fixtures__/data.ts | 6 +- superstruct/src/superstruct.ts | 21 +-- vest/src/vest.ts | 39 ++-- yarn.lock | 171 ++++++++---------- yup/src/yup.ts | 64 +++---- zod/src/__tests__/__fixtures__/data.ts | 6 +- zod/src/__tests__/__snapshots__/zod.ts.snap | 96 ++++++++-- zod/src/zod.ts | 57 +++--- 17 files changed, 364 insertions(+), 309 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index e00294ec..42c7db65 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -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', diff --git a/README.md b/README.md index ba6487f8..a19be0e4 100644 --- a/README.md +++ b/README.md @@ -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({ diff --git a/class-validator/src/class-validator.ts b/class-validator/src/class-validator.ts index d40907f3..71faf872 100644 --- a/class-validator/src/class-validator.ts +++ b/class-validator/src/class-validator.ts @@ -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: {} }; + }; diff --git a/computed-types/src/computed-types.ts b/computed-types/src/computed-types.ts index 839802ff..f03f0c81 100644 --- a/computed-types/src/computed-types.ts +++ b/computed-types/src/computed-types.ts @@ -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), + }; + } + }; diff --git a/io-ts/src/errorsToRecord.ts b/io-ts/src/errorsToRecord.ts index d3348a99..059f27e8 100644 --- a/io-ts/src/errorsToRecord.ts +++ b/io-ts/src/errorsToRecord.ts @@ -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 = never + A extends Record = never, >(): SemiGroup.Semigroup => ({ concat: (first, second) => Object.assign({}, second, first), }); @@ -106,10 +106,10 @@ const concatToMultipleErrors = ( errors: ReadonlyArray, ): 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; @@ -118,14 +118,14 @@ const concatToMultipleErrors = ( }), ); -const errorsToRecord = (validateAllFieldCriteria: boolean) => ( - validationErrors: ReadonlyArray, -): ErrorObject => { - const concat = validateAllFieldCriteria - ? concatToMultipleErrors - : concatToSingleError; +const errorsToRecord = + (validateAllFieldCriteria: boolean) => + (validationErrors: ReadonlyArray): ErrorObject => { + const concat = validateAllFieldCriteria + ? concatToMultipleErrors + : concatToSingleError; - return pipe(validationErrors, ReadonlyArray.map(formatError), concat); -}; + return pipe(validationErrors, ReadonlyArray.map(formatError), concat); + }; export default errorsToRecord; diff --git a/joi/src/joi.ts b/joi/src/joi.ts index 7ce617e8..1ffb46d1 100644 --- a/joi/src/joi.ts +++ b/joi/src/joi.ts @@ -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 = {}; - 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 = {}; + 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, + ) + : {}, + }; }; -}; diff --git a/nope/src/nope.ts b/nope/src/nope.ts index dc279b68..32cc2b30 100644 --- a/nope/src/nope.ts +++ b/nope/src/nope.ts @@ -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: {} }; + }; diff --git a/package.json b/package.json index d0d051ec..e182c705 100644 --- a/package.json +++ b/package.json @@ -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", @@ -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" }, diff --git a/src/__tests__/toNestObject.ts b/src/__tests__/toNestObject.ts index 877eaeac..8d049220 100644 --- a/src/__tests__/toNestObject.ts +++ b/src/__tests__/toNestObject.ts @@ -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', }, @@ -20,7 +20,7 @@ test('transforms flat object to nested object', () => { unused: { ref: 'unusedRef', }, - } as any) as Record; + } as any as Record; expect(toNestError(flatObject, fields)).toMatchInlineSnapshot(` Object { diff --git a/superstruct/src/__tests__/__fixtures__/data.ts b/superstruct/src/__tests__/__fixtures__/data.ts index c69d51fc..40a7b8e3 100644 --- a/superstruct/src/__tests__/__fixtures__/data.ts +++ b/superstruct/src/__tests__/__fixtures__/data.ts @@ -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), diff --git a/superstruct/src/superstruct.ts b/superstruct/src/superstruct.ts index bffa7a4c..56425568 100644 --- a/superstruct/src/superstruct.ts +++ b/superstruct/src/superstruct.ts @@ -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) + : {}, + }; }; -}; diff --git a/vest/src/vest.ts b/vest/src/vest.ts index c717ed0c..0778afc8 100644 --- a/vest/src/vest.ts +++ b/vest/src/vest.ts @@ -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: {} }; + }; diff --git a/yarn.lock b/yarn.lock index 85153d8c..63aaf808 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1273,18 +1273,18 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^11.2.6": - version "11.2.6" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.6.tgz#586a23adc63615985d85be0c903f374dab19200b" - integrity sha512-TXMCg0jT8xmuU8BkKMtp8l7Z50Ykew5WNX8UoIKTaLFwKkP2+1YDhOLA2Ga3wY4x29jyntk7EWfum0kjlYiSjQ== +"@testing-library/react@^11.2.7": + version "11.2.7" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818" + integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA== dependencies: "@babel/runtime" "^7.12.5" "@testing-library/dom" "^7.28.1" -"@testing-library/user-event@^13.1.8": - version "13.1.8" - resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.1.8.tgz#9cbf342b88d837ee188f9f9f4df6d1beaaf179c2" - integrity sha512-M04HgOlJvxILf5xyrkJaEQfFOtcvhy3usLldQIEg9zgFIYQofSmFGVfFlS7BWowqlBGLrItwGMlPXCoBgoHSiw== +"@testing-library/user-event@^13.1.9": + version "13.1.9" + resolved "https://registry.yarnpkg.com/@testing-library/user-event/-/user-event-13.1.9.tgz#29e49a42659ac3c1023565ff56819e0153a82e99" + integrity sha512-NZr0zL2TMOs2qk+dNlqrAdbaRW5dAmYwd1yuQ4r7HpkVEOj0MWuUjDWwKhcLd/atdBy8ZSMHSKp+kXSQe47ezg== dependencies: "@babel/runtime" "^7.12.5" @@ -1468,13 +1468,13 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.22.1.tgz#6bcdbaa4548553ab861b4e5f34936ead1349a543" - integrity sha512-kVTAghWDDhsvQ602tHBc6WmQkdaYbkcTwZu+7l24jtJiYvm9l+/y/b2BZANEezxPDiX5MK2ZecE+9BFi/YJryw== +"@typescript-eslint/eslint-plugin@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" + integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== dependencies: - "@typescript-eslint/experimental-utils" "4.22.1" - "@typescript-eslint/scope-manager" "4.22.1" + "@typescript-eslint/experimental-utils" "4.23.0" + "@typescript-eslint/scope-manager" "4.23.0" debug "^4.1.1" functional-red-black-tree "^1.0.1" lodash "^4.17.15" @@ -1482,60 +1482,60 @@ semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/experimental-utils@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.22.1.tgz#3938a5c89b27dc9a39b5de63a62ab1623ab27497" - integrity sha512-svYlHecSMCQGDO2qN1v477ax/IDQwWhc7PRBiwAdAMJE7GXk5stF4Z9R/8wbRkuX/5e9dHqbIWxjeOjckK3wLQ== +"@typescript-eslint/experimental-utils@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" + integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== dependencies: "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.22.1" - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/typescript-estree" "4.22.1" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" eslint-scope "^5.0.0" eslint-utils "^2.0.0" -"@typescript-eslint/parser@^4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.22.1.tgz#a95bda0fd01d994a15fc3e99dc984294f25c19cc" - integrity sha512-l+sUJFInWhuMxA6rtirzjooh8cM/AATAe3amvIkqKFeMzkn85V+eLzb1RyuXkHak4dLfYzOmF6DXPyflJvjQnw== +"@typescript-eslint/parser@^4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" + integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== dependencies: - "@typescript-eslint/scope-manager" "4.22.1" - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/typescript-estree" "4.22.1" + "@typescript-eslint/scope-manager" "4.23.0" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/typescript-estree" "4.23.0" debug "^4.1.1" -"@typescript-eslint/scope-manager@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.22.1.tgz#5bb357f94f9cd8b94e6be43dd637eb73b8f355b4" - integrity sha512-d5bAiPBiessSmNi8Amq/RuLslvcumxLmyhf1/Xa9IuaoFJ0YtshlJKxhlbY7l2JdEk3wS0EnmnfeJWSvADOe0g== +"@typescript-eslint/scope-manager@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" + integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== dependencies: - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/visitor-keys" "4.22.1" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" -"@typescript-eslint/types@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.22.1.tgz#bf99c6cec0b4a23d53a61894816927f2adad856a" - integrity sha512-2HTkbkdAeI3OOcWbqA8hWf/7z9c6gkmnWNGz0dKSLYLWywUlkOAQ2XcjhlKLj5xBFDf8FgAOF5aQbnLRvgNbCw== +"@typescript-eslint/types@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" + integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== -"@typescript-eslint/typescript-estree@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.22.1.tgz#dca379eead8cdfd4edc04805e83af6d148c164f9" - integrity sha512-p3We0pAPacT+onSGM+sPR+M9CblVqdA9F1JEdIqRVlxK5Qth4ochXQgIyb9daBomyQKAXbygxp1aXQRV0GC79A== +"@typescript-eslint/typescript-estree@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" + integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== dependencies: - "@typescript-eslint/types" "4.22.1" - "@typescript-eslint/visitor-keys" "4.22.1" + "@typescript-eslint/types" "4.23.0" + "@typescript-eslint/visitor-keys" "4.23.0" debug "^4.1.1" globby "^11.0.1" is-glob "^4.0.1" semver "^7.3.2" tsutils "^3.17.1" -"@typescript-eslint/visitor-keys@4.22.1": - version "4.22.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.22.1.tgz#6045ae25a11662c671f90b3a403d682dfca0b7a6" - integrity sha512-WPkOrIRm+WCLZxXQHCi+WG8T2MMTUFR70rWjdWYddLT7cEfb2P4a3O/J2U1FBVsSFTocXLCoXWY6MZGejeStvQ== +"@typescript-eslint/visitor-keys@4.23.0": + version "4.23.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" + integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== dependencies: - "@typescript-eslint/types" "4.22.1" + "@typescript-eslint/types" "4.23.0" eslint-visitor-keys "^2.0.0" abab@^2.0.3: @@ -2080,10 +2080,10 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -check-export-map@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/check-export-map/-/check-export-map-1.0.1.tgz#5d77a48b32fb2e28a382a1bea22f3dd198d5f65f" - integrity sha512-6nJK5v9fiUQjAACrLyVlIm8UlAak5zdvYxJhpl0zYa7IqUElInMU6lRL/+rTHjEmy+hbkn5Wphq4Rj9cRkEbqg== +check-export-map@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/check-export-map/-/check-export-map-1.1.1.tgz#4ab0b2b658fb14ceedb302ae7049dfad569297f9" + integrity sha512-dyVrZlewxo5UM804pW9mBzax2/sUJHtGidnbDVqKyM/LBPv5LclbsuEH4RrDSgoYaaISWOQGl7ga/ZfodiGfzA== dependencies: kolorist "^1.2.3" mri "^1.1.6" @@ -2267,6 +2267,11 @@ concat-with-sourcemaps@^1.1.0: dependencies: source-map "^0.6.1" +context@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/context/-/context-1.1.3.tgz#e2d1e358b1a40bdc42c7f8bacb66abb050cf00b6" + integrity sha512-eHF34RlIQuloYviKLfTIcdA8p5teA5zg8fFlhCFK5kBvXhDE/HKOc4ezd/XYoz8/kMqWw59orOfetAa0flG6nA== + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -2811,13 +2816,6 @@ eslint-config-prettier@^8.3.0: resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== -eslint-plugin-prettier@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.0.tgz#cdbad3bf1dbd2b177e9825737fe63b476a08f0c7" - integrity sha512-UDK6rJT6INSfcOo545jiaOwB701uAIt2/dR7WnFQoGCVl1/EMqdANBmwUaqqQ45aXprsTGzSa39LI1PyuRBxxw== - dependencies: - prettier-linter-helpers "^1.0.0" - eslint-scope@^5.0.0, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -3076,11 +3074,6 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - fast-glob@^3.1.1: version "3.2.4" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.4.tgz#d20aefbf99579383e7f3cc66529158c9b98554d3" @@ -4649,12 +4642,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lodash@^4.17.21: +lodash@4.x, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4903,10 +4891,10 @@ mkdirp@~0.5.1: dependencies: minimist "^1.2.5" -monocle-ts@^2.3.9: - version "2.3.9" - resolved "https://registry.yarnpkg.com/monocle-ts/-/monocle-ts-2.3.9.tgz#037d8a14f72dfd5ce6928c8730de1f0e504da94d" - integrity sha512-X5ppMOPAQV/BeENhPpEI0hJelXMoNVtHlTVk89ugZL7ZvK6JtBDFW/762aq1MOzCCaBAFlxGz1Wmxa0DW7tshQ== +monocle-ts@^2.3.10: + version "2.3.10" + resolved "https://registry.yarnpkg.com/monocle-ts/-/monocle-ts-2.3.10.tgz#f5edaacc459103ab6dda07ed0e297f0cd6455ed8" + integrity sha512-UvzfU1LR0XMfPN0ySPvMhIu54mzfuAzoggtIOc2JlEAfnR6sfv/5zGkxrNtYvLNYzm5bo/qc1v0DlUq4VlzE0g== mri@^1.1.0, mri@^1.1.6: version "1.1.6" @@ -5718,17 +5706,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" - integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.0.tgz#b6a5bf1284026ae640f17f7ff5658a7567fc0d18" + integrity sha512-kXtO4s0Lz/DW/IJ9QdWhAf7/NmPWQXkFr/r/WkR3vyI+0v8amTDxiaQSLzs8NBlytfLWX/7uQUMIW677yLKl4w== pretty-bytes@^3.0.0: version "3.0.1" @@ -5819,10 +5800,10 @@ react-dom@^17.0.2: object-assign "^4.1.1" scheduler "^0.20.2" -react-hook-form@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.0.0.tgz#7a7b0b51cc26f1448ab2a113bcff8b49e0f78864" - integrity sha512-cXoKnNnQe5IxieesTu+enDS7Jfh8lyX8mSMwzNvC4L0k6R6yQ2F8rIhvBbzlFOGSoeEnsR64dvMZ1izWRCAw6A== +react-hook-form@7.6.0: + version "7.6.0" + resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.6.0.tgz#ec8aa8376738356c5af09992404292ba04067ac4" + integrity sha512-Ai3lx5TZO/W274PVgIqOkjhCAxQJ7KRU9c6tTM1M0LDctCUZ0LavirUiy+42b0gxGLUswO0vPsxcpw1CkkebUA== react-is@^17.0.1: version "17.0.1" @@ -7083,10 +7064,12 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vest@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/vest/-/vest-3.1.2.tgz#674f2b43e37029cbed9a3b67fa545a1c77be6703" - integrity sha512-mdiTu28Gl1vrZpLwiNhXzIkfWmuwIfNUsMEXX0sSSk0bSLbAqV7bFIFI9g7jnZYn9AVGXrljFlLmKvMl5BJYpA== +vest@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/vest/-/vest-3.2.1.tgz#a73d1ee4d3dbd5e35a6f1561310a344bc43d4798" + integrity sha512-Wjvx2ap0x9Z70eCGIkAzpOGiY7X4+hkTszpIiMh/uSPNDqSIuefeotA78VUgxjHEv04NDJrAHX6EylEjcVqzwg== + dependencies: + context "^1.1.3" w3c-hr-time@^1.0.2: version "1.0.2" diff --git a/yup/src/yup.ts b/yup/src/yup.ts index 0de99d4e..10c5751a 100644 --- a/yup/src/yup.ts +++ b/yup/src/yup.ts @@ -35,36 +35,38 @@ const parseErrorSchema = ( }, {}); }; -export const yupResolver: Resolver = ( - schema, - schemaOptions = { - abortEarly: false, - }, - resolverOptions = {}, -) => async (values, context, options) => { - try { - if (schemaOptions.context && process.env.NODE_ENV === 'development') { - // eslint-disable-next-line no-console - console.warn( - "You should not used the yup options context. Please, use the 'useForm' context object instead", - ); - } +export const yupResolver: Resolver = + ( + schema, + schemaOptions = { + abortEarly: false, + }, + resolverOptions = {}, + ) => + async (values, context, options) => { + try { + if (schemaOptions.context && process.env.NODE_ENV === 'development') { + // eslint-disable-next-line no-console + console.warn( + "You should not used the yup options context. Please, use the 'useForm' context object instead", + ); + } - const result = await schema[ - resolverOptions.mode === 'sync' ? 'validateSync' : 'validate' - ](values, Object.assign({}, schemaOptions, { context })); + const result = await schema[ + resolverOptions.mode === 'sync' ? 'validateSync' : 'validate' + ](values, Object.assign({}, schemaOptions, { context })); - return { - values: result, - errors: {}, - }; - } catch (e) { - return { - values: {}, - errors: toNestError( - parseErrorSchema(e, options.criteriaMode === 'all'), - options.fields, - ), - }; - } -}; + return { + values: result, + errors: {}, + }; + } catch (e) { + return { + values: {}, + errors: toNestError( + parseErrorSchema(e, options.criteriaMode === 'all'), + options.fields, + ), + }; + } + }; diff --git a/zod/src/__tests__/__fixtures__/data.ts b/zod/src/__tests__/__fixtures__/data.ts index db4c5740..c8dd6e4f 100644 --- a/zod/src/__tests__/__fixtures__/data.ts +++ b/zod/src/__tests__/__fixtures__/data.ts @@ -15,11 +15,12 @@ export const schema = z ) .min(8, 'Must be at least 8 characters in length'), repeatPassword: z.string(), - accessToken: z.union([z.string(), z.number()]).optional(), + accessToken: z.union([z.string(), z.number()]), birthYear: z.number().min(1900).max(2013).optional(), email: z.string().email().optional(), tags: z.array(z.string()), enabled: z.boolean(), + url: z.string().url('Custom error url').or(z.literal('')), like: z .array( z.object({ @@ -42,6 +43,8 @@ export const validData: z.infer = { email: 'john@doe.com', tags: ['tag1', 'tag2'], enabled: true, + accessToken: 'accessToken', + url: 'https://react-hook-form.com/', like: [ { id: 1, @@ -55,6 +58,7 @@ export const invalidData = { email: '', birthYear: 'birthYear', like: [{ id: 'z' }], + url: 'abc', }; export const fields: Record = { diff --git a/zod/src/__tests__/__snapshots__/zod.ts.snap b/zod/src/__tests__/__snapshots__/zod.ts.snap index 81b28af7..13459f50 100644 --- a/zod/src/__tests__/__snapshots__/zod.ts.snap +++ b/zod/src/__tests__/__snapshots__/zod.ts.snap @@ -3,10 +3,15 @@ exports[`zodResolver should return a single error from zodResolver when validation fails 1`] = ` Object { "errors": Object { + "accessToken": Object { + "message": "Required", + "ref": undefined, + "type": "invalid_type", + }, "birthYear": Object { - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", }, "confirm": Object { "message": "Passwords don't match", @@ -38,9 +43,9 @@ Object { "type": "invalid_type", }, }, - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", }, "password": Object { "message": "One uppercase character", @@ -59,6 +64,11 @@ Object { "ref": undefined, "type": "invalid_type", }, + "url": Object { + "message": "Custom error url", + "ref": undefined, + "type": "invalid_string", + }, "username": Object { "message": "Required", "ref": Object { @@ -74,10 +84,15 @@ Object { exports[`zodResolver should return a single error from zodResolver with \`mode: sync\` when validation fails 1`] = ` Object { "errors": Object { + "accessToken": Object { + "message": "Required", + "ref": undefined, + "type": "invalid_type", + }, "birthYear": Object { - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", }, "confirm": Object { "message": "Passwords don't match", @@ -109,9 +124,9 @@ Object { "type": "invalid_type", }, }, - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", }, "password": Object { "message": "One uppercase character", @@ -130,6 +145,11 @@ Object { "ref": undefined, "type": "invalid_type", }, + "url": Object { + "message": "Custom error url", + "ref": undefined, + "type": "invalid_string", + }, "username": Object { "message": "Required", "ref": Object { @@ -145,10 +165,22 @@ Object { exports[`zodResolver should return all the errors from zodResolver when validation fails with \`validateAllFieldCriteria\` set to true 1`] = ` Object { "errors": Object { + "accessToken": Object { + "message": "Required", + "ref": undefined, + "type": "invalid_type", + "types": Object { + "invalid_type": Array [ + "Required", + "Required", + ], + "invalid_union": "Invalid input", + }, + }, "birthYear": Object { - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", "types": Object { "invalid_type": Array [ "Expected number, received string", @@ -202,9 +234,9 @@ Object { }, }, }, - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", "types": Object { "invalid_type": "Expected undefined, received array", "invalid_union": "Invalid input", @@ -241,6 +273,16 @@ Object { "invalid_type": "Required", }, }, + "url": Object { + "message": "Custom error url", + "ref": undefined, + "type": "invalid_string", + "types": Object { + "invalid_literal_value": "Input must be \\"\\"", + "invalid_string": "Custom error url", + "invalid_union": "Invalid input", + }, + }, "username": Object { "message": "Required", "ref": Object { @@ -259,10 +301,22 @@ Object { exports[`zodResolver should return all the errors from zodResolver when validation fails with \`validateAllFieldCriteria\` set to true and \`mode: sync\` 1`] = ` Object { "errors": Object { + "accessToken": Object { + "message": "Required", + "ref": undefined, + "type": "invalid_type", + "types": Object { + "invalid_type": Array [ + "Required", + "Required", + ], + "invalid_union": "Invalid input", + }, + }, "birthYear": Object { - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", "types": Object { "invalid_type": Array [ "Expected number, received string", @@ -316,9 +370,9 @@ Object { }, }, }, - "message": "Invalid input", + "message": "Expected number, received string", "ref": undefined, - "type": "invalid_union", + "type": "invalid_type", "types": Object { "invalid_type": "Expected undefined, received array", "invalid_union": "Invalid input", @@ -355,6 +409,16 @@ Object { "invalid_type": "Required", }, }, + "url": Object { + "message": "Custom error url", + "ref": undefined, + "type": "invalid_string", + "types": Object { + "invalid_literal_value": "Input must be \\"\\"", + "invalid_string": "Custom error url", + "invalid_union": "Invalid input", + }, + }, "username": Object { "message": "Required", "ref": Object { diff --git a/zod/src/zod.ts b/zod/src/zod.ts index 01046ed0..2fa37532 100644 --- a/zod/src/zod.ts +++ b/zod/src/zod.ts @@ -14,7 +14,16 @@ const parseErrorSchema = ( const _path = path.join('.'); if (!errors[_path]) { - errors[_path] = { message, type: code }; + if ('unionErrors' in error) { + const unionError = error.unionErrors[0].errors[0]; + + errors[_path] = { + message: unionError.message, + type: unionError.code, + }; + } else { + errors[_path] = { message, type: code }; + } } if ('unionErrors' in error) { @@ -44,27 +53,25 @@ const parseErrorSchema = ( return errors; }; -export const zodResolver: Resolver = ( - schema, - schemaOptions, - resolverOptions = {}, -) => async (values, _, options) => { - try { - return { - errors: {}, - values: await schema[ - resolverOptions.mode === 'sync' ? 'parse' : 'parseAsync' - ](values, schemaOptions), - }; - } catch (error) { - return { - values: {}, - errors: error.isEmpty - ? {} - : toNestError( - parseErrorSchema(error.errors, options.criteriaMode === 'all'), - options.fields, - ), - }; - } -}; +export const zodResolver: Resolver = + (schema, schemaOptions, resolverOptions = {}) => + async (values, _, options) => { + try { + return { + errors: {}, + values: await schema[ + resolverOptions.mode === 'sync' ? 'parse' : 'parseAsync' + ](values, schemaOptions), + }; + } catch (error) { + return { + values: {}, + errors: error.isEmpty + ? {} + : toNestError( + parseErrorSchema(error.errors, options.criteriaMode === 'all'), + options.fields, + ), + }; + } + };