Skip to content

Commit b2e1946

Browse files
committed
Removed accidental optional argument from errors.clear
1 parent 2fe4ac1 commit b2e1946

File tree

5 files changed

+173
-15
lines changed

5 files changed

+173
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
- `onUpdate` didn't cancel `applyAction` and `invalidateAll`.
1313
- Hopefully fixed excessively deep infinity type instantiation on `message` helper.
14+
- Removed `errors.clear` optional parameter `undefinePath`, which was left there by mistake.
1415

1516
### Added
1617

src/lib/client/index.ts

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export type SuperForm<T extends ZodValidation<AnyZodObject>, M = any> = {
251251
};
252252
formId: Writable<string | undefined>;
253253
errors: Writable<Validation<T, M>['errors']> & {
254-
clear: (undefinePath?: string[]) => void;
254+
clear: () => void;
255255
};
256256
constraints: Writable<Validation<T, M>['constraints']>;
257257
message: Writable<Validation<T, M>['message']>;
@@ -578,17 +578,11 @@ export function superForm<
578578
* To work with client-side validation, errors cannot be deleted but must
579579
* be set to undefined, to know where they existed before (tainted+error check in oninput)
580580
*/
581-
clear: (undefinePath?: string[]) => {
582-
_errors.update(($errors) => {
583-
traversePaths($errors, (pathData) => {
584-
if (Array.isArray(pathData.value)) {
585-
return pathData.set(undefined);
586-
}
587-
});
588-
if (undefinePath) setPaths($errors, [undefinePath], undefined);
589-
return $errors;
590-
});
591-
}
581+
clear: () =>
582+
clearErrors(_errors, {
583+
undefinePath: null,
584+
clearFormLevelErrors: true
585+
})
592586
};
593587

594588
const Tainted = writable<TaintedFields<T2> | undefined>();
@@ -948,6 +942,34 @@ function shouldSyncFlash<T extends AnyZodObject, M>(
948942
return options.syncFlashMessage;
949943
}
950944

945+
function clearErrors<T extends AnyZodObject>(
946+
Errors: Writable<ValidationErrors<T>>,
947+
options: {
948+
undefinePath: string[] | null;
949+
clearFormLevelErrors: boolean;
950+
}
951+
) {
952+
Errors.update(($errors) => {
953+
traversePaths($errors, (pathData) => {
954+
if (
955+
pathData.path.length == 1 &&
956+
pathData.path[0] == '_errors' &&
957+
!options.clearFormLevelErrors
958+
) {
959+
return;
960+
}
961+
if (Array.isArray(pathData.value)) {
962+
return pathData.set(undefined);
963+
}
964+
});
965+
966+
if (options.undefinePath)
967+
setPaths($errors, [options.undefinePath], undefined);
968+
969+
return $errors;
970+
});
971+
}
972+
951973
const effectMapCache = new WeakMap<object, boolean>();
952974

953975
// @DCI-context
@@ -1022,8 +1044,10 @@ async function validateField<T extends AnyZodObject, M>(
10221044
return get(Errors);
10231045
}
10241046

1025-
function Errors_clear(undefinePath: string[]) {
1026-
Errors.clear(undefinePath);
1047+
function Errors_clear(
1048+
options: NonNullable<Parameters<typeof clearErrors>[1]>
1049+
) {
1050+
return clearErrors(Errors, options);
10271051
}
10281052

10291053
function Errors_set(newErrors: ValidationErrors<UnwrapEffects<T>>) {
@@ -1195,7 +1219,7 @@ async function validateField<T extends AnyZodObject, M>(
11951219
// We validated the whole data structure, so clear all errors on success
11961220
// but also set the current path to undefined, so it will be used in the tainted+error
11971221
// check in oninput.
1198-
Errors_clear(path);
1222+
Errors_clear({ undefinePath: path, clearFormLevelErrors: false });
11991223
return undefined;
12001224
}
12011225
} else {
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { setError, superValidate } from '$lib/server';
2+
import type { Validation } from '$lib/index';
3+
import { fail } from '@sveltejs/kit';
4+
import { schema } from './schema';
5+
6+
export const load = async () => {
7+
const form = await superValidate(schema);
8+
return { form };
9+
};
10+
11+
///// Form actions //////////////////////////////////////////////////
12+
13+
function stripPassword(form: Validation<typeof schema>) {
14+
// comment out password clearing and form error works again
15+
form.data.password = '';
16+
form.data.confirmedPassword = '';
17+
return form;
18+
}
19+
20+
export const actions = {
21+
default: async ({ request }) => {
22+
const formData = await request.formData();
23+
const form = await superValidate(formData, schema);
24+
console.log('🚀 ~ file: +page.server.ts:24 ~ default: ~ form:', form);
25+
26+
if (!form.valid) return fail(400, { form: stripPassword(form) });
27+
28+
if (form.data.name === 'form') {
29+
return setError(
30+
stripPassword(form),
31+
null,
32+
'This is a sticky form error and it stays until the form gets submitted again'
33+
);
34+
}
35+
36+
return { form: stripPassword(form) };
37+
}
38+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<script lang="ts">
2+
import { superForm } from '$lib/client';
3+
import SuperDebug from '$lib/client/SuperDebug.svelte';
4+
import { schema } from './schema';
5+
import type { PageData } from './$types';
6+
7+
export let data: PageData;
8+
9+
const { form, errors, tainted, enhance } = superForm(data.form, {
10+
validators: schema
11+
});
12+
</script>
13+
14+
<h3>Superforms bug reporting</h3>
15+
16+
<SuperDebug data={{ $form, $errors, $tainted }} />
17+
18+
<form method="POST" use:enhance>
19+
<label>
20+
Name (type 'form' to trigger form error)<br />
21+
<input name="name" data-invalid={$errors.name} bind:value={$form.name} />
22+
{#if $errors.name}<span class="invalid">{$errors.name}</span>{/if}
23+
</label>
24+
<label>
25+
Password<br />
26+
<input
27+
name="password"
28+
data-invalid={$errors.password}
29+
bind:value={$form.password}
30+
/>
31+
{#if $errors.password}<span class="invalid">{$errors.password}</span
32+
>{/if}
33+
</label>
34+
<label>
35+
Confirm password<br />
36+
<input
37+
name="confirmedPassword"
38+
data-invalid={$errors.confirmedPassword}
39+
bind:value={$form.confirmedPassword}
40+
/>
41+
{#if $errors.confirmedPassword}<span class="invalid"
42+
>{$errors.confirmedPassword}</span
43+
>{/if}
44+
</label>
45+
<div>
46+
Form error here:
47+
{#if $errors._errors}<span class="invalid">{$errors._errors}</span>{/if}
48+
</div>
49+
<button>Submit</button>
50+
</form>
51+
52+
<hr />
53+
<p>
54+
To make the bug hunting as easy as possible, fork this project and create a
55+
MRE, then link to it on the <a
56+
href="https://github.com/ciscoheat/sveltekit-superforms/issues"
57+
>Superforms issue page</a
58+
>.
59+
</p>
60+
61+
<style>
62+
.invalid {
63+
color: red;
64+
}
65+
66+
input {
67+
background-color: #ddd;
68+
}
69+
70+
a {
71+
text-decoration: underline;
72+
}
73+
74+
hr {
75+
margin-top: 4rem;
76+
}
77+
</style>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { z } from 'zod';
2+
3+
export const schema = z
4+
.object({
5+
name: z.string().min(1).default('form'),
6+
password: z.string().min(8).default('123123123'),
7+
confirmedPassword: z.string().default('123123123')
8+
})
9+
// comment out superRefine and form error works again
10+
.superRefine(({ confirmedPassword, password }, ctx) => {
11+
if (confirmedPassword !== password) {
12+
ctx.addIssue({
13+
code: 'custom',
14+
message: 'Passwords must match',
15+
path: ['confirmedPassword']
16+
});
17+
}
18+
});

0 commit comments

Comments
 (0)