Skip to content

Commit 67152e7

Browse files
committed
[sveltekit] Refactor sveltekit form types
1 parent 4c6322c commit 67152e7

File tree

4 files changed

+168
-180
lines changed

4 files changed

+168
-180
lines changed

packages/sveltekit/.prettierrc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{
2-
"useTabs": true,
32
"singleQuote": true,
43
"trailingComma": "none",
54
"printWidth": 100,

packages/sveltekit/src/lib/client/use-form.svelte.ts

Lines changed: 140 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { onDestroy } from 'svelte';
33
import type { AnyKey } from '@sjsf/form/lib/types';
44
import { isRecord } from '@sjsf/form/lib/object';
55
import {
6-
DEFAULT_ID_SEPARATOR,
7-
DEFAULT_PSEUDO_ID_SEPARATOR,
8-
groupErrors,
9-
useForm2,
10-
type AdditionalPropertyKeyError,
11-
type AdditionalPropertyKeyValidator,
12-
type Schema,
13-
type SchemaValue,
14-
type UseFormOptions2
6+
DEFAULT_ID_SEPARATOR,
7+
DEFAULT_PSEUDO_ID_SEPARATOR,
8+
groupErrors,
9+
useForm2,
10+
type AdditionalPropertyKeyError,
11+
type AdditionalPropertyKeyValidator,
12+
type Schema,
13+
type SchemaValue,
14+
type UseFormOptions2
1515
} from '@sjsf/form';
1616

1717
import { page } from '$app/stores';
@@ -20,179 +20,160 @@ import type { InitialFormData, ValidatedFormData } from '../model';
2020

2121
import { useSvelteKitMutation, type SveltekitMutationOptions } from './use-mutation.svelte';
2222

23-
export type ValidatedFormDataFromActionDataBranch<ActionData, FormName extends AnyKey> =
24-
ActionData[keyof ActionData & FormName] extends ValidatedFormData<any, any>
25-
? ActionData[keyof ActionData & FormName]
26-
: never;
23+
export type ValidatedFormDataFromActionDataBranch<ActionData, FormName extends keyof ActionData> =
24+
ActionData[FormName] extends ValidatedFormData<any, any> ? ActionData[FormName] : never;
2725

2826
export type ValidatedFormDataFromActionDataUnion<
29-
ActionData,
30-
FormName extends AnyKey
27+
ActionData,
28+
FormName extends keyof ActionData
3129
> = ActionData extends any ? ValidatedFormDataFromActionDataBranch<ActionData, FormName> : never;
3230

3331
export type FormNameFromActionDataBranch<ActionData> = keyof {
34-
[K in keyof ActionData]: ActionData[K] extends ValidatedFormData<any, any> ? K : never;
32+
[K in keyof ActionData]: ActionData[K] extends ValidatedFormData<any, any> ? K : never;
3533
};
3634

3735
export type FormNameFromActionDataUnion<ActionData> = ActionData extends any
38-
? FormNameFromActionDataBranch<ActionData>
39-
: never;
36+
? FormNameFromActionDataBranch<ActionData>
37+
: never;
4038

4139
export type ValidatorErrorFromValidatedFormData<VFD> =
42-
VFD extends ValidatedFormData<infer E, any> ? E : never;
40+
VFD extends ValidatedFormData<infer E, any> ? E : never;
4341

4442
export type InitialFromDataFromPageData<PageData, FormName extends AnyKey> =
45-
PageData[keyof PageData & FormName] extends InitialFormData<any, any, any>
46-
? PageData[keyof PageData & FormName]
47-
: never;
43+
PageData[keyof PageData & FormName] extends InitialFormData<any, any, any>
44+
? PageData[keyof PageData & FormName]
45+
: InitialFormData<never, never, false>;
4846

4947
export type FormValueFromInitialFormData<IFD, E, FallbackValue> =
50-
IFD extends InitialFormData<infer T, E, any>
51-
? unknown extends T
52-
? FallbackValue
53-
: T
54-
: FallbackValue;
48+
IFD extends InitialFormData<infer T, E, any>
49+
? unknown extends T
50+
? FallbackValue
51+
: T
52+
: FallbackValue;
5553

5654
export type SendDataFromValidatedFormData<VFD, E> =
57-
VFD extends ValidatedFormData<E, infer SendData> ? SendData : false;
55+
VFD extends ValidatedFormData<E, infer SendData> ? SendData : false;
5856

5957
export type SendSchemaFromInitialFormData<IFD, V, E> =
60-
IFD extends InitialFormData<V, E, infer SendSchema> ? SendSchema : false;
61-
62-
type SvelteKitForm<
63-
ActionData,
64-
PageData,
65-
N extends FormNameFromActionDataUnion<ActionData>,
66-
FallbackValue = SchemaValue
67-
> = {
68-
name: N;
69-
__actionData: ActionData;
70-
__pageData: PageData;
71-
__fallbackValue: FallbackValue;
58+
IFD extends InitialFormData<V, E, infer SendSchema> ? SendSchema : false;
59+
60+
type SvelteKitFormMeta<ActionData, PageData, FallbackValue = SchemaValue> = {
61+
__actionData: ActionData;
62+
__pageData: PageData;
63+
__fallbackValue: FallbackValue;
7264
};
7365

74-
type NameFromSvelteKitForm<F extends SvelteKitForm<any, any, any>> =
75-
F extends SvelteKitForm<any, any, infer Name> ? Name : never;
76-
77-
export function svelteKitForm<
78-
ActionData extends Record<AnyKey, any> | null,
79-
PageData,
80-
N extends FormNameFromActionDataUnion<ActionData> = FormNameFromActionDataUnion<ActionData>,
81-
FallbackValue = SchemaValue
82-
>(
83-
name: FormNameFromActionDataUnion<ActionData>
84-
): SvelteKitForm<ActionData, PageData, N, FallbackValue> {
85-
return { name } as SvelteKitForm<ActionData, PageData, N, FallbackValue>;
66+
export function meta<ActionData, PageData, FallbackValue = SchemaValue>(): SvelteKitFormMeta<
67+
ActionData,
68+
PageData,
69+
FallbackValue
70+
> {
71+
return null as unknown as SvelteKitFormMeta<ActionData, PageData, FallbackValue>;
8672
}
8773

8874
export type AdditionalPropertyKeyValidationError =
89-
| string
90-
| ((ctx: { key: string; separator: string; separators: string[] }) => string);
91-
92-
export type UseSvelteKitFormOptions<
93-
F extends SvelteKitForm<any, any, any>,
94-
V,
95-
E,
96-
SendSchema extends boolean,
97-
AD = F['__actionData']
98-
> = Omit<UseFormOptions2<V, E>, 'onSubmit' | (SendSchema extends true ? 'schema' : never)> &
99-
SveltekitMutationOptions<AD, V> & {
100-
// Form options
101-
spec: F;
102-
/** @default false */
103-
forceDataInvalidation?: boolean;
104-
/** @default true */
105-
resetOnUpdate?: boolean;
106-
additionalPropertyKeyValidationError?: AdditionalPropertyKeyValidationError;
107-
} & (SendSchema extends true
108-
? {
109-
schema?: Schema;
110-
}
111-
: { schema: Schema });
75+
| string
76+
| ((ctx: { key: string; separator: string; separators: string[] }) => string);
77+
78+
export type SvelteKitFormOptions<ActionData, V, E, SendSchema extends boolean> = Omit<
79+
UseFormOptions2<V, E>,
80+
'onSubmit' | (SendSchema extends true ? 'schema' : never)
81+
> &
82+
SveltekitMutationOptions<ActionData, V> & {
83+
additionalPropertyKeyValidationError?: AdditionalPropertyKeyValidationError;
84+
} & (SendSchema extends true
85+
? {
86+
schema?: Schema;
87+
}
88+
: { schema: Schema });
11289

11390
export function useSvelteKitForm<
114-
const O extends UseSvelteKitFormOptions<any, any, any, any, any>,
115-
// Local
116-
SKF extends SvelteKitForm<any, any, any> = O['spec'],
117-
ActionData = SKF['__actionData'],
118-
PageData = SKF['__pageData'],
119-
FallbackValue = SKF['__fallbackValue'],
120-
N extends AnyKey = NameFromSvelteKitForm<SKF>,
121-
VFD = ValidatedFormDataFromActionDataUnion<ActionData, N>,
122-
IFD = InitialFromDataFromPageData<PageData, N>,
123-
E = O extends
124-
| { additionalPropertyKeyValidationError: AdditionalPropertyKeyValidationError }
125-
| { additionalPropertyKeyValidator: AdditionalPropertyKeyValidator }
126-
? ValidatorErrorFromValidatedFormData<VFD> | AdditionalPropertyKeyError
127-
: ValidatorErrorFromValidatedFormData<VFD>,
128-
V = FormValueFromInitialFormData<IFD, E, FallbackValue>,
129-
SendSchema extends boolean = SendSchemaFromInitialFormData<IFD, V, E>,
130-
SendData extends boolean = SendDataFromValidatedFormData<VFD, E>
131-
>(options: O) {
132-
let lastInitialFormData: InitialFormData<V, E, SendSchema> | undefined;
133-
let initialized = false;
134-
const spec: SKF = options.spec;
135-
const unsubscribe = page.subscribe((page) => {
136-
if (isRecord(page.form)) {
137-
const validationData = page.form[spec.name] as ValidatedFormData<E, SendData> | undefined;
138-
if (validationData !== undefined) {
139-
if (initialized) {
140-
if (validationData.sendData) {
141-
form.formValue = validationData.data;
142-
}
143-
form.errors = groupErrors(validationData.errors);
144-
} else {
145-
initialized = true;
146-
lastInitialFormData = {
147-
schema: options.schema ?? page.data[spec.name as string].schema,
148-
initialValue: validationData.data as V,
149-
initialErrors: validationData.errors
150-
};
151-
}
152-
return;
153-
}
154-
}
155-
if (!initialized) {
156-
initialized = true;
157-
lastInitialFormData = page.data[spec.name as string];
158-
return;
159-
}
160-
});
161-
onDestroy(unsubscribe);
162-
163-
const mutation = useSvelteKitMutation<ActionData, V>(options);
164-
165-
const separators = [
166-
options.idSeparator ?? DEFAULT_ID_SEPARATOR,
167-
options.pseudoIdSeparator ?? DEFAULT_PSEUDO_ID_SEPARATOR
168-
];
169-
const additionalPropertyKeyValidationError = $derived(
170-
options.additionalPropertyKeyValidationError
171-
);
172-
const form = useForm2<UseFormOptions2<V, E>>(
173-
Object.setPrototypeOf(options, {
174-
...lastInitialFormData,
175-
onSubmit: mutation.run,
176-
additionalPropertyKeyValidator: additionalPropertyKeyValidationError && {
177-
validateAdditionalPropertyKey(key: string): string[] {
178-
for (const separator of separators) {
179-
if (key.includes(separator)) {
180-
return [
181-
typeof additionalPropertyKeyValidationError === 'string'
182-
? additionalPropertyKeyValidationError
183-
: additionalPropertyKeyValidationError({ key, separator, separators })
184-
];
185-
}
186-
}
187-
return [];
188-
}
189-
}
190-
})
191-
);
192-
193-
return {
194-
form,
195-
mutation,
196-
enhance: form.enhance.bind(form)
197-
};
91+
Meta extends SvelteKitFormMeta<any, any>,
92+
N extends FormNameFromActionDataUnion<Meta['__actionData']>,
93+
Options extends SvelteKitFormOptions<
94+
Meta['__actionData'],
95+
V,
96+
E,
97+
SendDataFromValidatedFormData<VFD, E>
98+
>,
99+
VFD = ValidatedFormDataFromActionDataUnion<Meta['__actionData'], N>,
100+
IFD = InitialFromDataFromPageData<Meta['__pageData'], N>,
101+
VE = ValidatorErrorFromValidatedFormData<VFD>,
102+
V = FormValueFromInitialFormData<IFD, VE, Meta['__fallbackValue']>,
103+
E = Options extends
104+
| { additionalPropertyKeyValidationError: AdditionalPropertyKeyValidationError }
105+
| { additionalPropertyKeyValidator: AdditionalPropertyKeyValidator }
106+
? VE | AdditionalPropertyKeyError
107+
: VE,
108+
SendSchema extends boolean = SendSchemaFromInitialFormData<IFD, V, E>,
109+
SendData extends boolean = SendDataFromValidatedFormData<VFD, E>
110+
>(__do_not_use: Meta, name: N, options: Options) {
111+
let lastInitialFormData: InitialFormData<V, E, SendSchema> | undefined;
112+
let initialized = false;
113+
const unsubscribe = page.subscribe((page) => {
114+
if (isRecord(page.form)) {
115+
const validationData = page.form[name] as ValidatedFormData<E, SendData> | undefined;
116+
if (validationData !== undefined) {
117+
if (initialized) {
118+
if (validationData.sendData) {
119+
form.formValue = validationData.data;
120+
}
121+
form.errors = groupErrors(validationData.errors);
122+
} else {
123+
initialized = true;
124+
lastInitialFormData = {
125+
schema: options.schema ?? page.data[name as string].schema,
126+
initialValue: validationData.data as V,
127+
initialErrors: validationData.errors
128+
};
129+
}
130+
return;
131+
}
132+
}
133+
if (!initialized) {
134+
initialized = true;
135+
lastInitialFormData = page.data[name as string];
136+
return;
137+
}
138+
});
139+
onDestroy(unsubscribe);
140+
141+
const mutation = useSvelteKitMutation<Meta['__actionData'], V>(options);
142+
143+
const separators = [
144+
//@ts-expect-error TODO: This is related to passing `SendDataFromValidatedFormData<VFD, E>`
145+
// to `SvelteKitFormOptions`.
146+
options.idSeparator ?? DEFAULT_ID_SEPARATOR,
147+
//@ts-expect-error also
148+
options.pseudoIdSeparator ?? DEFAULT_PSEUDO_ID_SEPARATOR
149+
];
150+
const additionalPropertyKeyValidationError = $derived(
151+
options.additionalPropertyKeyValidationError
152+
);
153+
const form = useForm2<UseFormOptions2<V, E>>(
154+
Object.setPrototypeOf(options, {
155+
...lastInitialFormData,
156+
onSubmit: mutation.run,
157+
additionalPropertyKeyValidator: additionalPropertyKeyValidationError && {
158+
validateAdditionalPropertyKey(key: string): string[] {
159+
for (const separator of separators) {
160+
if (key.includes(separator)) {
161+
return [
162+
typeof additionalPropertyKeyValidationError === 'string'
163+
? additionalPropertyKeyValidationError
164+
: additionalPropertyKeyValidationError({ key, separator, separators })
165+
];
166+
}
167+
}
168+
return [];
169+
}
170+
}
171+
})
172+
);
173+
174+
return {
175+
form,
176+
mutation,
177+
enhance: form.enhance.bind(form)
178+
};
198179
}

packages/sveltekit/src/routes/+page.server.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,14 @@ export const actions = {
4949
sendData: true
5050
})
5151
};
52+
},
53+
register: async () => {
54+
return {
55+
form2: validateForm({
56+
schema,
57+
validator,
58+
data: { field: "123" }
59+
})
60+
}
5261
}
5362
} satisfies Actions;

0 commit comments

Comments
 (0)