@superhero/deep 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -49
- package/clone.js +46 -4
- package/clone.test.js +53 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -145,63 +145,66 @@ npm test
|
|
|
145
145
|
|
|
146
146
|
```
|
|
147
147
|
▶ @superhero/deep/assign
|
|
148
|
-
✔ Assigns arrays correctly (
|
|
149
|
-
✔ Assigns objects correctly (0.
|
|
150
|
-
✔ Overwrites non-object properties correctly (0.
|
|
151
|
-
✔ Handles undefined values correctly (0.
|
|
148
|
+
✔ Assigns arrays correctly (3.167398ms)
|
|
149
|
+
✔ Assigns objects correctly (0.763768ms)
|
|
150
|
+
✔ Overwrites non-object properties correctly (0.347727ms)
|
|
151
|
+
✔ Handles undefined values correctly (0.521744ms)
|
|
152
|
+
|
|
152
153
|
▶ Descriptor properties
|
|
153
154
|
▶ Retains
|
|
154
|
-
✔ non-writable, non-configurable and non-enumarable (0.
|
|
155
|
-
✔ writable but non-configurable and non-enumarable (0.
|
|
156
|
-
✔ writable and configurable but non-enumarable (0.
|
|
157
|
-
✔ Retains (1.
|
|
155
|
+
✔ non-writable, non-configurable and non-enumarable (0.474666ms)
|
|
156
|
+
✔ writable but non-configurable and non-enumarable (0.649244ms)
|
|
157
|
+
✔ writable and configurable but non-enumarable (0.298073ms)
|
|
158
|
+
✔ Retains (1.918778ms)
|
|
159
|
+
|
|
158
160
|
▶ Assigns
|
|
159
|
-
✔ non-writable, non-configurable and non-enumarable (0.
|
|
160
|
-
✔ Assigns (0.
|
|
161
|
-
✔ Descriptor properties (
|
|
162
|
-
|
|
163
|
-
✔ Merges nested
|
|
164
|
-
✔
|
|
165
|
-
✔
|
|
161
|
+
✔ non-writable, non-configurable and non-enumarable (0.485428ms)
|
|
162
|
+
✔ Assigns (0.889839ms)
|
|
163
|
+
✔ Descriptor properties (3.74915ms)
|
|
164
|
+
|
|
165
|
+
✔ Merges nested arrays correctly (1.898073ms)
|
|
166
|
+
✔ Merges nested objects correctly (0.761838ms)
|
|
167
|
+
✔ Does not alter objects with no conflicts (0.277694ms)
|
|
168
|
+
✔ @superhero/deep/assign (14.053485ms)
|
|
166
169
|
|
|
167
170
|
▶ @superhero/deep/clone
|
|
168
|
-
✔ Clones simple objects (
|
|
169
|
-
✔ Clones nested objects (0.
|
|
170
|
-
✔ Clones arrays (0.
|
|
171
|
-
✔ Handles circular references (0.
|
|
172
|
-
✔ Clones objects with null prototype (0.
|
|
173
|
-
✔ @superhero/deep/clone (
|
|
171
|
+
✔ Clones simple objects (7.235299ms)
|
|
172
|
+
✔ Clones nested objects (0.634553ms)
|
|
173
|
+
✔ Clones arrays (0.650244ms)
|
|
174
|
+
✔ Handles circular references (0.382144ms)
|
|
175
|
+
✔ Clones objects with null prototype (0.375334ms)
|
|
176
|
+
✔ @superhero/deep/clone (11.89498ms)
|
|
174
177
|
|
|
175
178
|
▶ @superhero/deep/freeze
|
|
176
|
-
✔ Freezes a simple object (
|
|
177
|
-
✔ Freezes nested objects recursively (0.
|
|
178
|
-
✔ Handles circular references gracefully (0.
|
|
179
|
-
✔ Freezes objects with symbols (0.
|
|
180
|
-
✔ Handles already frozen objects without error (0.
|
|
181
|
-
✔ Freezes objects with non-enumerable properties (0.
|
|
182
|
-
✔ Freezes arrays (0.
|
|
183
|
-
✔ Handles objects with null prototype (0.
|
|
184
|
-
✔ Freezes objects with multiple property types (0.
|
|
185
|
-
✔ @superhero/deep/freeze (
|
|
179
|
+
✔ Freezes a simple object (3.683567ms)
|
|
180
|
+
✔ Freezes nested objects recursively (0.406919ms)
|
|
181
|
+
✔ Handles circular references gracefully (0.309979ms)
|
|
182
|
+
✔ Freezes objects with symbols (0.405137ms)
|
|
183
|
+
✔ Handles already frozen objects without error (0.337038ms)
|
|
184
|
+
✔ Freezes objects with non-enumerable properties (0.354681ms)
|
|
185
|
+
✔ Freezes arrays (0.662049ms)
|
|
186
|
+
✔ Handles objects with null prototype (0.387428ms)
|
|
187
|
+
✔ Freezes objects with multiple property types (0.626902ms)
|
|
188
|
+
✔ @superhero/deep/freeze (10.607326ms)
|
|
186
189
|
|
|
187
190
|
▶ @superhero/deep
|
|
188
|
-
✔ All functions are accessible as a member to the default import object (1.
|
|
189
|
-
✔ All functions are accessible to import from the default import object (0.
|
|
190
|
-
✔ @superhero/deep (
|
|
191
|
+
✔ All functions are accessible as a member to the default import object (1.734551ms)
|
|
192
|
+
✔ All functions are accessible to import from the default import object (0.255022ms)
|
|
193
|
+
✔ @superhero/deep (5.188429ms)
|
|
191
194
|
|
|
192
195
|
▶ @superhero/deep/merge
|
|
193
|
-
✔ Merges arrays with unique values (3.
|
|
194
|
-
✔ Merges arrays with order preserved (0.
|
|
195
|
-
✔ Handles empty arrays correctly (0.
|
|
196
|
-
✔ Handles arrays with duplicate values (0.
|
|
197
|
-
✔ Merges objects and prioritizes restrictive descriptors (0.
|
|
198
|
-
✔ Merges objects with non-enumerable properties (0.
|
|
199
|
-
✔ Handles nested object merging (0.
|
|
200
|
-
✔ Stops at circular references (0.
|
|
201
|
-
✔ Stops when nested and with circular references (0.
|
|
202
|
-
✔ Returns second value for non-object types (0.
|
|
203
|
-
✔ Handles multiple merges sequentially (0.
|
|
204
|
-
✔ @superhero/deep/merge (
|
|
196
|
+
✔ Merges arrays with unique values (3.271451ms)
|
|
197
|
+
✔ Merges arrays with order preserved (0.455376ms)
|
|
198
|
+
✔ Handles empty arrays correctly (0.283857ms)
|
|
199
|
+
✔ Handles arrays with duplicate values (0.538911ms)
|
|
200
|
+
✔ Merges objects and prioritizes restrictive descriptors (0.592729ms)
|
|
201
|
+
✔ Merges objects with non-enumerable properties (0.343159ms)
|
|
202
|
+
✔ Handles nested object merging (0.359878ms)
|
|
203
|
+
✔ Stops at circular references (0.554345ms)
|
|
204
|
+
✔ Stops when nested and with circular references (0.667265ms)
|
|
205
|
+
✔ Returns second value for non-object types (0.744991ms)
|
|
206
|
+
✔ Handles multiple merges sequentially (0.327608ms)
|
|
207
|
+
✔ @superhero/deep/merge (11.803686ms)
|
|
205
208
|
|
|
206
209
|
tests 38
|
|
207
210
|
suites 8
|
|
@@ -212,8 +215,8 @@ file | line % | branch % | funcs % | uncovered lines
|
|
|
212
215
|
-----------------------------------------------------------------
|
|
213
216
|
assign.js | 100.00 | 100.00 | 100.00 |
|
|
214
217
|
assign.test.js | 100.00 | 100.00 | 100.00 |
|
|
215
|
-
clone.js |
|
|
216
|
-
clone.test.js |
|
|
218
|
+
clone.js | 95.83 | 92.86 | 100.00 | 22-23
|
|
219
|
+
clone.test.js | 100.00 | 100.00 | 100.00 |
|
|
217
220
|
freeze.js | 100.00 | 100.00 | 100.00 |
|
|
218
221
|
freeze.test.js | 100.00 | 100.00 | 100.00 |
|
|
219
222
|
index.js | 100.00 | 100.00 | 100.00 |
|
|
@@ -221,7 +224,7 @@ index.test.js | 100.00 | 100.00 | 100.00 |
|
|
|
221
224
|
merge.js | 100.00 | 100.00 | 100.00 |
|
|
222
225
|
merge.test.js | 100.00 | 100.00 | 100.00 |
|
|
223
226
|
-----------------------------------------------------------------
|
|
224
|
-
all files | 99.
|
|
227
|
+
all files | 99.78 | 99.25 | 100.00 |
|
|
225
228
|
-----------------------------------------------------------------
|
|
226
229
|
```
|
|
227
230
|
|
package/clone.js
CHANGED
|
@@ -1,6 +1,48 @@
|
|
|
1
|
-
export default function clone(
|
|
1
|
+
export default function clone(input)
|
|
2
2
|
{
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const seen = new WeakSet()
|
|
4
|
+
return deepClone(input, seen)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function deepClone(value, seen)
|
|
8
|
+
{
|
|
9
|
+
switch(Object.prototype.toString.call(value))
|
|
10
|
+
{
|
|
11
|
+
case '[object Array]' : return cloneArray(value, seen)
|
|
12
|
+
case '[object Object]' : return cloneObject(value, seen)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return structuredClone(value)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function cloneArray(array, seen)
|
|
19
|
+
{
|
|
20
|
+
if(seen.has(array))
|
|
21
|
+
{
|
|
22
|
+
return array
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
seen.add(array)
|
|
26
|
+
return array.map((item) => deepClone(item, seen))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function cloneObject(obj, seen)
|
|
30
|
+
{
|
|
31
|
+
if(seen.has(obj))
|
|
32
|
+
{
|
|
33
|
+
return obj
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
seen.add(obj)
|
|
37
|
+
|
|
38
|
+
const output = {}
|
|
39
|
+
|
|
40
|
+
for(const key of Object.getOwnPropertyNames(obj))
|
|
41
|
+
{
|
|
42
|
+
const descriptor = Object.getOwnPropertyDescriptor(obj, key)
|
|
43
|
+
Object.defineProperty(output, key,
|
|
44
|
+
{ ...descriptor, value : deepClone(descriptor.value, seen) })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return output
|
|
6
48
|
}
|
package/clone.test.js
CHANGED
|
@@ -8,75 +8,76 @@ suite('@superhero/deep/clone', () =>
|
|
|
8
8
|
{
|
|
9
9
|
const
|
|
10
10
|
obj = { foo: 'bar', baz: 42 },
|
|
11
|
-
|
|
12
|
-
legacy = deepclone(obj, true)
|
|
11
|
+
cloned = deepclone(obj)
|
|
13
12
|
|
|
14
|
-
assert.deepStrictEqual(
|
|
15
|
-
assert.
|
|
16
|
-
|
|
17
|
-
assert.notStrictEqual(result, obj, 'Cloned object should not be the same reference as the original')
|
|
18
|
-
assert.notStrictEqual(legacy, obj, 'Cloned object should not be the same reference as the original (legacy mode)')
|
|
13
|
+
assert.deepStrictEqual(cloned, obj, 'Cloned object should be equal to the original')
|
|
14
|
+
assert.notStrictEqual(cloned, obj, 'Not the same reference as the original')
|
|
19
15
|
})
|
|
20
16
|
|
|
21
17
|
test('Clones nested objects', () =>
|
|
22
18
|
{
|
|
23
|
-
const obj = { foo: { bar: { baz: 'qux' } } }
|
|
24
|
-
|
|
25
19
|
const
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
obj = { foo: { bar: { baz: 'qux' } } },
|
|
21
|
+
cloned = deepclone(obj)
|
|
22
|
+
|
|
23
|
+
assert.deepStrictEqual(cloned, obj, 'Cloned nested object should be equal to the original')
|
|
24
|
+
assert.notStrictEqual(cloned.foo, obj.foo, 'Not the same reference as the original')
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
test('Preserves descriptors', () =>
|
|
28
|
+
{
|
|
29
|
+
const origin = {}
|
|
30
|
+
|
|
31
|
+
Object.defineProperty(origin, 'foo', { value: {}, enumerable: true, writable: true, configurable: true })
|
|
32
|
+
Object.defineProperty(origin, 'bar', { value: {}, enumerable: false, writable: true, configurable: true })
|
|
33
|
+
Object.defineProperty(origin, 'baz', { value: {}, enumerable: false, writable: false, configurable: true })
|
|
34
|
+
Object.defineProperty(origin, 'qux', { value: {}, enumerable: false, writable: false, configurable: false })
|
|
35
|
+
|
|
36
|
+
const cloned = deepclone(origin)
|
|
37
|
+
|
|
38
|
+
assert.deepStrictEqual(cloned, origin, 'Cloned nested object should be equal to the original')
|
|
39
|
+
assert.notStrictEqual(cloned, origin, 'Not the same reference as the original')
|
|
40
|
+
assert.notStrictEqual(cloned.foo, origin.foo, 'Cloned nested object should not share reference with the original')
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
const
|
|
43
|
+
clonedDescriptors = Object.getOwnPropertyDescriptors(cloned),
|
|
44
|
+
originDescriptors = Object.getOwnPropertyDescriptors(origin)
|
|
31
45
|
|
|
32
|
-
|
|
33
|
-
|
|
46
|
+
for(const key in originDescriptors)
|
|
47
|
+
{
|
|
48
|
+
assert.equal(clonedDescriptors[key].enumerable, originDescriptors[key].enumerable)
|
|
49
|
+
assert.equal(clonedDescriptors[key].writable, originDescriptors[key].writable)
|
|
50
|
+
assert.equal(clonedDescriptors[key].configurable, originDescriptors[key].configurable)
|
|
51
|
+
}
|
|
34
52
|
})
|
|
35
53
|
|
|
36
54
|
test('Clones arrays', () =>
|
|
37
55
|
{
|
|
38
|
-
const arr = [1, 2, 3, [4, 5]]
|
|
39
|
-
|
|
40
56
|
const
|
|
41
|
-
|
|
42
|
-
|
|
57
|
+
array = [1, 2, 3, [4, 5]],
|
|
58
|
+
cloned = deepclone(array)
|
|
43
59
|
|
|
44
|
-
assert.deepStrictEqual(
|
|
45
|
-
assert.
|
|
60
|
+
assert.deepStrictEqual(cloned, array, 'Cloned array should be equal to the original')
|
|
61
|
+
assert.notStrictEqual(cloned, array, 'Not the same reference as the original')
|
|
62
|
+
assert.notStrictEqual(cloned[3], array[3], 'Nested array in clone should not share reference with the original')
|
|
63
|
+
})
|
|
46
64
|
|
|
47
|
-
|
|
48
|
-
|
|
65
|
+
test('Handles circular references', () =>
|
|
66
|
+
{
|
|
67
|
+
const obj = {}
|
|
68
|
+
obj.self = obj
|
|
49
69
|
|
|
50
|
-
|
|
51
|
-
assert.
|
|
70
|
+
const cloned = deepclone(obj)
|
|
71
|
+
assert.strictEqual(cloned.self, obj, 'Circular references should be preserved in the clone')
|
|
52
72
|
})
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
{
|
|
56
|
-
test.skip('Handles circular references (structuredClone not available)')
|
|
57
|
-
test.skip('Clones objects with null prototype (structuredClone not available)')
|
|
58
|
-
}
|
|
59
|
-
else
|
|
74
|
+
test('Clones objects with null prototype', () =>
|
|
60
75
|
{
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
assert.strictEqual(result.self, result, 'Circular references should be preserved in the clone')
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
test('Clones objects with null prototype', () =>
|
|
72
|
-
{
|
|
73
|
-
const obj = Object.create(null)
|
|
74
|
-
obj.foo = 'bar'
|
|
75
|
-
|
|
76
|
-
const result = deepclone(obj)
|
|
77
|
-
|
|
78
|
-
assert.deepEqual(result, obj, 'Cloned object with null prototype should be equal to the original')
|
|
79
|
-
assert.notStrictEqual(result, obj, 'Cloned object with null prototype should not share reference with the original')
|
|
80
|
-
})
|
|
81
|
-
}
|
|
76
|
+
const obj = Object.create(null)
|
|
77
|
+
obj.foo = 'bar'
|
|
78
|
+
|
|
79
|
+
const cloned = deepclone(obj)
|
|
80
|
+
assert.deepEqual(cloned, obj, 'Cloned object with null prototype should be equal to the original')
|
|
81
|
+
assert.notStrictEqual(cloned, obj, 'Not the same reference as the original')
|
|
82
|
+
})
|
|
82
83
|
})
|