Skip to content
Merged
3 changes: 2 additions & 1 deletion superstruct/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"license": "MIT",
"peerDependencies": {
"react-hook-form": ">=6.6.0",
"@hookform/resolvers": "^1.0.0"
"@hookform/resolvers": "^1.0.0",
"superstruct": ">=0.12.0"
}
}
90 changes: 0 additions & 90 deletions superstruct/src/__tests__/__snapshots__/superstruct.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -62,93 +62,3 @@ Object {
"values": Object {},
}
`;

exports[`superstructResolver should return all the errors from superstructResolver when validation fails with \`validateAllFieldCriteria\` set to true 1`] = `
Object {
"errors": Object {
"birthYear": Object {
"message": "Expected a number, but received: \\"birthYear\\"",
"ref": undefined,
"type": "number",
"types": Object {
"number": "Expected a number, but received: \\"birthYear\\"",
},
},
"email": Object {
"message": "Expected a string matching \`/^[\\\\w-\\\\.]+@([\\\\w-]+\\\\.)+[\\\\w-]{2,4}$/\` but received \\"\\"",
"ref": Object {
"name": "email",
},
"type": "string",
"types": Object {
"string": "Expected a string matching \`/^[\\\\w-\\\\.]+@([\\\\w-]+\\\\.)+[\\\\w-]{2,4}$/\` but received \\"\\"",
},
},
"enabled": Object {
"message": "Expected a value of type \`boolean\`, but received: \`undefined\`",
"ref": undefined,
"type": "boolean",
"types": Object {
"boolean": "Expected a value of type \`boolean\`, but received: \`undefined\`",
},
},
"like": Array [
Object {
"id": Object {
"message": "Expected a number, but received: \\"z\\"",
"ref": undefined,
"type": "number",
"types": Object {
"number": "Expected a number, but received: \\"z\\"",
},
},
"name": Object {
"message": "Expected a string, but received: undefined",
"ref": undefined,
"type": "string",
"types": Object {
"string": "Expected a string, but received: undefined",
},
},
},
],
"password": Object {
"message": "Expected a string matching \`/^[a-zA-Z0-9]{3,30}/\` but received \\"___\\"",
"ref": Object {
"name": "password",
},
"type": "string",
"types": Object {
"string": "Expected a string matching \`/^[a-zA-Z0-9]{3,30}/\` but received \\"___\\"",
},
},
"repeatPassword": Object {
"message": "Expected a value of type \`Password\`, but received: \`undefined\`",
"ref": undefined,
"type": "Password",
"types": Object {
"Password": "Expected a value of type \`Password\`, but received: \`undefined\`",
},
},
"tags": Object {
"message": "Expected an array value, but received: undefined",
"ref": undefined,
"type": "array",
"types": Object {
"array": "Expected an array value, but received: undefined",
},
},
"username": Object {
"message": "Expected a string, but received: undefined",
"ref": Object {
"name": "username",
},
"type": "string",
"types": Object {
"string": "Expected a string, but received: undefined",
},
},
},
"values": Object {},
}
`;
9 changes: 0 additions & 9 deletions superstruct/src/__tests__/superstruct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,4 @@ describe('superstructResolver', () => {

expect(result).toMatchSnapshot();
});

it('should return all the errors from superstructResolver when validation fails with `validateAllFieldCriteria` set to true', async () => {
const result = await superstructResolver(schema)(invalidData, undefined, {
fields,
criteriaMode: 'all',
});

expect(result).toMatchSnapshot();
});
});
71 changes: 18 additions & 53 deletions superstruct/src/superstruct.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,30 @@
import { appendErrors } from 'react-hook-form';
import { FieldError } from 'react-hook-form';
import { toNestError } from '@hookform/resolvers';

import { StructError, validate } from 'superstruct';
import { convertArrayToPathName } from '@hookform/resolvers';
import { Resolver } from './types';

const parseErrorSchema = (
error: StructError,
validateAllFieldCriteria: boolean,
) =>
error
.failures()
.reduce((previous: Record<string, any>, { path, message = '', type }) => {
const currentPath = convertArrayToPathName(path);
return {
...previous,
...(path
? previous[currentPath] && validateAllFieldCriteria
? {
[currentPath]: appendErrors(
currentPath,
validateAllFieldCriteria,
previous,
type || '',
message,
),
}
: {
[currentPath]: previous[currentPath] || {
message,
type,
...(validateAllFieldCriteria
? {
types: { [type || '']: message || true },
}
: {}),
},
}
: {}),
};
}, {});
const parseErrorSchema = (error: StructError) =>
error.failures().reduce<Record<string, FieldError>>(
(previous, error) =>
(previous[error.path.join('.')] = {
message: error.message,
type: error.type,
}) && previous,
{},
);

export const superstructResolver: Resolver = (schema, options) => async (
export const superstructResolver: Resolver = (schema, resolverOptions) => (
values,
_context,
{ criteriaMode, fields },
_,
options,
) => {
const [errors, result] = validate(values, schema, options);

if (errors != null) {
return {
values: {},
errors: toNestError(
parseErrorSchema(errors, criteriaMode === 'all'),
fields,
),
};
}
const result = validate(values, schema, resolverOptions);

return {
values: result,
errors: {},
values: result[1] || {},
errors: result[0]
? toNestError(parseErrorSchema(result[0]), options.fields)
: {},
};
};
2 changes: 1 addition & 1 deletion superstruct/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ export type Resolver = <T extends Struct<any, any>>(
values: UnpackNestedValue<TFieldValues>,
context: TContext | undefined,
options: ResolverOptions<TFieldValues>,
) => Promise<ResolverResult<TFieldValues>>;
) => ResolverResult<TFieldValues>;