Skip to content

Commit c627518

Browse files
test(helpers): refactor decycle
1 parent 3c5d17e commit c627518

1 file changed

Lines changed: 75 additions & 63 deletions

File tree

__tests__/helpers/decycle.ts

Lines changed: 75 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,81 @@ interface DecycledObject {
2828
$ref?: string;
2929
}
3030

31-
export function decycle(object: unknown, replacer?: ReplacerFunction) {
32-
'use strict';
31+
function deepCopy(
32+
value: unknown,
33+
path: string,
34+
objects: WeakMap<object, string>,
35+
replacer?: ReplacerFunction,
36+
): unknown {
37+
// Recurses through the object, producing the deep copy.
38+
39+
let old_path: string | undefined; // The path of an earlier occurance of value
40+
let nu: unknown[] | DecycledObject; // The new object or array
41+
42+
// If a replacer function was provided, then call it to get a replacement value.
43+
44+
if (replacer !== undefined) {
45+
value = replacer(value);
46+
}
47+
48+
// typeof null === "object", so go on if this value is really an object but not
49+
// one of the weird builtin objects.
50+
51+
if (
52+
typeof value === 'object' &&
53+
value !== null &&
54+
!(value instanceof Boolean) &&
55+
!(value instanceof Date) &&
56+
!(value instanceof Number) &&
57+
!(value instanceof RegExp) &&
58+
!(value instanceof String)
59+
) {
60+
// If the value is an object or array, look to see if we have already
61+
// encountered it. If so, return a {"$ref":PATH} object. This uses an
62+
// ES6 WeakMap.
63+
64+
old_path = objects.get(value);
65+
if (old_path !== undefined) {
66+
return { $ref: old_path };
67+
}
68+
69+
// Otherwise, accumulate the unique value and its path.
70+
71+
objects.set(value, path);
72+
73+
// If it is an array, replicate the array.
74+
75+
if (Array.isArray(value)) {
76+
nu = [];
77+
(value as unknown[]).forEach(function (element: unknown, i: number) {
78+
(nu as unknown[])[i] = deepCopy(
79+
element,
80+
path + '[' + String(i) + ']',
81+
objects,
82+
replacer,
83+
);
84+
});
85+
} else {
86+
// If it is an object, replicate the object.
87+
88+
nu = {} as DecycledObject;
89+
Object.keys(value as Record<string, unknown>).forEach(function (
90+
name: string,
91+
) {
92+
(nu as DecycledObject)[name] = deepCopy(
93+
(value as Record<string, unknown>)[name],
94+
path + '[' + JSON.stringify(name) + ']',
95+
objects,
96+
replacer,
97+
);
98+
});
99+
}
100+
return nu;
101+
}
102+
return value;
103+
}
33104

105+
export function decycle(object: unknown, replacer?: ReplacerFunction) {
34106
// Make a deep copy of an object or array, assuring that there is at most
35107
// one instance of each object or array in the resulting structure. The
36108
// duplicate references (which might be forming cycles) are replaced with
@@ -57,65 +129,5 @@ export function decycle(object: unknown, replacer?: ReplacerFunction) {
57129

58130
const objects = new WeakMap<object, string>(); // object to path mappings
59131

60-
return (function derez(value: unknown, path: string): unknown {
61-
// The derez function recurses through the object, producing the deep copy.
62-
63-
let old_path: string | undefined; // The path of an earlier occurance of value
64-
let nu: unknown[] | DecycledObject; // The new object or array
65-
66-
// If a replacer function was provided, then call it to get a replacement value.
67-
68-
if (replacer !== undefined) {
69-
value = replacer(value);
70-
}
71-
72-
// typeof null === "object", so go on if this value is really an object but not
73-
// one of the weird builtin objects.
74-
75-
if (
76-
typeof value === 'object' &&
77-
value !== null &&
78-
!(value instanceof Boolean) &&
79-
!(value instanceof Date) &&
80-
!(value instanceof Number) &&
81-
!(value instanceof RegExp) &&
82-
!(value instanceof String)
83-
) {
84-
// If the value is an object or array, look to see if we have already
85-
// encountered it. If so, return a {"$ref":PATH} object. This uses an
86-
// ES6 WeakMap.
87-
88-
old_path = objects.get(value);
89-
if (old_path !== undefined) {
90-
return { $ref: old_path };
91-
}
92-
93-
// Otherwise, accumulate the unique value and its path.
94-
95-
objects.set(value, path);
96-
97-
// If it is an array, replicate the array.
98-
99-
if (Array.isArray(value)) {
100-
nu = [];
101-
(value as unknown[]).forEach(function (element: unknown, i: number) {
102-
(nu as unknown[])[i] = derez(element, path + '[' + String(i) + ']');
103-
});
104-
} else {
105-
// If it is an object, replicate the object.
106-
107-
nu = {} as DecycledObject;
108-
Object.keys(value as Record<string, unknown>).forEach(function (
109-
name: string,
110-
) {
111-
(nu as DecycledObject)[name] = derez(
112-
(value as Record<string, unknown>)[name],
113-
path + '[' + JSON.stringify(name) + ']',
114-
);
115-
});
116-
}
117-
return nu;
118-
}
119-
return value;
120-
})(object, '$');
132+
return deepCopy(object, '$', objects, replacer);
121133
}

0 commit comments

Comments
 (0)