import { toNestErrors, validateFieldsNatively } from '@hookform/resolvers';
import type {
FieldError,
FieldErrors,
FieldValues,
Resolver,
} from 'react-hook-form';
import * as t from 'typanion';
function parseErrors(errors: string[], parsedErrors: FieldErrors = {}) {
return errors.reduce((acc, error) => {
const fieldIndex = error.indexOf(':');
const field = error.slice(1, fieldIndex);
const message = error.slice(fieldIndex + 1).trim();
acc[field] = {
message,
} as FieldError;
return acc;
}, parsedErrors);
}
export function typanionResolver(
schema: t.StrictValidator,
schemaOptions?: Pick,
resolverOptions?: {
mode?: 'async' | 'sync';
raw?: false;
},
): Resolver>;
export function typanionResolver(
schema: t.StrictValidator,
schemaOptions: Pick | undefined,
resolverOptions: {
mode?: 'async' | 'sync';
raw: true;
},
): Resolver;
/**
* Creates a resolver for react-hook-form using Typanion schema validation
* @param {t.StrictValidator} schema - The Typanion schema to validate against
* @param {Pick} [schemaOptions] - Optional Typanion validation options
* @returns {Resolver>} A resolver function compatible with react-hook-form
* @example
* const schema = t.isObject({
* name: t.isString(),
* age: t.isInteger()
* });
*
* useForm({
* resolver: typanionResolver(schema)
* });
*/
export function typanionResolver(
schema: t.StrictValidator,
schemaOptions: Pick = {},
): Resolver {
return (values: Input, _, options) => {
const rawErrors: string[] = [];
const isValid = schema(
values,
Object.assign(
{},
{
errors: rawErrors,
},
schemaOptions,
),
);
const parsedErrors = parseErrors(rawErrors);
if (isValid) {
options.shouldUseNativeValidation &&
validateFieldsNatively(parsedErrors, options);
return { values, errors: {} };
}
return { values: {}, errors: toNestErrors(parsedErrors, options) };
};
}