Skip to content

Commit b48872a

Browse files
committed
Validation tests passing.
1 parent 9c343f2 commit b48872a

File tree

5 files changed

+72
-51
lines changed

5 files changed

+72
-51
lines changed

src/lib/client/index.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -892,27 +892,28 @@ async function validateField<T extends AnyZodObject, M>(
892892
}
893893

894894
let value = options.value;
895+
let shouldUpdate = true;
895896
const currentData = get(data);
896897

897898
if (!('value' in options)) {
899+
// Use value from data
898900
const dataToValidate = traversePath(
899901
currentData,
900902
path as FieldPath<typeof currentData>
901903
);
902904

903-
if (!dataToValidate) {
904-
throw new SuperFormError('Validation data not found: ' + path);
905-
}
906-
907-
value = dataToValidate.value;
905+
value = dataToValidate?.value;
908906
} else if (options.update === true || options.update === 'value') {
907+
// Value should be updating the data
909908
data.update(
910909
($data) => {
911910
setPaths($data, [path], value);
912911
return $data;
913912
},
914913
{ taint: options.taint }
915914
);
915+
} else {
916+
shouldUpdate = false;
916917
}
917918

918919
//console.log('🚀 ~ file: index.ts:871 ~ validate:', path, value);
@@ -951,7 +952,7 @@ async function validateField<T extends AnyZodObject, M>(
951952
if ('safeParseAsync' in validators) {
952953
// Zod validator
953954
// Check if any effects exist for the path, then parse the entire schema.
954-
const leaf = traversePath(
955+
const noEffects = traversePath(
955956
validators,
956957
validationPath as FieldPath<typeof validators>,
957958
(pathData) => {
@@ -962,10 +963,10 @@ async function validateField<T extends AnyZodObject, M>(
962963
}
963964
);
964965

965-
if (leaf) {
966+
if (noEffects) {
966967
const validator = extractValidator(
967-
unwrapZodType(leaf.parent),
968-
leaf.key
968+
unwrapZodType(noEffects.parent),
969+
noEffects.key
969970
);
970971
if (validator) {
971972
//console.log('🚀 ~ file: index.ts:972 ~ no effects:', validator);
@@ -982,14 +983,22 @@ async function validateField<T extends AnyZodObject, M>(
982983
//console.log('🚀 ~ file: index.ts:983 ~ Effects found, validating all');
983984

984985
// Effects are found, validate entire data, unfortunately
986+
let dataToValidate = currentData;
987+
988+
if (!shouldUpdate) {
989+
// If value shouldn't update, clone and set the new value
990+
dataToValidate = clone(currentData);
991+
setPaths(dataToValidate, [path], value);
992+
}
993+
985994
const result = await (validators as ZodTypeAny).safeParseAsync(
986-
get(data)
995+
dataToValidate
987996
);
988997

989998
if (!result.success) {
990999
const errors = result.error.format();
9911000
const current = traversePath(errors, path as FieldPath<typeof errors>);
992-
return setError(current?.value?._errors);
1001+
return setError(options.errors ?? current?.value?._errors);
9931002
} else {
9941003
return setError(undefined);
9951004
}

src/routes/+layout.svelte

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838
<br />
3939
<a href="/files">Files</a> |
4040
<a href="/spa">SPA</a> |
41-
<a href="/nested-validation">Client-side validation</a>
41+
<a href="/nested-validation">Client-side validation</a> |
42+
<a href="/tests/rex">Side-effect validation</a>
4243
</nav>
4344

4445
<slot />

src/routes/tests/rex/+page.server.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
import { fail } from '@sveltejs/kit';
22
import type { Actions, PageServerLoad } from './$types';
3-
import { basicSchema, subRefined, refined } from './schema';
3+
import { basicSchema, refined } from './schema';
44
import { message, superValidate } from '$lib/server';
55

6-
export const load = (async () => {
6+
export const load = (async ({ url }) => {
77
// Server API:
8-
const form = await superValidate(subRefined);
8+
const form = await superValidate(
9+
url.searchParams.has('refined') ? refined : basicSchema
10+
);
911

1012
// Always return { form } in load and form actions.
1113
return { form };
1214
}) satisfies PageServerLoad;
1315

1416
export const actions: Actions = {
15-
default: async ({ request }) => {
16-
const form = await superValidate(request, subRefined);
17+
default: async ({ request, url }) => {
18+
const form = await superValidate(
19+
request,
20+
url.searchParams.has('refined') ? refined : basicSchema
21+
);
1722
console.log('POST', form);
1823

1924
// Convenient validation check:

src/routes/tests/rex/+page.svelte

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
11
<script lang="ts">
22
import type { PageData } from './$types';
33
import { superForm } from '$lib/client';
4-
import { basicSchema, subRefined, refined } from './schema';
4+
import { basicSchema, refined } from './schema';
55
import { page } from '$app/stores';
66
77
export let data: PageData;
88
9+
const validators = $page.url.searchParams.has('refined')
10+
? refined
11+
: basicSchema;
12+
913
// Client API:
1014
const { form, errors, constraints, message, enhance } = superForm(
1115
data.form,
1216
{
1317
dataType: 'json',
14-
validators: subRefined,
18+
validators: validators,
1519
validationMethod: ($page.url.searchParams.get('method') ??
1620
undefined) as any
1721
}
1822
);
1923
</script>
2024

2125
<h4>Validation method: {$page.url.searchParams.get('method') ?? 'auto'}</h4>
26+
<h4>
27+
Schema: {$page.url.searchParams.has('refined') ? 'refined' : 'simple'}
28+
</h4>
2229

2330
<form method="POST" use:enhance>
2431
{#if $message}
@@ -45,7 +52,7 @@
4552
<!-- {...$constraints.email} -->
4653
{#if $errors.email}<span class="invalid">{$errors.email}</span>{/if}
4754

48-
<div>
55+
<div class="tags">
4956
{#each $form.tags as _, i}
5057
<div>
5158
<label for="min">Min</label>

src/routes/tests/rex/schema.ts

Lines changed: 30 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,40 +15,39 @@ export const basicSchema = z.object({
1515
.default([{ min: 5, max: 10 }])
1616
});
1717

18-
export const subRefined = z.object({
19-
name: z.string().min(4).default('Hello world!'),
20-
email: z.string().email(),
21-
items: z.optional(
22-
z.array(z.object({ title: z.string(), name: z.string().min(3) }))
23-
),
24-
tags: z
25-
.object({
26-
min: z.number().int().min(5),
27-
max: z.number().int().min(5)
28-
})
29-
.refine(
30-
(data) => {
31-
if (data.min && data.max) {
32-
return data.min <= data.max;
18+
export const refined = z
19+
.object({
20+
name: z.string().min(4).default('Hello world!'),
21+
email: z.string().email(),
22+
items: z.optional(
23+
z.array(z.object({ title: z.string(), name: z.string().min(3) }))
24+
),
25+
tags: z
26+
.object({
27+
min: z.number().int().min(5),
28+
max: z.number().int().min(5)
29+
})
30+
.refine(
31+
(data) => {
32+
if (data.min && data.max) {
33+
return data.min <= data.max;
34+
}
35+
return true;
36+
},
37+
{
38+
path: ['max'],
39+
message: 'Max must be greater or equal to min'
3340
}
34-
return true;
35-
},
36-
{
37-
path: ['max'],
38-
message: 'Max must be greater or equal to min'
39-
}
40-
)
41-
.array()
42-
.default([{ min: 5, max: 10 }])
43-
});
44-
45-
export const refined = basicSchema
46-
.refine(({ name }) => name === 'bob', {
47-
message: 'Oof, name is non bob',
41+
)
42+
.array()
43+
.default([{ min: 5, max: 10 }])
44+
})
45+
.refine(({ name }) => name !== 'nope', {
46+
message: 'Nope',
4847
path: ['name']
4948
})
50-
.refine(({ name }) => name === 'john', {
51-
message: 'Oof, name is not john',
49+
.refine(({ name }) => name !== 'nope', {
50+
message: 'Nope',
5251
path: ['name']
5352
})
5453
.superRefine((data, ctx) => {

0 commit comments

Comments
 (0)