From 72e37cbadce7c5e1c333d3ee0bb0c0496a9dc55c Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 02:44:04 -0700 Subject: [PATCH 01/10] chore: initial work to add composition api to svelte --- packages/svelte-form/src/createForm.svelte.ts | 51 ++- .../svelte-form/src/createFormRune.svelte.ts | 408 ++++++++++++++++++ 2 files changed, 457 insertions(+), 2 deletions(-) create mode 100644 packages/svelte-form/src/createFormRune.svelte.ts diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts index 4de61273f..b9de14756 100644 --- a/packages/svelte-form/src/createForm.svelte.ts +++ b/packages/svelte-form/src/createForm.svelte.ts @@ -179,6 +179,53 @@ export interface SvelteFormApi< WithoutFunction } + +/** + * An extended version of the `FormApi` class that includes Svelte-specific functionalities from `SvelteFormApi` + */ +export type SvelteFormExtendedApi< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta, +> = FormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta +> & + SvelteFormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + > + + export function createForm< TParentData, TFormOnMount extends undefined | FormValidateOrFn, @@ -241,10 +288,10 @@ export function createForm< // @ts-expect-error constructor definition exists only on a type level extendedApi.Field = (internal, props) => - Field(internal, { ...props, form: api }) + Field(internal, { ...props, form: api as never } as never) extendedApi.createField = (props) => createField(() => { - return { ...props(), form: api } + return { ...props(), form: api } as never }) as never // Type cast because else "Error: Type instantiation is excessively deep and possibly infinite." extendedApi.useStore = (selector) => useStore(api.store, selector) // @ts-expect-error constructor definition exists only on a type level diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts new file mode 100644 index 000000000..9282c1d88 --- /dev/null +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -0,0 +1,408 @@ +import { createForm } from './createForm.svelte' +import type { + AnyFieldApi, + AnyFormApi, + BaseFormOptions, + DeepKeysOfType, + FieldApi, + FieldsMap, + FormAsyncValidateOrFn, + FormOptions, + FormValidateOrFn, +} from '@tanstack/form-core' +import type { FieldComponent } from './useField' +import type { SvelteFormExtendedApi } from './createForm.svelte' +import { getContext } from 'svelte' + +// We should never hit the `null` case here +const fieldContextKey = "__tanstack_field_context_key" +const formContextKey = "__tanstack_form_context_key" + +/** + * TypeScript inferencing is weird. + * + * If you have: + * + * @example + * + * interface Args { + * arg?: T + * } + * + * function test(arg?: Partial>): T { + * return 0 as any; + * } + * + * const a = test({}); + * + * Then `T` will default to `unknown`. + * + * However, if we change `test` to be: + * + * @example + * + * function test(arg?: Partial>): T; + * + * Then `T` becomes `undefined`. + * + * Here, we are checking if the passed type `T` extends `DefaultT` and **only** + * `DefaultT`, as if that's the case we assume that inferencing has not occured. + */ +type UnwrapOrAny = [unknown] extends [T] ? any : T +type UnwrapDefaultOrAny = [DefaultT] extends [T] + ? [T] extends [DefaultT] + ? any + : T + : T + +export function createFormHookContexts() { + function useFieldContext() { + const field = getContext(fieldContextKey) as AnyFieldApi; + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!field) { + throw new Error( + '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`', + ) + } + + return field as FieldApi< + any, + string, + TData, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any + > + } + + function useFormContext() { + const form = getContext(formContextKey) as AnyFormApi + + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + if (!form) { + throw new Error( + '`formContext` only works when within a `formComponent` passed to `createFormHook`', + ) + } + + return form as SvelteFormExtendedApi< + // If you need access to the form data, you need to use `withForm` instead + Record, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any, + any + > + } + + return { fieldContext: fieldContextKey, useFieldContext, useFormContext, formContext: formContextKey } +} + +interface CreateFormHookProps< + TFieldComponents extends Record>, + TFormComponents extends Record>, +> { + fieldComponents: TFieldComponents + fieldContext: Context + formComponents: TFormComponents + formContext: Context +} + +/** + * @private + */ +export type AppFieldExtendedReactFormApi< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta, + TFieldComponents extends Record>, + TFormComponents extends Record>, +> = SvelteFormExtendedApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta +> & + NoInfer & { + AppField: FieldComponent< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + NoInfer + > + AppForm: ComponentType + } + +export interface WithFormProps< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta, + TFieldComponents extends Record>, + TFormComponents extends Record>, + TRenderProps extends object = Record, +> extends FormOptions< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + > { + // Optional, but adds props to the `render` function outside of `form` + props?: TRenderProps + render: ( + props: PropsWithChildren< + NoInfer & { + form: AppFieldExtendedReactFormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TFieldComponents, + TFormComponents + > + } + >, + ) => JSX.Element +} + +export function createFormHook< + const TComponents extends Record>, + const TFormComponents extends Record>, +>({ + fieldComponents, + fieldContext, + formContext, + formComponents, +}: CreateFormHookProps) { + function useAppForm< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta, + >( + props: () => FormOptions< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + >, + ): AppFieldExtendedReactFormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TComponents, + TFormComponents + > { + const form = createForm(props) + + const AppForm = useMemo(() => { + const AppForm = (({ children }) => { + return ( + {children} + ) + }) as ComponentType + return AppForm + }, [form]) + + const AppField = useMemo(() => { + const AppField = (({ children, ...props }) => { + return ( + + {(field) => ( + // eslint-disable-next-line @eslint-react/no-context-provider + + {children(Object.assign(field, fieldComponents))} + + )} + + ) + }) as FieldComponent< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TComponents + > + return AppField + }, [form]) + + const extendedForm = useMemo(() => { + return Object.assign(form, { + AppField, + AppForm, + ...formComponents, + }) + }, [form, AppField, AppForm]) + + return extendedForm + } + + function withForm< + TFormData, + TOnMount extends undefined | FormValidateOrFn, + TOnChange extends undefined | FormValidateOrFn, + TOnChangeAsync extends undefined | FormAsyncValidateOrFn, + TOnBlur extends undefined | FormValidateOrFn, + TOnBlurAsync extends undefined | FormAsyncValidateOrFn, + TOnSubmit extends undefined | FormValidateOrFn, + TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, + TOnDynamic extends undefined | FormValidateOrFn, + TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, + TOnServer extends undefined | FormAsyncValidateOrFn, + TSubmitMeta, + TRenderProps extends object = {}, + >({ + render, + props, + }: WithFormProps< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TComponents, + TFormComponents, + TRenderProps + >): WithFormProps< + UnwrapOrAny, + UnwrapDefaultOrAny, TOnMount>, + UnwrapDefaultOrAny, TOnChange>, + UnwrapDefaultOrAny, TOnChangeAsync>, + UnwrapDefaultOrAny, TOnBlur>, + UnwrapDefaultOrAny, TOnBlurAsync>, + UnwrapDefaultOrAny, TOnSubmit>, + UnwrapDefaultOrAny, TOnSubmitAsync>, + UnwrapDefaultOrAny, TOnDynamic>, + UnwrapDefaultOrAny< + undefined | FormValidateOrFn, + TOnDynamicAsync + >, + UnwrapDefaultOrAny, TOnServer>, + UnwrapOrAny, + UnwrapOrAny, + UnwrapOrAny, + UnwrapOrAny + >['render'] { + return (innerProps) => render({ ...props, ...innerProps }) + } + + return { + useAppForm, + withForm, + } +} From 86a9e366e8d15dfa492db176497cf90456d9489e Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:02:03 -0700 Subject: [PATCH 02/10] chore: more fixes on types, maybe --- .../svelte-form/src/createFormRune.svelte.ts | 30 +-- packages/svelte-form/src/types.ts | 218 +++++++++--------- 2 files changed, 126 insertions(+), 122 deletions(-) diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index 9282c1d88..d52d0139f 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -10,9 +10,9 @@ import type { FormOptions, FormValidateOrFn, } from '@tanstack/form-core' -import type { FieldComponent } from './useField' +import type { FieldComponent } from './types.js' import type { SvelteFormExtendedApi } from './createForm.svelte' -import { getContext } from 'svelte' +import { Component, getContext, Snippet, SvelteComponent } from 'svelte' // We should never hit the `null` case here const fieldContextKey = "__tanstack_field_context_key" @@ -124,8 +124,8 @@ export function createFormHookContexts() { } interface CreateFormHookProps< - TFieldComponents extends Record>, - TFormComponents extends Record>, + TFieldComponents extends Record>, + TFormComponents extends Record>, > { fieldComponents: TFieldComponents fieldContext: Context @@ -149,8 +149,8 @@ export type AppFieldExtendedReactFormApi< TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, TSubmitMeta, - TFieldComponents extends Record>, - TFormComponents extends Record>, + TFieldComponents extends Record>, + TFormComponents extends Record>, > = SvelteFormExtendedApi< TFormData, TOnMount, @@ -181,7 +181,7 @@ export type AppFieldExtendedReactFormApi< TSubmitMeta, NoInfer > - AppForm: ComponentType + AppForm: Component<{children: Snippet}> } export interface WithFormProps< @@ -197,8 +197,8 @@ export interface WithFormProps< TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TOnServer extends undefined | FormAsyncValidateOrFn, TSubmitMeta, - TFieldComponents extends Record>, - TFormComponents extends Record>, + TFieldComponents extends Record>, + TFormComponents extends Record>, TRenderProps extends object = Record, > extends FormOptions< TFormData, @@ -217,7 +217,7 @@ export interface WithFormProps< // Optional, but adds props to the `render` function outside of `form` props?: TRenderProps render: ( - props: PropsWithChildren< + props: NoInfer & { form: AppFieldExtendedReactFormApi< TFormData, @@ -234,15 +234,15 @@ export interface WithFormProps< TSubmitMeta, TFieldComponents, TFormComponents - > + >, + children: Snippet } - >, - ) => JSX.Element + ) => SvelteComponent } export function createFormHook< - const TComponents extends Record>, - const TFormComponents extends Record>, + const TComponents extends Record>, + const TFormComponents extends Record>, >({ fieldComponents, fieldContext, diff --git a/packages/svelte-form/src/types.ts b/packages/svelte-form/src/types.ts index cfb9d587c..6f2806d31 100644 --- a/packages/svelte-form/src/types.ts +++ b/packages/svelte-form/src/types.ts @@ -23,20 +23,20 @@ export type CreateFieldOptions< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, @@ -123,6 +123,7 @@ export type FieldComponent< TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, + ExtendedApi = {}, > = // This giant type allows the type // - to be used as a function (which they are now in Svelte 5) @@ -135,20 +136,20 @@ export type FieldComponent< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, >( internal: any, { @@ -178,70 +179,72 @@ export type FieldComponent< TFormOnDynamic, TFormOnDynamicAsync, TFormOnServer, - TParentSubmitMeta + TParentSubmitMeta, + ExtendedApi >, 'form' >, ) => {}) & - (new < - TName extends DeepKeys, - TData extends DeepValue, - TOnMount extends undefined | FieldValidateOrFn, - TOnChange extends - | undefined - | FieldValidateOrFn, - TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnBlur extends undefined | FieldValidateOrFn, - TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnSubmit extends - | undefined - | FieldValidateOrFn, - TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnDynamic extends - | undefined - | FieldValidateOrFn, - TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, - >( - opts: ComponentConstructorOptions< - Omit< - FieldComponentProps< - TParentData, - TName, - TData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TFormOnMount, - TFormOnChange, - TFormOnChangeAsync, - TFormOnBlur, - TFormOnBlurAsync, - TFormOnSubmit, - TFormOnSubmitAsync, - TFormOnDynamic, - TFormOnDynamicAsync, - TFormOnServer, - TParentSubmitMeta - >, - 'form' - > - >, - ) => SvelteComponent) & - WithoutFunction + (new < + TName extends DeepKeys, + TData extends DeepValue, + TOnMount extends undefined | FieldValidateOrFn, + TOnChange extends + | undefined + | FieldValidateOrFn, + TOnChangeAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnBlur extends undefined | FieldValidateOrFn, + TOnBlurAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnSubmit extends + | undefined + | FieldValidateOrFn, + TOnSubmitAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnDynamic extends + | undefined + | FieldValidateOrFn, + TOnDynamicAsync extends + | undefined + | FieldAsyncValidateOrFn, + >( + opts: ComponentConstructorOptions< + Omit< + FieldComponentProps< + TParentData, + TName, + TData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnDynamic, + TFormOnDynamicAsync, + TFormOnServer, + TParentSubmitMeta, + ExtendedApi + >, + 'form' + > + >, + ) => SvelteComponent) & + WithoutFunction type FieldComponentProps< TParentData, @@ -250,20 +253,20 @@ type FieldComponentProps< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, @@ -275,6 +278,7 @@ type FieldComponentProps< TFormOnDynamicAsync extends undefined | FormAsyncValidateOrFn, TFormOnServer extends undefined | FormAsyncValidateOrFn, TParentSubmitMeta, + ExtendedApi = {}, > = { children: Snippet< [ @@ -302,7 +306,7 @@ type FieldComponentProps< TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta - >, + > & ExtendedApi, ] > } & Omit< @@ -353,20 +357,20 @@ export type CreateField< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TSubmitMeta, >( opts: () => { name: Narrow } & Omit< @@ -422,17 +426,17 @@ export type CreateField< TFormOnServer, TParentSubmitMeta > & - SvelteFieldApi< - TParentData, - TFormOnMount, - TFormOnChange, - TFormOnChangeAsync, - TFormOnBlur, - TFormOnBlurAsync, - TFormOnSubmit, - TFormOnSubmitAsync, - TFormOnDynamic, - TFormOnDynamicAsync, - TFormOnServer, - TParentSubmitMeta - > + SvelteFieldApi< + TParentData, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnDynamic, + TFormOnDynamicAsync, + TFormOnServer, + TParentSubmitMeta + > From 2102ab42a6af00ed1519f564c964a40d5f83f5c5 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:19:59 -0700 Subject: [PATCH 03/10] chore: more work on the Svelte composition adapter --- packages/svelte-form/src/AppField.svelte | 17 +++ packages/svelte-form/src/AppForm.svelte | 16 +++ packages/svelte-form/src/InnerAppField.svelte | 16 +++ packages/svelte-form/src/context-keys.ts | 2 + .../svelte-form/src/createFormRune.svelte.ts | 111 +++++++----------- 5 files changed, 94 insertions(+), 68 deletions(-) create mode 100644 packages/svelte-form/src/AppField.svelte create mode 100644 packages/svelte-form/src/AppForm.svelte create mode 100644 packages/svelte-form/src/InnerAppField.svelte create mode 100644 packages/svelte-form/src/context-keys.ts diff --git a/packages/svelte-form/src/AppField.svelte b/packages/svelte-form/src/AppField.svelte new file mode 100644 index 000000000..9fa1b43fe --- /dev/null +++ b/packages/svelte-form/src/AppField.svelte @@ -0,0 +1,17 @@ + + + + + {#snippet children(field: any)} + + {/snippet} + diff --git a/packages/svelte-form/src/AppForm.svelte b/packages/svelte-form/src/AppForm.svelte new file mode 100644 index 000000000..ce0bf3798 --- /dev/null +++ b/packages/svelte-form/src/AppForm.svelte @@ -0,0 +1,16 @@ + + + +{@render children?.()} diff --git a/packages/svelte-form/src/InnerAppField.svelte b/packages/svelte-form/src/InnerAppField.svelte new file mode 100644 index 000000000..626203cc1 --- /dev/null +++ b/packages/svelte-form/src/InnerAppField.svelte @@ -0,0 +1,16 @@ + + + +{@render children?.()} diff --git a/packages/svelte-form/src/context-keys.ts b/packages/svelte-form/src/context-keys.ts new file mode 100644 index 000000000..e1e591207 --- /dev/null +++ b/packages/svelte-form/src/context-keys.ts @@ -0,0 +1,2 @@ +export const fieldContextKey = "__tanstack_field_context_key" +export const formContextKey = "__tanstack_form_context_key" diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index d52d0139f..fd172974c 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -2,10 +2,7 @@ import { createForm } from './createForm.svelte' import type { AnyFieldApi, AnyFormApi, - BaseFormOptions, - DeepKeysOfType, FieldApi, - FieldsMap, FormAsyncValidateOrFn, FormOptions, FormValidateOrFn, @@ -13,10 +10,9 @@ import type { import type { FieldComponent } from './types.js' import type { SvelteFormExtendedApi } from './createForm.svelte' import { Component, getContext, Snippet, SvelteComponent } from 'svelte' - -// We should never hit the `null` case here -const fieldContextKey = "__tanstack_field_context_key" -const formContextKey = "__tanstack_form_context_key" +import AppFormSvelte from './AppForm.svelte' +import AppFieldSvelte from './AppField.svelte' +import { fieldContextKey, formContextKey } from './context-keys.js' /** * TypeScript inferencing is weird. @@ -51,8 +47,8 @@ const formContextKey = "__tanstack_form_context_key" type UnwrapOrAny = [unknown] extends [T] ? any : T type UnwrapDefaultOrAny = [DefaultT] extends [T] ? [T] extends [DefaultT] - ? any - : T + ? any + : T : T export function createFormHookContexts() { @@ -181,7 +177,7 @@ export type AppFieldExtendedReactFormApi< TSubmitMeta, NoInfer > - AppForm: Component<{children: Snippet}> + AppForm: Component<{ children: Snippet }> } export interface WithFormProps< @@ -201,23 +197,23 @@ export interface WithFormProps< TFormComponents extends Record>, TRenderProps extends object = Record, > extends FormOptions< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta - > { + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta +> { // Optional, but adds props to the `render` function outside of `form` props?: TRenderProps render: ( - props: + props: NoInfer & { form: AppFieldExtendedReactFormApi< TFormData, @@ -295,52 +291,31 @@ export function createFormHook< > { const form = createForm(props) - const AppForm = useMemo(() => { - const AppForm = (({ children }) => { - return ( - {children} - ) - }) as ComponentType - return AppForm - }, [form]) + const AppForm = ((internal, props) => { + return AppFormSvelte(internal, { ...props, form }) + }) as Component<{ children: Snippet }> - const AppField = useMemo(() => { - const AppField = (({ children, ...props }) => { - return ( - - {(field) => ( - // eslint-disable-next-line @eslint-react/no-context-provider - - {children(Object.assign(field, fieldComponents))} - - )} - - ) - }) as FieldComponent< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta, - TComponents - > - return AppField - }, [form]) + const AppField = ((internal, props) => AppFieldSvelte(internal, { ...props, form } as never)) as FieldComponent< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TComponents + > - const extendedForm = useMemo(() => { - return Object.assign(form, { - AppField, - AppForm, - ...formComponents, - }) - }, [form, AppField, AppForm]) + const extendedForm = Object.assign(form, { + AppField, + AppForm, + ...formComponents, + }) return extendedForm } From ca176cb8594ecb8fdd3566d9a78ce4606d8b7a90 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:20:40 -0700 Subject: [PATCH 04/10] chore: rename from Hook to Rune --- packages/svelte-form/src/createFormRune.svelte.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index fd172974c..32f676064 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -51,14 +51,14 @@ type UnwrapDefaultOrAny = [DefaultT] extends [T] : T : T -export function createFormHookContexts() { +export function createFormRuneContexts() { function useFieldContext() { const field = getContext(fieldContextKey) as AnyFieldApi; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!field) { throw new Error( - '`fieldContext` only works when within a `fieldComponent` passed to `createFormHook`', + '`fieldContext` only works when within a `fieldComponent` passed to `createFormRune`', ) } @@ -95,7 +95,7 @@ export function createFormHookContexts() { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!form) { throw new Error( - '`formContext` only works when within a `formComponent` passed to `createFormHook`', + '`formContext` only works when within a `formComponent` passed to `createFormRune`', ) } @@ -119,7 +119,7 @@ export function createFormHookContexts() { return { fieldContext: fieldContextKey, useFieldContext, useFormContext, formContext: formContextKey } } -interface CreateFormHookProps< +interface CreateFormRuneProps< TFieldComponents extends Record>, TFormComponents extends Record>, > { @@ -236,7 +236,7 @@ export interface WithFormProps< ) => SvelteComponent } -export function createFormHook< +export function createFormRune< const TComponents extends Record>, const TFormComponents extends Record>, >({ @@ -244,7 +244,7 @@ export function createFormHook< fieldContext, formContext, formComponents, -}: CreateFormHookProps) { +}: CreateFormRuneProps) { function useAppForm< TFormData, TOnMount extends undefined | FormValidateOrFn, From 329a1305864170eea149ba0c64bd1203b33f62a7 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:25:33 -0700 Subject: [PATCH 05/10] chore: attempt to finalize Svelte composition support --- packages/svelte-form/src/AppField.svelte | 5 +- packages/svelte-form/src/InnerAppField.svelte | 7 +- .../svelte-form/src/createFormRune.svelte.ts | 65 +------------------ 3 files changed, 9 insertions(+), 68 deletions(-) diff --git a/packages/svelte-form/src/AppField.svelte b/packages/svelte-form/src/AppField.svelte index 9fa1b43fe..3fdc0aff1 100644 --- a/packages/svelte-form/src/AppField.svelte +++ b/packages/svelte-form/src/AppField.svelte @@ -5,13 +5,14 @@ interface Props { form: any + fieldComponents: any children: Snippet } - const { children, form }: Props = $props() + const { children, form, fieldComponents }: Props = $props() {#snippet children(field: any)} - + {/snippet} diff --git a/packages/svelte-form/src/InnerAppField.svelte b/packages/svelte-form/src/InnerAppField.svelte index 626203cc1..de0f34873 100644 --- a/packages/svelte-form/src/InnerAppField.svelte +++ b/packages/svelte-form/src/InnerAppField.svelte @@ -5,12 +5,13 @@ interface Props { field: any - children: Snippet + fieldComponents: any + children: Snippet<[any]> } - const { children, field }: Props = $props() + const { children, field, fieldComponents }: Props = $props() setContext(fieldContextKey, field) -{@render children?.()} +{@render children?.(Object.assign(field, fieldComponents))} diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index 32f676064..908a872bb 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -116,7 +116,7 @@ export function createFormRuneContexts() { > } - return { fieldContext: fieldContextKey, useFieldContext, useFormContext, formContext: formContextKey } + return { useFieldContext, useFormContext } } interface CreateFormRuneProps< @@ -124,9 +124,7 @@ interface CreateFormRuneProps< TFormComponents extends Record>, > { fieldComponents: TFieldComponents - fieldContext: Context formComponents: TFormComponents - formContext: Context } /** @@ -241,8 +239,6 @@ export function createFormRune< const TFormComponents extends Record>, >({ fieldComponents, - fieldContext, - formContext, formComponents, }: CreateFormRuneProps) { function useAppForm< @@ -295,7 +291,7 @@ export function createFormRune< return AppFormSvelte(internal, { ...props, form }) }) as Component<{ children: Snippet }> - const AppField = ((internal, props) => AppFieldSvelte(internal, { ...props, form } as never)) as FieldComponent< + const AppField = ((internal, props) => AppFieldSvelte(internal, { ...props, form, fieldComponents } as never)) as FieldComponent< TFormData, TOnMount, TOnChange, @@ -320,64 +316,7 @@ export function createFormRune< return extendedForm } - function withForm< - TFormData, - TOnMount extends undefined | FormValidateOrFn, - TOnChange extends undefined | FormValidateOrFn, - TOnChangeAsync extends undefined | FormAsyncValidateOrFn, - TOnBlur extends undefined | FormValidateOrFn, - TOnBlurAsync extends undefined | FormAsyncValidateOrFn, - TOnSubmit extends undefined | FormValidateOrFn, - TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, - TOnDynamic extends undefined | FormValidateOrFn, - TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, - TOnServer extends undefined | FormAsyncValidateOrFn, - TSubmitMeta, - TRenderProps extends object = {}, - >({ - render, - props, - }: WithFormProps< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta, - TComponents, - TFormComponents, - TRenderProps - >): WithFormProps< - UnwrapOrAny, - UnwrapDefaultOrAny, TOnMount>, - UnwrapDefaultOrAny, TOnChange>, - UnwrapDefaultOrAny, TOnChangeAsync>, - UnwrapDefaultOrAny, TOnBlur>, - UnwrapDefaultOrAny, TOnBlurAsync>, - UnwrapDefaultOrAny, TOnSubmit>, - UnwrapDefaultOrAny, TOnSubmitAsync>, - UnwrapDefaultOrAny, TOnDynamic>, - UnwrapDefaultOrAny< - undefined | FormValidateOrFn, - TOnDynamicAsync - >, - UnwrapDefaultOrAny, TOnServer>, - UnwrapOrAny, - UnwrapOrAny, - UnwrapOrAny, - UnwrapOrAny - >['render'] { - return (innerProps) => render({ ...props, ...innerProps }) - } - return { useAppForm, - withForm, } } From 29843ad5470fbd3c7634ec2cad9d60eb363f8e47 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:41:45 -0700 Subject: [PATCH 06/10] chore: add tests and fix issues with usage --- packages/svelte-form/src/AppField.svelte | 5 ++- .../svelte-form/src/createFormRune.svelte.ts | 6 +-- packages/svelte-form/src/index.ts | 2 + .../tests/large-components/context.ts | 3 ++ .../tests/large-components/rune.ts | 9 ++++ .../tests/large-components/text-field.svelte | 16 +++++++ packages/svelte-form/tests/large.svelte | 42 +++++++++++++++++++ packages/svelte-form/tests/large.test.ts | 36 ++++++++++++++++ 8 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 packages/svelte-form/tests/large-components/context.ts create mode 100644 packages/svelte-form/tests/large-components/rune.ts create mode 100644 packages/svelte-form/tests/large-components/text-field.svelte create mode 100644 packages/svelte-form/tests/large.svelte create mode 100644 packages/svelte-form/tests/large.test.ts diff --git a/packages/svelte-form/src/AppField.svelte b/packages/svelte-form/src/AppField.svelte index 3fdc0aff1..41daccd13 100644 --- a/packages/svelte-form/src/AppField.svelte +++ b/packages/svelte-form/src/AppField.svelte @@ -7,11 +7,12 @@ form: any fieldComponents: any children: Snippet + fieldProps: any } - const { children, form, fieldComponents }: Props = $props() + const { children, form, fieldComponents, fieldProps }: Props = $props() - + {#snippet children(field: any)} {/snippet} diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index 908a872bb..1bb444056 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -241,7 +241,7 @@ export function createFormRune< fieldComponents, formComponents, }: CreateFormRuneProps) { - function useAppForm< + function createAppForm< TFormData, TOnMount extends undefined | FormValidateOrFn, TOnChange extends undefined | FormValidateOrFn, @@ -291,7 +291,7 @@ export function createFormRune< return AppFormSvelte(internal, { ...props, form }) }) as Component<{ children: Snippet }> - const AppField = ((internal, props) => AppFieldSvelte(internal, { ...props, form, fieldComponents } as never)) as FieldComponent< + const AppField = ((internal, {children, ...fieldProps}) => AppFieldSvelte(internal, { fieldProps, form, fieldComponents, children } as never)) as FieldComponent< TFormData, TOnMount, TOnChange, @@ -317,6 +317,6 @@ export function createFormRune< } return { - useAppForm, + createAppForm, } } diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts index a76cbb7f2..26a7c4d30 100644 --- a/packages/svelte-form/src/index.ts +++ b/packages/svelte-form/src/index.ts @@ -7,3 +7,5 @@ export { createForm, type SvelteFormApi } from './createForm.svelte.js' export { default as Field, createField } from './Field.svelte' export type { CreateField, FieldComponent } from './types.js' + +export { createFormRune, createFormRuneContexts } from "./createFormRune.svelte.js" \ No newline at end of file diff --git a/packages/svelte-form/tests/large-components/context.ts b/packages/svelte-form/tests/large-components/context.ts new file mode 100644 index 000000000..bb53d8f7f --- /dev/null +++ b/packages/svelte-form/tests/large-components/context.ts @@ -0,0 +1,3 @@ +import { createFormRuneContexts } from "../../src/index.js"; + +export const { useFieldContext } = createFormRuneContexts() \ No newline at end of file diff --git a/packages/svelte-form/tests/large-components/rune.ts b/packages/svelte-form/tests/large-components/rune.ts new file mode 100644 index 000000000..3654a55b1 --- /dev/null +++ b/packages/svelte-form/tests/large-components/rune.ts @@ -0,0 +1,9 @@ +import { createFormRune } from "../../src/createFormRune.svelte.js"; +import TextField from "./text-field.svelte"; + +export const { createAppForm } = createFormRune({ + fieldComponents: { + TextField, + }, + formComponents: {}, +}) \ No newline at end of file diff --git a/packages/svelte-form/tests/large-components/text-field.svelte b/packages/svelte-form/tests/large-components/text-field.svelte new file mode 100644 index 000000000..267569203 --- /dev/null +++ b/packages/svelte-form/tests/large-components/text-field.svelte @@ -0,0 +1,16 @@ + + + diff --git a/packages/svelte-form/tests/large.svelte b/packages/svelte-form/tests/large.svelte new file mode 100644 index 000000000..30ee0f575 --- /dev/null +++ b/packages/svelte-form/tests/large.svelte @@ -0,0 +1,42 @@ + + + + + + +
{ + e.preventDefault() + e.stopPropagation() + form.handleSubmit() + }} +> +

TanStack Form - Svelte Demo

+ + + {#snippet children(field)} + + {/snippet} + +
+ +
{JSON.stringify(formState.current, null, 2)}
diff --git a/packages/svelte-form/tests/large.test.ts b/packages/svelte-form/tests/large.test.ts new file mode 100644 index 000000000..aee46d855 --- /dev/null +++ b/packages/svelte-form/tests/large.test.ts @@ -0,0 +1,36 @@ +import { afterEach, beforeEach, describe, expect, it } from 'vitest' +import { userEvent } from '@testing-library/user-event' +import { mount, unmount } from 'svelte' +import TestForm, { getSampleData } from './large.svelte' + +describe('Svelte Tests', () => { + let element: HTMLDivElement + let instance: any + beforeEach(async () => { + element = document.createElement('div') + document.body.appendChild(element) + instance = mount(TestForm, { + target: element, + }) + }) + + afterEach(() => { + unmount(instance) + element.remove() + }) + + it('should have initial values', async () => { + expect(element.querySelector('#firstName')).toHaveValue( + getSampleData().firstName, + ) + }) + + it('should mirror user input', async () => { + const firstName = element.querySelector('#firstName')! + const firstNameValue = 'Jobs' + await userEvent.type(firstName, firstNameValue) + + const form = JSON.parse(element.querySelector('pre')!.textContent!) + expect(form.values.firstName).toBe(getSampleData().firstName + firstNameValue) + }) +}) From babb7c7c6ed72e8c2ea66244e535320d4aa2ce03 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 10:44:07 +0000 Subject: [PATCH 07/10] ci: apply automated fixes and generate docs --- packages/svelte-form/src/context-keys.ts | 4 +- packages/svelte-form/src/createForm.svelte.ts | 2 - .../svelte-form/src/createFormRune.svelte.ts | 79 ++++--- packages/svelte-form/src/index.ts | 5 +- packages/svelte-form/src/types.ts | 215 +++++++++--------- .../tests/large-components/context.ts | 4 +- .../tests/large-components/rune.ts | 6 +- packages/svelte-form/tests/large.test.ts | 4 +- 8 files changed, 164 insertions(+), 155 deletions(-) diff --git a/packages/svelte-form/src/context-keys.ts b/packages/svelte-form/src/context-keys.ts index e1e591207..7903532e9 100644 --- a/packages/svelte-form/src/context-keys.ts +++ b/packages/svelte-form/src/context-keys.ts @@ -1,2 +1,2 @@ -export const fieldContextKey = "__tanstack_field_context_key" -export const formContextKey = "__tanstack_form_context_key" +export const fieldContextKey = '__tanstack_field_context_key' +export const formContextKey = '__tanstack_form_context_key' diff --git a/packages/svelte-form/src/createForm.svelte.ts b/packages/svelte-form/src/createForm.svelte.ts index b9de14756..5bd92181c 100644 --- a/packages/svelte-form/src/createForm.svelte.ts +++ b/packages/svelte-form/src/createForm.svelte.ts @@ -179,7 +179,6 @@ export interface SvelteFormApi< WithoutFunction } - /** * An extended version of the `FormApi` class that includes Svelte-specific functionalities from `SvelteFormApi` */ @@ -225,7 +224,6 @@ export type SvelteFormExtendedApi< TSubmitMeta > - export function createForm< TParentData, TFormOnMount extends undefined | FormValidateOrFn, diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index 1bb444056..13d77d6f6 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -47,13 +47,13 @@ import { fieldContextKey, formContextKey } from './context-keys.js' type UnwrapOrAny = [unknown] extends [T] ? any : T type UnwrapDefaultOrAny = [DefaultT] extends [T] ? [T] extends [DefaultT] - ? any - : T + ? any + : T : T export function createFormRuneContexts() { function useFieldContext() { - const field = getContext(fieldContextKey) as AnyFieldApi; + const field = getContext(fieldContextKey) as AnyFieldApi // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!field) { @@ -195,42 +195,41 @@ export interface WithFormProps< TFormComponents extends Record>, TRenderProps extends object = Record, > extends FormOptions< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta -> { + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta + > { // Optional, but adds props to the `render` function outside of `form` props?: TRenderProps render: ( - props: - NoInfer & { - form: AppFieldExtendedReactFormApi< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta, - TFieldComponents, - TFormComponents - >, - children: Snippet - } + props: NoInfer & { + form: AppFieldExtendedReactFormApi< + TFormData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TOnServer, + TSubmitMeta, + TFieldComponents, + TFormComponents + > + children: Snippet + }, ) => SvelteComponent } @@ -291,7 +290,13 @@ export function createFormRune< return AppFormSvelte(internal, { ...props, form }) }) as Component<{ children: Snippet }> - const AppField = ((internal, {children, ...fieldProps}) => AppFieldSvelte(internal, { fieldProps, form, fieldComponents, children } as never)) as FieldComponent< + const AppField = ((internal, { children, ...fieldProps }) => + AppFieldSvelte(internal, { + fieldProps, + form, + fieldComponents, + children, + } as never)) as FieldComponent< TFormData, TOnMount, TOnChange, diff --git a/packages/svelte-form/src/index.ts b/packages/svelte-form/src/index.ts index 26a7c4d30..ed692af69 100644 --- a/packages/svelte-form/src/index.ts +++ b/packages/svelte-form/src/index.ts @@ -8,4 +8,7 @@ export { default as Field, createField } from './Field.svelte' export type { CreateField, FieldComponent } from './types.js' -export { createFormRune, createFormRuneContexts } from "./createFormRune.svelte.js" \ No newline at end of file +export { + createFormRune, + createFormRuneContexts, +} from './createFormRune.svelte.js' diff --git a/packages/svelte-form/src/types.ts b/packages/svelte-form/src/types.ts index 6f2806d31..853834d0a 100644 --- a/packages/svelte-form/src/types.ts +++ b/packages/svelte-form/src/types.ts @@ -23,20 +23,20 @@ export type CreateFieldOptions< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, @@ -136,20 +136,20 @@ export type FieldComponent< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, >( internal: any, { @@ -185,66 +185,66 @@ export type FieldComponent< 'form' >, ) => {}) & - (new < - TName extends DeepKeys, - TData extends DeepValue, - TOnMount extends undefined | FieldValidateOrFn, - TOnChange extends - | undefined - | FieldValidateOrFn, - TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnBlur extends undefined | FieldValidateOrFn, - TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnSubmit extends - | undefined - | FieldValidateOrFn, - TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, - TOnDynamic extends - | undefined - | FieldValidateOrFn, - TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, - >( - opts: ComponentConstructorOptions< - Omit< - FieldComponentProps< - TParentData, - TName, - TData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TFormOnMount, - TFormOnChange, - TFormOnChangeAsync, - TFormOnBlur, - TFormOnBlurAsync, - TFormOnSubmit, - TFormOnSubmitAsync, - TFormOnDynamic, - TFormOnDynamicAsync, - TFormOnServer, - TParentSubmitMeta, - ExtendedApi - >, - 'form' - > - >, - ) => SvelteComponent) & - WithoutFunction + (new < + TName extends DeepKeys, + TData extends DeepValue, + TOnMount extends undefined | FieldValidateOrFn, + TOnChange extends + | undefined + | FieldValidateOrFn, + TOnChangeAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnBlur extends undefined | FieldValidateOrFn, + TOnBlurAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnSubmit extends + | undefined + | FieldValidateOrFn, + TOnSubmitAsync extends + | undefined + | FieldAsyncValidateOrFn, + TOnDynamic extends + | undefined + | FieldValidateOrFn, + TOnDynamicAsync extends + | undefined + | FieldAsyncValidateOrFn, + >( + opts: ComponentConstructorOptions< + Omit< + FieldComponentProps< + TParentData, + TName, + TData, + TOnMount, + TOnChange, + TOnChangeAsync, + TOnBlur, + TOnBlurAsync, + TOnSubmit, + TOnSubmitAsync, + TOnDynamic, + TOnDynamicAsync, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnDynamic, + TFormOnDynamicAsync, + TFormOnServer, + TParentSubmitMeta, + ExtendedApi + >, + 'form' + > + >, + ) => SvelteComponent) & + WithoutFunction type FieldComponentProps< TParentData, @@ -253,20 +253,20 @@ type FieldComponentProps< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TFormOnMount extends undefined | FormValidateOrFn, TFormOnChange extends undefined | FormValidateOrFn, TFormOnChangeAsync extends undefined | FormAsyncValidateOrFn, @@ -306,7 +306,8 @@ type FieldComponentProps< TFormOnDynamicAsync, TFormOnServer, TParentSubmitMeta - > & ExtendedApi, + > & + ExtendedApi, ] > } & Omit< @@ -357,20 +358,20 @@ export type CreateField< TOnMount extends undefined | FieldValidateOrFn, TOnChange extends undefined | FieldValidateOrFn, TOnChangeAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnBlur extends undefined | FieldValidateOrFn, TOnBlurAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnSubmit extends undefined | FieldValidateOrFn, TOnSubmitAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TOnDynamic extends undefined | FieldValidateOrFn, TOnDynamicAsync extends - | undefined - | FieldAsyncValidateOrFn, + | undefined + | FieldAsyncValidateOrFn, TSubmitMeta, >( opts: () => { name: Narrow } & Omit< @@ -426,17 +427,17 @@ export type CreateField< TFormOnServer, TParentSubmitMeta > & - SvelteFieldApi< - TParentData, - TFormOnMount, - TFormOnChange, - TFormOnChangeAsync, - TFormOnBlur, - TFormOnBlurAsync, - TFormOnSubmit, - TFormOnSubmitAsync, - TFormOnDynamic, - TFormOnDynamicAsync, - TFormOnServer, - TParentSubmitMeta - > + SvelteFieldApi< + TParentData, + TFormOnMount, + TFormOnChange, + TFormOnChangeAsync, + TFormOnBlur, + TFormOnBlurAsync, + TFormOnSubmit, + TFormOnSubmitAsync, + TFormOnDynamic, + TFormOnDynamicAsync, + TFormOnServer, + TParentSubmitMeta + > diff --git a/packages/svelte-form/tests/large-components/context.ts b/packages/svelte-form/tests/large-components/context.ts index bb53d8f7f..3e20b01d6 100644 --- a/packages/svelte-form/tests/large-components/context.ts +++ b/packages/svelte-form/tests/large-components/context.ts @@ -1,3 +1,3 @@ -import { createFormRuneContexts } from "../../src/index.js"; +import { createFormRuneContexts } from '../../src/index.js' -export const { useFieldContext } = createFormRuneContexts() \ No newline at end of file +export const { useFieldContext } = createFormRuneContexts() diff --git a/packages/svelte-form/tests/large-components/rune.ts b/packages/svelte-form/tests/large-components/rune.ts index 3654a55b1..727526ae8 100644 --- a/packages/svelte-form/tests/large-components/rune.ts +++ b/packages/svelte-form/tests/large-components/rune.ts @@ -1,9 +1,9 @@ -import { createFormRune } from "../../src/createFormRune.svelte.js"; -import TextField from "./text-field.svelte"; +import { createFormRune } from '../../src/createFormRune.svelte.js' +import TextField from './text-field.svelte' export const { createAppForm } = createFormRune({ fieldComponents: { TextField, }, formComponents: {}, -}) \ No newline at end of file +}) diff --git a/packages/svelte-form/tests/large.test.ts b/packages/svelte-form/tests/large.test.ts index aee46d855..9ccdc0a0c 100644 --- a/packages/svelte-form/tests/large.test.ts +++ b/packages/svelte-form/tests/large.test.ts @@ -31,6 +31,8 @@ describe('Svelte Tests', () => { await userEvent.type(firstName, firstNameValue) const form = JSON.parse(element.querySelector('pre')!.textContent!) - expect(form.values.firstName).toBe(getSampleData().firstName + firstNameValue) + expect(form.values.firstName).toBe( + getSampleData().firstName + firstNameValue, + ) }) }) From 7a5873dc9556d086af16c75cc41c6eccee29e012 Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:54:46 -0700 Subject: [PATCH 08/10] chore: fix linting --- packages/svelte-form/src/createFormRune.svelte.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index 13d77d6f6..bc8d27d6b 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -1,4 +1,8 @@ +import { getContext } from 'svelte' import { createForm } from './createForm.svelte' +import AppFormSvelte from './AppForm.svelte' +import AppFieldSvelte from './AppField.svelte' +import { fieldContextKey, formContextKey } from './context-keys.js' import type { AnyFieldApi, AnyFormApi, @@ -9,10 +13,7 @@ import type { } from '@tanstack/form-core' import type { FieldComponent } from './types.js' import type { SvelteFormExtendedApi } from './createForm.svelte' -import { Component, getContext, Snippet, SvelteComponent } from 'svelte' -import AppFormSvelte from './AppForm.svelte' -import AppFieldSvelte from './AppField.svelte' -import { fieldContextKey, formContextKey } from './context-keys.js' +import type { Component, Snippet, SvelteComponent } from 'svelte'; /** * TypeScript inferencing is weird. From 2e98a3cebefbb6d66aa47b6318b7f64383ebe61a Mon Sep 17 00:00:00 2001 From: Corbin Crutchley Date: Sat, 23 Aug 2025 03:57:15 -0700 Subject: [PATCH 09/10] chore: fix knip --- .../svelte-form/src/createFormRune.svelte.ts | 59 +------------------ 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index bc8d27d6b..e0f14ab8e 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -131,7 +131,7 @@ interface CreateFormRuneProps< /** * @private */ -export type AppFieldExtendedReactFormApi< +type AppFieldExtendedSvelteFormApi< TFormData, TOnMount extends undefined | FormValidateOrFn, TOnChange extends undefined | FormValidateOrFn, @@ -179,61 +179,6 @@ export type AppFieldExtendedReactFormApi< AppForm: Component<{ children: Snippet }> } -export interface WithFormProps< - TFormData, - TOnMount extends undefined | FormValidateOrFn, - TOnChange extends undefined | FormValidateOrFn, - TOnChangeAsync extends undefined | FormAsyncValidateOrFn, - TOnBlur extends undefined | FormValidateOrFn, - TOnBlurAsync extends undefined | FormAsyncValidateOrFn, - TOnSubmit extends undefined | FormValidateOrFn, - TOnSubmitAsync extends undefined | FormAsyncValidateOrFn, - TOnDynamic extends undefined | FormValidateOrFn, - TOnDynamicAsync extends undefined | FormAsyncValidateOrFn, - TOnServer extends undefined | FormAsyncValidateOrFn, - TSubmitMeta, - TFieldComponents extends Record>, - TFormComponents extends Record>, - TRenderProps extends object = Record, -> extends FormOptions< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta - > { - // Optional, but adds props to the `render` function outside of `form` - props?: TRenderProps - render: ( - props: NoInfer & { - form: AppFieldExtendedReactFormApi< - TFormData, - TOnMount, - TOnChange, - TOnChangeAsync, - TOnBlur, - TOnBlurAsync, - TOnSubmit, - TOnSubmitAsync, - TOnDynamic, - TOnDynamicAsync, - TOnServer, - TSubmitMeta, - TFieldComponents, - TFormComponents - > - children: Snippet - }, - ) => SvelteComponent -} - export function createFormRune< const TComponents extends Record>, const TFormComponents extends Record>, @@ -269,7 +214,7 @@ export function createFormRune< TOnServer, TSubmitMeta >, - ): AppFieldExtendedReactFormApi< + ): AppFieldExtendedSvelteFormApi< TFormData, TOnMount, TOnChange, From 231ae85269e635deb5769587a97c3c142ac9c2f1 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 10:58:07 +0000 Subject: [PATCH 10/10] ci: apply automated fixes and generate docs --- packages/svelte-form/src/createFormRune.svelte.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte-form/src/createFormRune.svelte.ts b/packages/svelte-form/src/createFormRune.svelte.ts index e0f14ab8e..2946584db 100644 --- a/packages/svelte-form/src/createFormRune.svelte.ts +++ b/packages/svelte-form/src/createFormRune.svelte.ts @@ -13,7 +13,7 @@ import type { } from '@tanstack/form-core' import type { FieldComponent } from './types.js' import type { SvelteFormExtendedApi } from './createForm.svelte' -import type { Component, Snippet, SvelteComponent } from 'svelte'; +import type { Component, Snippet, SvelteComponent } from 'svelte' /** * TypeScript inferencing is weird.