-
Notifications
You must be signed in to change notification settings - Fork 0
/
jsonStringify.ts
82 lines (70 loc) · 1.9 KB
/
jsonStringify.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
interface KeyValue {
key: string;
value: any;
}
type ComparatorFunction = (a: KeyValue, b: KeyValue) => number;
interface Option {
cmp?: ComparatorFunction;
cycles?: boolean;
}
export function jsonStringify(
data: any,
opts?: Option | ComparatorFunction,
): string | undefined {
let comparator: ComparatorFunction | undefined;
let allowCycle = false;
if (typeof opts === "function") {
comparator = opts;
} else {
allowCycle = opts?.cycles === true;
comparator = opts?.cmp;
}
const seen: Set<any> = new Set();
return (function stringify(node: any) {
if (node && node.toJSON && typeof node.toJSON === "function") {
node = node.toJSON();
}
if (node === undefined) return;
if (node === null) return "null";
if (typeof node === "number") return isFinite(node) ? "" + node : "null";
if (typeof node !== "object") return JSON.stringify(node);
let out = "";
if (Array.isArray(node)) {
out += "[";
for (let i = 0; i < node.length; i++) {
if (i) out += ",";
out += stringify(node[i]) || "null";
}
return out + "]";
}
if (seen.has(node)) {
if (allowCycle) return JSON.stringify("__cycle__");
throw new TypeError("Converting circular structure to JSON");
}
seen.add(node);
const sortedKeys = comparator
? Object.entries(node)
.sort((a, b) =>
(comparator as ComparatorFunction)(
{
key: a[0],
value: a[1],
},
{
key: b[0],
value: b[1],
},
)
)
.map((keyValue) => keyValue[0])
: Object.keys(node).sort();
for (const key of sortedKeys) {
const value = stringify(node[key]);
if (!value) continue;
if (out) out += ",";
out += `"${key}":${value}`;
}
seen.delete(node);
return `{${out}}`;
})(data);
}