Skip to content

Commit 8ced083

Browse files
authored
fix: serialization of signals containing null and undefined values (#2744)
1 parent 22af152 commit 8ced083

File tree

4 files changed

+80
-12
lines changed

4 files changed

+80
-12
lines changed

src/jsonify/custom_test.ts

+42-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ Deno.test("custom parse - Point", () => {
1212
}
1313

1414
const str = stringify(new Point(30, 40), {
15-
Point: (value) => value instanceof Point ? [value.x, value.y] : undefined,
15+
Point: (value) =>
16+
value instanceof Point ? { value: [value.x, value.y] } : undefined,
1617
});
1718

1819
expect(str).toEqual('[["Point",1],[2,3],30,40]');
@@ -35,18 +36,56 @@ Deno.test("custom stringify - Signals", () => {
3536
const s = signal(2);
3637
expect(stringify(s, {
3738
Signal: (s2: unknown) => {
38-
return s2 instanceof Signal ? s2.peek() : undefined;
39+
return s2 instanceof Signal ? { value: s2.peek() } : undefined;
3940
},
4041
})).toEqual(
4142
'[["Signal",1],2]',
4243
);
4344
});
4445

46+
Deno.test("custom parse - Signals with null value", () => {
47+
const res = parse<Signal>('[["Signal",-2]]', {
48+
Signal: (value) => signal(value),
49+
});
50+
expect(res).toBeInstanceOf(Signal);
51+
expect(res.peek()).toEqual(null);
52+
});
53+
54+
Deno.test("custom stringify - Signals with null value", () => {
55+
const s = signal(null);
56+
expect(stringify(s, {
57+
Signal: (s2: unknown) => {
58+
return s2 instanceof Signal ? { value: s2.peek() } : undefined;
59+
},
60+
})).toEqual(
61+
'[["Signal",-2]]',
62+
);
63+
});
64+
65+
Deno.test("custom parse - Signals with undefined value", () => {
66+
const res = parse<Signal>('[["Signal",-1]]', {
67+
Signal: (value) => signal(value),
68+
});
69+
expect(res).toBeInstanceOf(Signal);
70+
expect(res.peek()).toEqual(undefined);
71+
});
72+
73+
Deno.test("custom stringify - Signals with undefined value", () => {
74+
const s = signal(undefined);
75+
expect(stringify(s, {
76+
Signal: (s2: unknown) => {
77+
return s2 instanceof Signal ? { value: s2.peek() } : undefined;
78+
},
79+
})).toEqual(
80+
'[["Signal",-1]]',
81+
);
82+
});
83+
4584
Deno.test("custom stringify - referenced Signals", () => {
4685
const s = signal(2);
4786
expect(stringify([s, s], {
4887
Signal: (s2: unknown) => {
49-
return s2 instanceof Signal ? s2.peek() : undefined;
88+
return s2 instanceof Signal ? { value: s2.peek() } : undefined;
5089
},
5190
})).toEqual(
5291
'[[1,1],["Signal",2],2]',

src/jsonify/parse.ts

+26-2
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,32 @@ function unpack(
6868
if (custom !== undefined && name in custom) {
6969
const fn = custom[name];
7070
const ref = current[1];
71-
unpack(arr, hydrated, ref, custom);
72-
const value = hydrated[ref];
71+
let value;
72+
if (ref < 0) {
73+
switch (ref) {
74+
case UNDEFINED:
75+
value = undefined;
76+
break;
77+
case NULL:
78+
value = null;
79+
break;
80+
case NAN:
81+
value = NaN;
82+
break;
83+
case INFINITY_POS:
84+
value = Infinity;
85+
break;
86+
case INFINITY_NEG:
87+
value = -Infinity;
88+
break;
89+
case ZERO_NEG:
90+
value = -0;
91+
break;
92+
}
93+
} else {
94+
unpack(arr, hydrated, ref, custom);
95+
value = hydrated[ref];
96+
}
7397
hydrated[idx] = fn(value);
7498
return;
7599
}

src/jsonify/stringify.ts

+7-4
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import {
88
} from "./constants.ts";
99
import { HOLE } from "./constants.ts";
1010

11-
// deno-lint-ignore no-explicit-any
12-
export type Stringifiers = Record<string, (value: any) => any>;
11+
export type Stringifiers = Record<
12+
string,
13+
// deno-lint-ignore no-explicit-any
14+
(value: any) => { value: any } | undefined
15+
>;
1316

1417
/**
1518
* Serializes the following:
@@ -94,8 +97,8 @@ function serializeInner(
9497
const res = fn(value);
9598
if (res === undefined) continue;
9699

97-
serializeInner(out, indexes, res, custom);
98-
str = `["${k}",${idx + 1}]`;
100+
const innerIdx = serializeInner(out, indexes, res.value, custom);
101+
str = `["${k}",${innerIdx}]`;
99102
out[idx] = str;
100103
return idx;
101104
}

src/runtime/server/preact_hooks.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -369,14 +369,16 @@ function isVNode(x: any): x is VNode {
369369

370370
const stringifiers: Stringifiers = {
371371
Signal: (value: unknown) => {
372-
return isSignal(value) ? value.peek() : undefined;
372+
return isSignal(value) ? { value: value.peek() } : undefined;
373373
},
374374
Slot: (value: unknown) => {
375375
if (isVNode(value) && value.type === Slot) {
376376
const props = value.props as SlotProps;
377377
return {
378-
name: props.name,
379-
id: props.id,
378+
value: {
379+
name: props.name,
380+
id: props.id,
381+
},
380382
};
381383
}
382384
},

0 commit comments

Comments
 (0)