remix-hook-form
Advanced tools
Comparing version 1.2.3 to 2.0.0
@@ -1,4 +0,133 @@ | ||
export { parseFormData, createFormData, getValidatedFormData, validateFormData, getFormDataFromSearchParams, } from "./utilities"; | ||
export * from "./hook"; | ||
export type { UseRemixFormOptions } from "./hook"; | ||
//# sourceMappingURL=index.d.ts.map | ||
import * as react_hook_form from 'react-hook-form'; | ||
import { FieldValues, Resolver, FieldErrors, SubmitHandler, SubmitErrorHandler } from 'react-hook-form'; | ||
import React from 'react'; | ||
import { SubmitFunction, FetcherWithComponents } from '@remix-run/react'; | ||
import { FieldValues as FieldValues$1, UseFormProps, Path, RegisterOptions, UseFormReturn } from 'react-hook-form/dist/types'; | ||
declare const getFormDataFromSearchParams: (request: Pick<Request, "url">) => Record<any, any>; | ||
/** | ||
* Parses the data from an HTTP request and validates it against a schema. Works in both loaders and actions, in loaders it extracts the data from the search params. | ||
* In actions it extracts it from request formData. | ||
* | ||
* @async | ||
* @param {Request} request - An object that represents an HTTP request. | ||
* @param validator - A function that resolves the schema. | ||
* @returns A Promise that resolves to an object containing the validated data or any errors that occurred during validation. | ||
*/ | ||
declare const getValidatedFormData: <T extends FieldValues>(request: Request, resolver: Resolver) => Promise<{ | ||
receivedValues: Record<any, any>; | ||
errors: FieldErrors<T>; | ||
data: undefined; | ||
} | { | ||
receivedValues: Record<any, any>; | ||
errors: undefined; | ||
data: T; | ||
}>; | ||
/** | ||
* Helper method used in actions to validate the form data parsed from the frontend using zod and return a json error if validation fails. | ||
* @param data Data to validate | ||
* @param resolver Schema to validate and cast the data with | ||
* @returns Returns the validated data if successful, otherwise returns the error object | ||
*/ | ||
declare const validateFormData: <T extends FieldValues>(data: any, resolver: Resolver) => Promise<{ | ||
errors: FieldErrors<T>; | ||
data: undefined; | ||
} | { | ||
errors: undefined; | ||
data: T; | ||
}>; | ||
/** | ||
Creates a new instance of FormData with the specified data and key. | ||
@template T - The type of the data parameter. It can be any type of FieldValues. | ||
@param {T} data - The data to be added to the FormData. It can be either an object of type FieldValues. | ||
@param {string} [key="formData"] - The key to be used for adding the data to the FormData. | ||
@returns {FormData} - The FormData object with the data added to it. | ||
*/ | ||
declare const createFormData: <T extends FieldValues>(data: T, key?: string) => FormData; | ||
/** | ||
Parses the specified Request object's FormData to retrieve the data associated with the specified key. | ||
@template T - The type of the data to be returned. | ||
@param {Request} request - The Request object whose FormData is to be parsed. | ||
@param {string} [key="formData"] - The key of the data to be retrieved from the FormData. | ||
@returns {Promise<T>} - A promise that resolves to the data of type T. | ||
@throws {Error} - If no data is found for the specified key, or if the retrieved data is not a string. | ||
*/ | ||
declare const parseFormData: <T extends unknown>(request: Request, key?: string) => Promise<T>; | ||
type SubmitFunctionOptions = Parameters<SubmitFunction>[1]; | ||
interface UseRemixFormOptions<T extends FieldValues$1> extends UseFormProps<T> { | ||
submitHandlers?: { | ||
onValid?: SubmitHandler<T>; | ||
onInvalid?: SubmitErrorHandler<T>; | ||
}; | ||
submitConfig?: SubmitFunctionOptions; | ||
submitData?: FieldValues$1; | ||
fetcher?: FetcherWithComponents<T>; | ||
} | ||
declare const useRemixForm: <T extends FieldValues$1>({ submitHandlers, submitConfig, submitData, fetcher, ...formProps }: UseRemixFormOptions<T>) => { | ||
handleSubmit: (e?: React.BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>; | ||
register: (name: Path<T>, options?: RegisterOptions<T> | undefined) => { | ||
defaultValue: any; | ||
onChange: react_hook_form.ChangeHandler; | ||
onBlur: react_hook_form.ChangeHandler; | ||
ref: react_hook_form.RefCallBack; | ||
name: Path<T>; | ||
min?: string | number | undefined; | ||
max?: string | number | undefined; | ||
maxLength?: number | undefined; | ||
minLength?: number | undefined; | ||
pattern?: string | undefined; | ||
required?: boolean | undefined; | ||
disabled?: boolean | undefined; | ||
}; | ||
formState: { | ||
dirtyFields: Partial<Readonly<react_hook_form.DeepMap<react_hook_form.DeepPartial<T>, boolean>>>; | ||
isDirty: boolean; | ||
isSubmitSuccessful: boolean; | ||
isSubmitted: boolean; | ||
isSubmitting: boolean; | ||
isValid: boolean; | ||
isValidating: boolean; | ||
touchedFields: Partial<Readonly<react_hook_form.DeepMap<react_hook_form.DeepPartial<T>, boolean>>>; | ||
submitCount: number; | ||
isLoading: boolean; | ||
errors: Partial<react_hook_form.FieldErrorsImpl<react_hook_form.DeepRequired<T>>>; | ||
}; | ||
watch: react_hook_form.UseFormWatch<T>; | ||
getValues: react_hook_form.UseFormGetValues<T>; | ||
getFieldState: react_hook_form.UseFormGetFieldState<T>; | ||
setError: react_hook_form.UseFormSetError<T>; | ||
clearErrors: react_hook_form.UseFormClearErrors<T>; | ||
setValue: react_hook_form.UseFormSetValue<T>; | ||
trigger: react_hook_form.UseFormTrigger<T>; | ||
resetField: react_hook_form.UseFormResetField<T>; | ||
reset: react_hook_form.UseFormReset<T>; | ||
unregister: react_hook_form.UseFormUnregister<T>; | ||
control: react_hook_form.Control<T, any>; | ||
setFocus: react_hook_form.UseFormSetFocus<T>; | ||
}; | ||
interface RemixFormProviderProps<T extends FieldValues$1> extends Omit<UseFormReturn<T>, "handleSubmit"> { | ||
children: React.ReactNode; | ||
handleSubmit: any; | ||
register: any; | ||
} | ||
declare const RemixFormProvider: <T extends FieldValues$1>({ children, ...props }: RemixFormProviderProps<T>) => JSX.Element; | ||
declare const useRemixFormContext: <T extends FieldValues$1>() => { | ||
handleSubmit: (e?: React.BaseSyntheticEvent<object, any, any> | undefined) => Promise<void>; | ||
watch: react_hook_form.UseFormWatch<T>; | ||
getValues: react_hook_form.UseFormGetValues<T>; | ||
getFieldState: react_hook_form.UseFormGetFieldState<T>; | ||
setError: react_hook_form.UseFormSetError<T>; | ||
clearErrors: react_hook_form.UseFormClearErrors<T>; | ||
setValue: react_hook_form.UseFormSetValue<T>; | ||
trigger: react_hook_form.UseFormTrigger<T>; | ||
formState: react_hook_form.FormState<T>; | ||
resetField: react_hook_form.UseFormResetField<T>; | ||
reset: react_hook_form.UseFormReset<T>; | ||
unregister: react_hook_form.UseFormUnregister<T>; | ||
control: react_hook_form.Control<T, any>; | ||
register: react_hook_form.UseFormRegister<T>; | ||
setFocus: react_hook_form.UseFormSetFocus<T>; | ||
}; | ||
export { RemixFormProvider, SubmitFunctionOptions, UseRemixFormOptions, createFormData, getFormDataFromSearchParams, getValidatedFormData, parseFormData, useRemixForm, useRemixFormContext, validateFormData }; |
@@ -1,95 +0,196 @@ | ||
import J from "react"; | ||
import { useSubmit as L, useActionData as U, useNavigation as K } from "@remix-run/react"; | ||
import { useForm as T, FormProvider as z, useFormContext as B } from "react-hook-form"; | ||
const g = (t) => { | ||
const a = {}; | ||
for (const [i, o] of t.entries()) { | ||
const n = i.split("."); | ||
let e = a; | ||
for (let s = 0; s < n.length - 1; s++) { | ||
const c = n[s]; | ||
e[c] || (e[c] = /^\d+$/.test(n[s + 1]) ? [] : {}), e = e[c]; | ||
// src/utilities/index.ts | ||
var generateFormData = (formData) => { | ||
const outputObject = {}; | ||
for (const [key, value] of formData.entries()) { | ||
const keyParts = key.split("."); | ||
let currentObject = outputObject; | ||
for (let i = 0; i < keyParts.length - 1; i++) { | ||
const keyPart = keyParts[i]; | ||
if (!currentObject[keyPart]) { | ||
currentObject[keyPart] = /^\d+$/.test(keyParts[i + 1]) ? [] : {}; | ||
} | ||
currentObject = currentObject[keyPart]; | ||
} | ||
const r = n[n.length - 1], l = /\[\d*\]$|\[\]$/.test(r); | ||
if (l) { | ||
const s = r.replace(/\[\d*\]$|\[\]$/, ""); | ||
e[s] || (e[s] = []), e[s].push(o); | ||
const lastKeyPart = keyParts[keyParts.length - 1]; | ||
const lastKeyPartIsArray = /\[\d*\]$|\[\]$/.test(lastKeyPart); | ||
if (lastKeyPartIsArray) { | ||
const key2 = lastKeyPart.replace(/\[\d*\]$|\[\]$/, ""); | ||
if (!currentObject[key2]) { | ||
currentObject[key2] = []; | ||
} | ||
currentObject[key2].push(value); | ||
} | ||
l || (/^\d+$/.test(r) ? e.push(o) : e[r] = o); | ||
if (!lastKeyPartIsArray) { | ||
if (/^\d+$/.test(lastKeyPart)) { | ||
currentObject.push(value); | ||
} else { | ||
currentObject[lastKeyPart] = value; | ||
} | ||
} | ||
} | ||
return a; | ||
}, M = (t) => { | ||
const a = new URL(t.url).searchParams; | ||
return g(a); | ||
}, Q = (t) => t.method === "GET" || t.method === "get", tt = async (t, a) => { | ||
const i = Q(t) ? M(t) : await Y(t); | ||
return { ...await W(i, a), receivedValues: i }; | ||
}, W = async (t, a) => { | ||
const { errors: i, values: o } = await a(t, {}, { shouldUseNativeValidation: !1, fields: {} }); | ||
return Object.keys(i).length > 0 ? { errors: i, data: void 0 } : { errors: void 0, data: o }; | ||
}, X = (t, a = "formData") => { | ||
const i = new FormData(), o = JSON.stringify(t); | ||
return i.append(a, o), i; | ||
}, Y = async (t, a = "formData") => { | ||
const i = await t.formData(), o = i.get(a); | ||
if (!o) | ||
return g(i); | ||
if (typeof o != "string") | ||
return outputObject; | ||
}; | ||
var getFormDataFromSearchParams = (request) => { | ||
const searchParams = new URL(request.url).searchParams; | ||
return generateFormData(searchParams); | ||
}; | ||
var isGet = (request) => request.method === "GET" || request.method === "get"; | ||
var getValidatedFormData = async (request, resolver) => { | ||
const data = isGet(request) ? getFormDataFromSearchParams(request) : await parseFormData(request); | ||
const validatedOutput = await validateFormData(data, resolver); | ||
return { ...validatedOutput, receivedValues: data }; | ||
}; | ||
var validateFormData = async (data, resolver) => { | ||
const { errors, values } = await resolver( | ||
data, | ||
{}, | ||
{ shouldUseNativeValidation: false, fields: {} } | ||
); | ||
if (Object.keys(errors).length > 0) { | ||
return { errors, data: void 0 }; | ||
} | ||
return { errors: void 0, data: values }; | ||
}; | ||
var createFormData = (data, key = "formData") => { | ||
const formData = new FormData(); | ||
const finalData = JSON.stringify(data); | ||
formData.append(key, finalData); | ||
return formData; | ||
}; | ||
var parseFormData = async (request, key = "formData") => { | ||
const formData = await request.formData(); | ||
const data = formData.get(key); | ||
if (!data) { | ||
return generateFormData(formData); | ||
} | ||
if (!(typeof data === "string")) { | ||
throw new Error("Data is not a string"); | ||
return JSON.parse(o); | ||
}, S = (t, a, i = [], o = 0) => { | ||
if (!a) | ||
return t; | ||
for (const [n, e] of Object.entries(a)) | ||
!i.includes(n.toString()) && i.length && o === 0 || (typeof e == "object" && !Array.isArray(e) ? (t[n] || (t[n] = {}), S(t[n], e, i, o + 1)) : e && (t[n] = e)); | ||
return t; | ||
}, ot = ({ submitHandlers: t, submitConfig: a, submitData: i, fetcher: o, ...n }) => { | ||
var e, r, l, s; | ||
const c = L(), y = U(), b = (e = o == null ? void 0 : o.submit) !== null && e !== void 0 ? e : c, u = (r = o == null ? void 0 : o.data) !== null && r !== void 0 ? r : y, d = T(n), p = K().state !== "idle" || o && o.state !== "idle", D = (m) => { | ||
b(X({ ...m, ...i }), { | ||
} | ||
return JSON.parse(data); | ||
}; | ||
var mergeErrors = (frontendErrors, backendErrors, validKeys = [], depth = 0) => { | ||
if (!backendErrors) { | ||
return frontendErrors; | ||
} | ||
for (const [key, rightValue] of Object.entries(backendErrors)) { | ||
if (!validKeys.includes(key.toString()) && validKeys.length && depth === 0) { | ||
continue; | ||
} | ||
if (typeof rightValue === "object" && !Array.isArray(rightValue)) { | ||
if (!frontendErrors[key]) { | ||
frontendErrors[key] = {}; | ||
} | ||
mergeErrors(frontendErrors[key], rightValue, validKeys, depth + 1); | ||
} else if (rightValue) { | ||
frontendErrors[key] = rightValue; | ||
} | ||
} | ||
return frontendErrors; | ||
}; | ||
// src/hook/index.tsx | ||
import React from "react"; | ||
import { | ||
useActionData, | ||
useSubmit, | ||
useNavigation | ||
} from "@remix-run/react"; | ||
import { | ||
useFormContext | ||
} from "react-hook-form"; | ||
import { useForm, FormProvider } from "react-hook-form"; | ||
var useRemixForm = ({ | ||
submitHandlers, | ||
submitConfig, | ||
submitData, | ||
fetcher, | ||
...formProps | ||
}) => { | ||
var _a, _b, _c, _d; | ||
const actionSubmit = useSubmit(); | ||
const actionData = useActionData(); | ||
const submit = (_a = fetcher == null ? void 0 : fetcher.submit) != null ? _a : actionSubmit; | ||
const data = (_b = fetcher == null ? void 0 : fetcher.data) != null ? _b : actionData; | ||
const methods = useForm(formProps); | ||
const navigation = useNavigation(); | ||
const isSubmittingForm = navigation.state !== "idle" || fetcher && fetcher.state !== "idle"; | ||
const onSubmit = (data2) => { | ||
submit(createFormData({ ...data2, ...submitData }), { | ||
method: "post", | ||
...a | ||
...submitConfig | ||
}); | ||
}, F = d.getValues(), h = Object.keys(F), V = () => { | ||
}, O = d.formState, { dirtyFields: P, isDirty: w, isSubmitSuccessful: j, isSubmitted: k, isSubmitting: x, isValid: _, isValidating: $, touchedFields: R, submitCount: A, errors: N, isLoading: C } = O, I = S(N, u != null && u.errors ? u.errors : u, h); | ||
}; | ||
const values = methods.getValues(); | ||
const validKeys = Object.keys(values); | ||
const onInvalid = () => { | ||
}; | ||
const formState = methods.formState; | ||
const { | ||
dirtyFields, | ||
isDirty, | ||
isSubmitSuccessful, | ||
isSubmitted, | ||
isSubmitting, | ||
isValid, | ||
isValidating, | ||
touchedFields, | ||
submitCount, | ||
errors, | ||
isLoading | ||
} = formState; | ||
const formErrors = mergeErrors( | ||
errors, | ||
(data == null ? void 0 : data.errors) ? data.errors : data, | ||
validKeys | ||
); | ||
return { | ||
...d, | ||
handleSubmit: d.handleSubmit((l = t == null ? void 0 : t.onValid) !== null && l !== void 0 ? l : D, (s = t == null ? void 0 : t.onInvalid) !== null && s !== void 0 ? s : V), | ||
register: (m, G) => { | ||
var v, f; | ||
...methods, | ||
handleSubmit: methods.handleSubmit( | ||
(_c = submitHandlers == null ? void 0 : submitHandlers.onValid) != null ? _c : onSubmit, | ||
(_d = submitHandlers == null ? void 0 : submitHandlers.onInvalid) != null ? _d : onInvalid | ||
), | ||
register: (name, options) => { | ||
var _a2, _b2; | ||
return { | ||
...d.register(m, G), | ||
defaultValue: (f = (v = u == null ? void 0 : u.defaultValues) === null || v === void 0 ? void 0 : v[m]) !== null && f !== void 0 ? f : "" | ||
...methods.register(name, options), | ||
defaultValue: (_b2 = (_a2 = data == null ? void 0 : data.defaultValues) == null ? void 0 : _a2[name]) != null ? _b2 : "" | ||
}; | ||
}, | ||
formState: { | ||
dirtyFields: P, | ||
isDirty: w, | ||
isSubmitSuccessful: j, | ||
isSubmitted: k, | ||
isSubmitting: p || x, | ||
isValid: _, | ||
isValidating: $, | ||
touchedFields: R, | ||
submitCount: A, | ||
isLoading: C, | ||
errors: I | ||
dirtyFields, | ||
isDirty, | ||
isSubmitSuccessful, | ||
isSubmitted, | ||
isSubmitting: isSubmittingForm || isSubmitting, | ||
isValid, | ||
isValidating, | ||
touchedFields, | ||
submitCount, | ||
isLoading, | ||
errors: formErrors | ||
} | ||
}; | ||
}, at = ({ children: t, ...a }) => J.createElement(z, { ...a }, t), it = () => { | ||
const t = B(); | ||
}; | ||
var RemixFormProvider = ({ | ||
children, | ||
...props | ||
}) => { | ||
return /* @__PURE__ */ React.createElement(FormProvider, { ...props }, children); | ||
}; | ||
var useRemixFormContext = () => { | ||
const methods = useFormContext(); | ||
return { | ||
...t, | ||
handleSubmit: t.handleSubmit | ||
...methods, | ||
handleSubmit: methods.handleSubmit | ||
}; | ||
}; | ||
export { | ||
at as RemixFormProvider, | ||
X as createFormData, | ||
M as getFormDataFromSearchParams, | ||
tt as getValidatedFormData, | ||
Y as parseFormData, | ||
ot as useRemixForm, | ||
it as useRemixFormContext, | ||
W as validateFormData | ||
RemixFormProvider, | ||
createFormData, | ||
getFormDataFromSearchParams, | ||
getValidatedFormData, | ||
parseFormData, | ||
useRemixForm, | ||
useRemixFormContext, | ||
validateFormData | ||
}; |
{ | ||
"name": "remix-hook-form", | ||
"version": "1.2.3", | ||
"version": "2.0.0", | ||
"description": "Utility wrapper around react-hook-form for use with Remix.run", | ||
"type": "module", | ||
"main": "./dist/index.umd.cjs", | ||
"main": "./dist/index.cjs", | ||
"module": "./dist/index.js", | ||
@@ -11,3 +11,3 @@ "exports": { | ||
"import": "./dist/index.js", | ||
"require": "./dist/index.umd.cjs" | ||
"require": "./dist/index.cjs" | ||
} | ||
@@ -24,8 +24,9 @@ }, | ||
"scripts": { | ||
"build": "tsup src/index.ts --format cjs,esm --dts --clean", | ||
"remix-dev": "npm run dev -w src/testing-app", | ||
"build:dev": "vite build -m development", | ||
"dev": "npm-run-all -s build:dev -p remix-dev vite", | ||
"vite": "vite build --watch -m development", | ||
"prepublishOnly": "vite build", | ||
"build": "vite build", | ||
"build:dev": "npm run build", | ||
"build:dev:watch": "npm run build -- --watch", | ||
"dev": "npm-run-all -s build:dev -p remix-dev build:dev:watch", | ||
"vite": "npm run build --watch -m development", | ||
"prepublishOnly": "npm run build", | ||
"test": "vitest run", | ||
@@ -83,2 +84,3 @@ "tsc": "tsc", | ||
"rollup-plugin-typescript2": "^0.34.1", | ||
"tsup": "^7.2.0", | ||
"typescript": "^5.0.4", | ||
@@ -89,2 +91,2 @@ "vite": "^4.2.1", | ||
} | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
45406
556
0
22
7
1