Skip to content

Commit 8127cc3

Browse files
committed
Adding side-effect errors only on tainted fields.
1 parent 8b59774 commit 8127cc3

File tree

3 files changed

+47
-10
lines changed

3 files changed

+47
-10
lines changed

src/compare.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ const refined = z.object({
468468
.optional()
469469
});
470470

471-
test.only('Checking side effects', () => {
471+
test('Checking side effects', () => {
472472
expect(hasEffects(social)).toStrictEqual(false);
473473
expect(hasEffects(refined)).toStrictEqual(true);
474474
});

src/lib/client/index.ts

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ import {
4848
comparePaths,
4949
setPaths,
5050
pathExists,
51-
type ZodTypeInfo
51+
type ZodTypeInfo,
52+
traversePaths
5253
} from '../entity.js';
5354
import { fieldProxy } from './proxies.js';
5455
import { clone } from '../utils.js';
@@ -642,9 +643,6 @@ export function superForm<
642643

643644
///// When use:enhance is enabled ///////////////////////////////////////////
644645

645-
// Prevent client validation on first page load
646-
// (when it recieives data from the server)
647-
648646
function isTainted(obj: unknown): boolean {
649647
if (obj === null)
650648
throw new SuperFormError('$tainted store contained null');
@@ -776,6 +774,7 @@ export function superForm<
776774
options.defaultValidator,
777775
Form,
778776
Errors,
777+
Tainted,
779778
opts
780779
);
781780
},
@@ -843,6 +842,16 @@ function shouldSyncFlash<T extends AnyZodObject, M>(
843842
return options.syncFlashMessage;
844843
}
845844

845+
function isPathTainted(
846+
path: string[],
847+
tainted: TaintedFields<AnyZodObject> | undefined
848+
) {
849+
if (tainted === undefined) return false;
850+
const leaf = traversePath(tainted, path as FieldPath<typeof tainted>);
851+
if (!leaf) return false;
852+
return leaf.value === true;
853+
}
854+
846855
const effectMapCache = new WeakMap<object, boolean>();
847856

848857
async function validateField<T extends AnyZodObject, M>(
@@ -851,6 +860,7 @@ async function validateField<T extends AnyZodObject, M>(
851860
defaultValidator: FormOptions<T, M>['defaultValidator'],
852861
data: SuperForm<T, M>['form'],
853862
errors: SuperForm<T, M>['errors'],
863+
tainted: SuperForm<T, M>['tainted'],
854864
options: ValidateOptions<unknown> = {}
855865
): Promise<string[] | undefined> {
856866
if (options.update === undefined) options.update = true;
@@ -1006,9 +1016,35 @@ async function validateField<T extends AnyZodObject, M>(
10061016
);
10071017

10081018
if (!result.success) {
1009-
const errors = result.error.format();
1010-
const current = traversePath(errors, path as FieldPath<typeof errors>);
1011-
return setError(options.errors ?? current?.value?._errors);
1019+
const newErrors = mapErrors(result.error.format());
1020+
1021+
if (options.update === true || options.update == 'errors') {
1022+
console.log('🚀 ~ file: index.ts:1020 ~ newErrors:', newErrors);
1023+
1024+
// Set errors for other (tainted) fields, that may have been changed
1025+
const taintedFields = get(tainted);
1026+
const currentErrors = get(errors);
1027+
let updated = false;
1028+
1029+
traversePaths(newErrors, (pathData) => {
1030+
if (!Array.isArray(pathData.value)) return;
1031+
if (isPathTainted(pathData.path, taintedFields)) {
1032+
setPaths(currentErrors, [pathData.path], pathData.value);
1033+
updated = true;
1034+
}
1035+
return 'skip';
1036+
});
1037+
1038+
if (updated) errors.set(currentErrors);
1039+
}
1040+
1041+
// Finally, set errors for the specific field
1042+
const current = traversePath(
1043+
newErrors,
1044+
path as FieldPath<typeof newErrors>
1045+
);
1046+
1047+
return setError(options.errors ?? current?.value);
10121048
} else {
10131049
return setError(undefined);
10141050
}
@@ -1073,7 +1109,8 @@ function formEnhance<T extends AnyZodObject, M>(
10731109
options.validators,
10741110
options.defaultValidator,
10751111
data,
1076-
errors
1112+
errors,
1113+
tainted
10771114
);
10781115
}
10791116

src/lib/entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ export function traversePaths<T extends object, Path extends FieldPath<T>>(
186186
const status = modifier(pathData);
187187

188188
if (status === 'abort') return status;
189-
else if (status === 'skip') break;
189+
else if (status === 'skip') continue;
190190
else if (!isLeaf) {
191191
const status = traversePaths(value, modifier, pathData.path as any);
192192
if (status === 'abort') return status;

0 commit comments

Comments
 (0)