Repository: react-hook-form/resolvers
Pull request #528 (2023-03-20 07:39:18)

fix: resolvers fails silently & add `raw` values to all resolvers
Related to https://github.com/react-hook-form/resolvers/pull/504

@bluebill1049 ready to review but do not merge because of a breaking change: `rawValues` => `raw`

github-actions[bot]: **Size Change:** +51.3 kB (+252%) 🆘

**Total Size:** 71.7 kB

| Filename | Size | Change |  |
| :--- | :---: | :---: | :---: |
| `computed-types/dist/computed-types.js` | 386 B | +27 B (+8%) | 🔍 |
| `computed-types/dist/computed-types.module.js` | 401 B | +25 B (+7%) | 🔍 |
| `computed-types/dist/computed-types.umd.js` | 477 B | +21 B (+5%) | 🔍 |
| `yup/dist/yup.js` | 7.29 kB | +6.67 kB (+1060%) | 🆘 |
| `yup/dist/yup.module.js` | 7.32 kB | +6.67 kB (+1038%) | 🆘 |
| `yup/dist/yup.umd.js` | 7.38 kB | +6.64 kB (+908%) | 🆘 |
| `zod/dist/zod.js` | 11 kB | +10.5 kB (+1829%) | 🆘 |
| `zod/dist/zod.module.js` | 11 kB | +10.4 kB (+1764%) | 🆘 |
| `zod/dist/zod.umd.js` | 11.1 kB | +10.4 kB (+1532%) | 🆘 |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `ajv/dist/ajv.js` | 638 B |
| `ajv/dist/ajv.module.js` | 597 B |
| `ajv/dist/ajv.umd.js` | 751 B |
| `class-validator/dist/class-validator.js` | 528 B |
| `class-validator/dist/class-validator.module.js` | 552 B |
| `class-validator/dist/class-validator.umd.js` | 631 B |
| `dist/resolvers.js` | 359 B |
| `dist/resolvers.module.js` | 373 B |
| `dist/resolvers.umd.js` | 454 B |
| `io-ts/dist/io-ts.js` | 1.28 kB |
| `io-ts/dist/io-ts.module.js` | 1.18 kB |
| `io-ts/dist/io-ts.umd.js` | 1.41 kB |
| `joi/dist/joi.js` | 592 B |
| `joi/dist/joi.module.js` | 610 B |
| `joi/dist/joi.umd.js` | 698 B |
| `nope/dist/nope.js` | 345 B |
| `nope/dist/nope.module.js` | 365 B |
| `nope/dist/nope.umd.js` | 434 B |
| `superstruct/dist/superstruct.js` | 302 B |
| `superstruct/dist/superstruct.module.js` | 319 B |
| `superstruct/dist/superstruct.umd.js` | 404 B |
| `typanion/dist/typanion.js` | 323 B |
| `typanion/dist/typanion.module.js` | 336 B |
| `typanion/dist/typanion.umd.js` | 418 B |
| `vest/dist/vest.js` | 472 B |
| `vest/dist/vest.module.js` | 436 B |
| `vest/dist/vest.umd.js` | 562 B |

</details>



<a href="https://github.com/preactjs/compressed-size-action"><sub>compressed-size-action</sub></a>

Patch:
diff --git a/computed-types/src/computed-types.ts b/computed-types/src/computed-types.ts
index 66427cd..f84b48a 100644
--- a/computed-types/src/computed-types.ts
+++ b/computed-types/src/computed-types.ts
@@ -1,7 +1,10 @@
 import type { FieldErrors } from 'react-hook-form';
 import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
-import type { ValidationError } from 'computed-types';
 import type { Resolver } from './types';
+import type { ValidationError } from 'computed-types';
+
+const isValidationError = (error: any): error is ValidationError =>
+  error.errors != null;
 
 const parseErrorSchema = (computedTypesError: ValidationError) => {
   const parsedErrors: FieldErrors = {};
@@ -27,9 +30,13 @@ export const computedTypesResolver: Resolver =
         values: data,
       };
     } catch (error: any) {
-      return {
-        values: {},
-        errors: toNestError(parseErrorSchema(error), options),
-      };
+      if (isValidationError(error)) {
+        return {
+          values: {},
+          errors: toNestError(parseErrorSchema(error), options),
+        };
+      }
+
+      throw error;
     }
   };
diff --git a/superstruct/src/superstruct.ts b/superstruct/src/superstruct.ts
index 8dcc74b..a563d22 100644
--- a/superstruct/src/superstruct.ts
+++ b/superstruct/src/superstruct.ts
@@ -15,8 +15,9 @@ const parseErrorSchema = (error: StructError) =>
   );
 
 export const superstructResolver: Resolver =
-  (schema, resolverOptions) => (values, _, options) => {
-    const result = validate(values, schema, resolverOptions);
+  (schema, schemaOptions, resolverOptions = {}) =>
+  (values, _, options) => {
+    const result = validate(values, schema, schemaOptions);
 
     if (result[0]) {
       return {
@@ -28,7 +29,7 @@ export const superstructResolver: Resolver =
     options.shouldUseNativeValidation && validateFieldsNatively({}, options);
 
     return {
-      values: result[1],
+      values: resolverOptions.raw ? values : result[1],
       errors: {},
     };
   };
diff --git a/superstruct/src/types.ts b/superstruct/src/types.ts
index 51d3d31..075bace 100644
--- a/superstruct/src/types.ts
+++ b/superstruct/src/types.ts
@@ -1,8 +1,4 @@
-import {
-  FieldValues,
-  ResolverOptions,
-  ResolverResult,
-} from 'react-hook-form';
+import { FieldValues, ResolverOptions, ResolverResult } from 'react-hook-form';
 import { validate, Struct } from 'superstruct';
 
 type Options = Parameters<typeof validate>[2];
@@ -10,6 +6,13 @@ type Options = Parameters<typeof validate>[2];
 export type Resolver = <T extends Struct<any, any>>(
   schema: T,
   options?: Options,
+  factoryOptions?: {
+    /**
+     * Return the raw input values rather than the parsed values.
+     * @default false
+     */
+    raw?: boolean;
+  },
 ) => <TFieldValues extends FieldValues, TContext>(
   values: TFieldValues,
   context: TContext | undefined,
diff --git a/yup/src/types.ts b/yup/src/types.ts
index 3156f5a..8bbc43d 100644
--- a/yup/src/types.ts
+++ b/yup/src/types.ts
@@ -6,7 +6,17 @@ type Options<T extends Yup.ObjectSchema<any>> = Parameters<T['validate']>[1];
 export type Resolver = <T extends Yup.ObjectSchema<any>>(
   schema: T,
   schemaOptions?: Options<T>,
-  factoryOptions?: { mode?: 'async' | 'sync'; rawValues?: boolean },
+  factoryOptions?: {
+    /**
+     * @default async
+     */
+    mode?: 'async' | 'sync';
+    /**
+     * Return the raw input values rather than the parsed values.
+     * @default false
+     */
+    raw?: boolean;
+  },
 ) => <TFieldValues extends FieldValues, TContext>(
   values: TFieldValues,
   context: TContext | undefined,
diff --git a/yup/src/yup.ts b/yup/src/yup.ts
index 83cfb64..df65488 100644
--- a/yup/src/yup.ts
+++ b/yup/src/yup.ts
@@ -59,7 +59,7 @@ export const yupResolver: Resolver =
       options.shouldUseNativeValidation && validateFieldsNatively({}, options);
 
       return {
-        values: resolverOptions.rawValues ? values : result,
+        values: resolverOptions.raw ? values : result,
         errors: {},
       };
     } catch (e: any) {
diff --git a/zod/src/types.ts b/zod/src/types.ts
index e340bbb..eae802c 100644
--- a/zod/src/types.ts
+++ b/zod/src/types.ts
@@ -1,8 +1,4 @@
-import {
-  FieldValues,
-  ResolverResult,
-  ResolverOptions,
-} from 'react-hook-form';
+import { FieldValues, ResolverResult, ResolverOptions } from 'react-hook-form';
 import { z } from 'zod';
 
 export type Resolver = <T extends z.Schema<any, any>>(
@@ -17,7 +13,7 @@ export type Resolver = <T extends z.Schema<any, any>>(
      * Return the raw input values rather than the parsed values.
      * @default false
      */
-    rawValues?: boolean;
+    raw?: boolean;
   },
 ) => <TFieldValues extends FieldValues, TContext>(
   values: TFieldValues,
diff --git a/zod/src/zod.ts b/zod/src/zod.ts
index 1dab834..53c95bd 100644
--- a/zod/src/zod.ts
+++ b/zod/src/zod.ts
@@ -1,8 +1,10 @@
 import { appendErrors, FieldError, FieldErrors } from 'react-hook-form';
-import { z } from 'zod';
+import { z, ZodError } from 'zod';
 import { toNestError, validateFieldsNatively } from '@hookform/resolvers';
 import type { Resolver } from './types';
 
+const isZodError = (error: any): error is ZodError => error.errors != null;
+
 const parseErrorSchema = (
   zodErrors: z.ZodIssue[],
   validateAllFieldCriteria: boolean,
@@ -65,21 +67,23 @@ export const zodResolver: Resolver =
 
       return {
         errors: {} as FieldErrors,
-        values: resolverOptions.rawValues ? values : data,
+        values: resolverOptions.raw ? values : data,
       };
     } catch (error: any) {
-      return {
-        values: {},
-        errors: error.isEmpty
-          ? {}
-          : toNestError(
-              parseErrorSchema(
-                error.errors,
-                !options.shouldUseNativeValidation &&
-                  options.criteriaMode === 'all',
-              ),
-              options,
+      if (isZodError(error)) {
+        return {
+          values: {},
+          errors: toNestError(
+            parseErrorSchema(
+              error.errors,
+              !options.shouldUseNativeValidation &&
+                options.criteriaMode === 'all',
             ),
-      };
+            options,
+          ),
+        };
+      }
+
+      throw error;
     }
   };


Test patch:
diff --git a/computed-types/src/__tests__/computed-types.ts b/computed-types/src/__tests__/computed-types.ts
index fc05784..d5c97c6 100644
--- a/computed-types/src/__tests__/computed-types.ts
+++ b/computed-types/src/__tests__/computed-types.ts
@@ -21,4 +21,20 @@ describe('computedTypesResolver', () => {
 
     expect(result).toMatchSnapshot();
   });
+
+  it('should throw any error unrelated to computed-types', async () => {
+    const schemaWithCustomError = schema.transform(() => {
+      throw Error('custom error');
+    });
+    const promise = computedTypesResolver(schemaWithCustomError)(
+      validData,
+      undefined,
+      {
+        fields,
+        shouldUseNativeValidation,
+      },
+    );
+
+    await expect(promise).rejects.toThrow('custom error');
+  });
 });
diff --git a/superstruct/src/__tests__/superstruct.ts b/superstruct/src/__tests__/superstruct.ts
index da53553..4be7e5c 100644
--- a/superstruct/src/__tests__/superstruct.ts
+++ b/superstruct/src/__tests__/superstruct.ts
@@ -21,4 +21,17 @@ describe('superstructResolver', () => {
 
     expect(result).toMatchSnapshot();
   });
+
+  it('should return values from superstructResolver when validation pass & raw=true', async () => {
+    const result = await superstructResolver(schema, undefined, { raw: true })(
+      validData,
+      undefined,
+      {
+        fields,
+        shouldUseNativeValidation,
+      },
+    );
+
+    expect(result).toEqual({ errors: {}, values: validData });
+  });
 });
diff --git a/yup/src/__tests__/yup.ts b/yup/src/__tests__/yup.ts
index 8683d43..587d415 100644
--- a/yup/src/__tests__/yup.ts
+++ b/yup/src/__tests__/yup.ts
@@ -190,4 +190,34 @@ describe('yupResolver', () => {
 
     expect(result).toMatchSnapshot();
   });
+
+  it('should throw any error unrelated to Yup', async () => {
+    const schemaWithCustomError = schema.transform(() => {
+      throw Error('custom error');
+    });
+    const promise = yupResolver(schemaWithCustomError)(validData, undefined, {
+      fields,
+      shouldUseNativeValidation,
+    });
+
+    await expect(promise).rejects.toThrow('custom error');
+  });
+
+  it('should return values from yupResolver when validation pass & raw=true', async () => {
+    const schemaSpy = vi.spyOn(schema, 'validate');
+    const schemaSyncSpy = vi.spyOn(schema, 'validateSync');
+
+    const result = await yupResolver(schema, undefined, { raw: true })(
+      validData,
+      undefined,
+      {
+        fields,
+        shouldUseNativeValidation,
+      },
+    );
+
+    expect(schemaSpy).toHaveBeenCalledTimes(1);
+    expect(schemaSyncSpy).not.toHaveBeenCalled();
+    expect(result).toEqual({ errors: {}, values: validData });
+  });
 });
diff --git a/zod/src/__tests__/zod.ts b/zod/src/__tests__/zod.ts
index fb86ee7..5169821 100644
--- a/zod/src/__tests__/zod.ts
+++ b/zod/src/__tests__/zod.ts
@@ -4,11 +4,11 @@ import { schema, validData, invalidData, fields } from './__fixtures__/data';
 const shouldUseNativeValidation = false;
 
 describe('zodResolver', () => {
-  it('should return values from zodResolver when validation pass & rawValues=true', async () => {
+  it('should return values from zodResolver when validation pass & raw=true', async () => {
     const parseAsyncSpy = vi.spyOn(schema, 'parseAsync');
 
     const result = await zodResolver(schema, undefined, {
-      rawValues: true,
+      raw: true,
     })(validData, undefined, {
       fields,
       shouldUseNativeValidation,
@@ -77,4 +77,16 @@ describe('zodResolver', () => {
 
     expect(result).toMatchSnapshot();
   });
+
+  it('should throw any error unrelated to Zod', async () => {
+    const schemaWithCustomError = schema.refine(() => {
+      throw Error('custom error');
+    });
+    const promise = zodResolver(schemaWithCustomError)(validData, undefined, {
+      fields,
+      shouldUseNativeValidation,
+    });
+
+    await expect(promise).rejects.toThrow('custom error');
+  });
 });