@superhero/deep 4.0.0 → 4.1.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 CHANGED
@@ -22,7 +22,7 @@ import deepassign from '@superhero/deep/assign'
22
22
  const target = { foo: { bar: 1 } }
23
23
  const source = { foo: { baz: 2 } }
24
24
 
25
- deepassign.assign(target, source)
25
+ deepassign(target, source)
26
26
 
27
27
  console.log(target) // { foo: { bar: 1, baz: 2 } }
28
28
  ```
@@ -46,7 +46,7 @@ import deepmerge from '@superhero/deep/merge'
46
46
  const obj1 = { foo: { bar: 1 }, arr: [1, 2] }
47
47
  const obj2 = { foo: { baz: 2 }, arr: [2, 3] }
48
48
 
49
- const result = deepmerge.merge(obj1, obj2)
49
+ const result = deepmerge(obj1, obj2)
50
50
 
51
51
  console.log(result)
52
52
  // { foo: { bar: 1, baz: 2 }, arr: [1, 2, 3] }
@@ -70,7 +70,7 @@ import deepfreeze from '@superhero/deep/freeze'
70
70
  const obj = { foo: { bar: 'baz' } }
71
71
  obj.foo.self = obj.foo // Circular reference
72
72
 
73
- deepfreeze.freeze(obj)
73
+ deepfreeze(obj)
74
74
 
75
75
  obj.foo.bar = 'new value' // TypeError: Cannot assign to read-only property
76
76
  ```
@@ -93,7 +93,7 @@ import deepclone from '@superhero/deep/clone'
93
93
 
94
94
  const obj = { foo: { bar: 'baz' }, arr: [1, 2, 3] }
95
95
 
96
- const clone = deepclone.clone(obj)
96
+ const clone = deepclone(obj)
97
97
 
98
98
  console.log(clone) // Deeply cloned object
99
99
  console.log(clone === obj) // false
@@ -101,6 +101,37 @@ console.log(clone === obj) // false
101
101
 
102
102
  ---
103
103
 
104
+ ## 5. **Deep**
105
+
106
+ ### Purpose:
107
+ Makes the functions accessible through the imported default object.
108
+
109
+ ### Features:
110
+ - Exports all the above mentioned functions.
111
+ - Exports a default object with all the above mentioned functions as members.
112
+
113
+ ### Example:
114
+ ```javascript
115
+ import deep from '@superhero/deep'
116
+
117
+ deep.assign(/* ... */)
118
+ deep.clone(/* ... */)
119
+ deep.freeze(/* ... */)
120
+ deep.merge(/* ... */)
121
+ ```
122
+
123
+ ### Example:
124
+ ```javascript
125
+ import { assign, clone, freeze, merge } from '@superhero/deep'
126
+
127
+ assign(/* ... */)
128
+ clone(/* ... */)
129
+ freeze(/* ... */)
130
+ merge(/* ... */)
131
+ ```
132
+
133
+ ---
134
+
104
135
  ## Testing
105
136
  Each utility class has a set of unit tests to ensure correctness across different cases.
106
137
 
@@ -114,61 +145,67 @@ npm test
114
145
 
115
146
  ```
116
147
  ▶ @superhero/deep/assign
117
- Merges arrays correctly (2.205053ms)
118
- Merges objects correctly (0.37599ms)
119
- ✔ Overwrites non-object properties correctly (0.211516ms)
120
- ✔ Handles undefined values correctly (0.174874ms)
148
+ Assigns arrays correctly (1.993351ms)
149
+ Assigns objects correctly (0.797187ms)
150
+ ✔ Overwrites non-object properties correctly (0.311862ms)
151
+ ✔ Handles undefined values correctly (0.67959ms)
121
152
  ▶ Descriptor properties
122
153
  ▶ Retains
123
- ✔ non-writable, non-configurable and non-enumarable (0.302976ms)
124
- ✔ writable but non-configurable and non-enumarable (0.250721ms)
125
- ✔ writable and configurable but non-enumarable (0.148421ms)
126
- ✔ Retains (0.912545ms)
154
+ ✔ non-writable, non-configurable and non-enumarable (0.365558ms)
155
+ ✔ writable but non-configurable and non-enumarable (0.384859ms)
156
+ ✔ writable and configurable but non-enumarable (0.24546ms)
157
+ ✔ Retains (1.395563ms)
127
158
  ▶ Assigns
128
- ✔ non-writable, non-configurable and non-enumarable (0.266101ms)
129
- ✔ Assigns (1.379408ms)
130
- ✔ Descriptor properties (2.578897ms)
131
- ✔ Merges nested arrays correctly (0.188364ms)
132
- ✔ Merges nested objects correctly (0.159693ms)
133
- ✔ Does not alter objects with no conflicts (0.127767ms)
134
- ✔ @superhero/deep/assign (7.763161ms)
159
+ ✔ non-writable, non-configurable and non-enumarable (0.246451ms)
160
+ ✔ Assigns (0.528402ms)
161
+ ✔ Descriptor properties (2.438803ms)
162
+ ✔ Merges nested arrays correctly (1.67104ms)
163
+ ✔ Merges nested objects correctly (0.649403ms)
164
+ ✔ Does not alter objects with no conflicts (0.219023ms)
165
+ ✔ @superhero/deep/assign (10.784739ms)
135
166
 
136
167
  ▶ @superhero/deep/clone
137
- ✔ Clones simple objects (2.379372ms)
138
- ✔ Clones nested objects (0.316103ms)
139
- ✔ Clones arrays (0.31091ms)
140
- ✔ Handles circular references (0.204913ms)
141
- ✔ Clones objects with null prototype (0.385473ms)
142
- ✔ @superhero/deep/clone (5.133978ms)
168
+ ✔ Clones simple objects (3.898407ms)
169
+ ✔ Clones nested objects (0.358366ms)
170
+ ✔ Clones arrays (0.347023ms)
171
+ ✔ Handles circular references (0.190668ms)
172
+ ✔ Clones objects with null prototype (0.302189ms)
173
+ ✔ @superhero/deep/clone (6.720191ms)
143
174
 
144
175
  ▶ @superhero/deep/freeze
145
- ✔ Freezes a simple object (1.831304ms)
146
- ✔ Freezes nested objects recursively (0.242757ms)
147
- ✔ Handles circular references gracefully (0.155595ms)
148
- ✔ Freezes objects with symbols (0.190945ms)
149
- ✔ Handles already frozen objects without error (0.129469ms)
150
- ✔ Freezes objects with non-enumerable properties (0.193278ms)
151
- ✔ Freezes arrays (0.168815ms)
152
- ✔ Handles objects with null prototype (0.160426ms)
153
- ✔ Freezes objects with multiple property types (0.351257ms)
154
- ✔ @superhero/deep/freeze (5.204715ms)
176
+ ✔ Freezes a simple object (2.561531ms)
177
+ ✔ Freezes nested objects recursively (0.357866ms)
178
+ ✔ Handles circular references gracefully (0.308297ms)
179
+ ✔ Freezes objects with symbols (0.210516ms)
180
+ ✔ Handles already frozen objects without error (0.157051ms)
181
+ ✔ Freezes objects with non-enumerable properties (0.214033ms)
182
+ ✔ Freezes arrays (0.244445ms)
183
+ ✔ Handles objects with null prototype (0.337528ms)
184
+ ✔ Freezes objects with multiple property types (0.525787ms)
185
+ ✔ @superhero/deep/freeze (7.261487ms)
186
+
187
+ ▶ @superhero/deep
188
+ ✔ All functions are accessible as a member to the default import object (1.250244ms)
189
+ ✔ All functions are accessible to import from the default import object (0.23689ms)
190
+ ✔ @superhero/deep (3.080305ms)
155
191
 
156
192
  ▶ @superhero/deep/merge
157
- ✔ Merges arrays with unique values (2.511877ms)
158
- ✔ Merges arrays with order preserved (0.324244ms)
159
- ✔ Handles empty arrays correctly (0.319828ms)
160
- ✔ Handles arrays with duplicate values (0.258181ms)
161
- ✔ Merges objects and prioritizes restrictive descriptors (0.462677ms)
162
- ✔ Merges objects with non-enumerable properties (0.203703ms)
163
- ✔ Handles nested object merging (0.198121ms)
164
- ✔ Stops at circular references (0.137618ms)
165
- ✔ Stops when nested and with circular references (0.34896ms)
166
- ✔ Returns second value for non-object types (0.230399ms)
167
- ✔ Handles multiple merges sequentially (0.294846ms)
168
- ✔ @superhero/deep/merge (7.196893ms)
169
-
170
- tests 36
171
- pass 36
193
+ ✔ Merges arrays with unique values (3.122153ms)
194
+ ✔ Merges arrays with order preserved (0.298964ms)
195
+ ✔ Handles empty arrays correctly (0.275066ms)
196
+ ✔ Handles arrays with duplicate values (0.492229ms)
197
+ ✔ Merges objects and prioritizes restrictive descriptors (0.623443ms)
198
+ ✔ Merges objects with non-enumerable properties (0.342317ms)
199
+ ✔ Handles nested object merging (0.43726ms)
200
+ ✔ Stops at circular references (0.827394ms)
201
+ ✔ Stops when nested and with circular references (0.720571ms)
202
+ ✔ Returns second value for non-object types (0.925228ms)
203
+ ✔ Handles multiple merges sequentially (0.359582ms)
204
+ ✔ @superhero/deep/merge (12.298476ms)
205
+
206
+ tests 38
207
+ suites 8
208
+ pass 38
172
209
 
173
210
  -----------------------------------------------------------------
174
211
  file | line % | branch % | funcs % | uncovered lines
@@ -179,10 +216,12 @@ clone.js | 100.00 | 100.00 | 100.00 |
179
216
  clone.test.js | 96.34 | 87.50 | 100.00 | 56-58
180
217
  freeze.js | 100.00 | 100.00 | 100.00 |
181
218
  freeze.test.js | 100.00 | 100.00 | 100.00 |
219
+ index.js | 100.00 | 100.00 | 100.00 |
220
+ index.test.js | 100.00 | 100.00 | 100.00 |
182
221
  merge.js | 100.00 | 100.00 | 100.00 |
183
222
  merge.test.js | 100.00 | 100.00 | 100.00 |
184
223
  -----------------------------------------------------------------
185
- all files | 99.65 | 99.15 | 100.00 |
224
+ all files | 99.66 | 99.19 | 100.00 |
186
225
  -----------------------------------------------------------------
187
226
  ```
188
227
 
package/assign.js CHANGED
@@ -1,89 +1,86 @@
1
- export default new class DeepAssign
1
+ export default function assign(a, ...b)
2
2
  {
3
- assign(a, ...b)
3
+ b.forEach((b) => assignB2A(a, b))
4
+ }
5
+
6
+ function assignB2A(a, b)
7
+ {
8
+ if(b === undefined)
4
9
  {
5
- b.forEach((b) => this.#assign(a, b))
10
+ return a
6
11
  }
7
12
 
8
- #assign(a, b)
13
+ if(Object.prototype.toString.call(a) === '[object Array]'
14
+ && Object.prototype.toString.call(b) === '[object Array]')
9
15
  {
10
- if(b === undefined)
11
- {
12
- return a
13
- }
14
-
15
- if(Object.prototype.toString.call(a) === '[object Array]'
16
- && Object.prototype.toString.call(b) === '[object Array]')
17
- {
18
- return this.#assignArray(a, b)
19
- }
20
-
21
- if(Object.prototype.toString.call(a) === '[object Object]'
22
- && Object.prototype.toString.call(b) === '[object Object]')
23
- {
24
- return this.#assignObject(a, b)
25
- }
26
-
27
- return b
16
+ return assignArray(a, b)
28
17
  }
29
18
 
30
- #assignArray(a, b)
19
+ if(Object.prototype.toString.call(a) === '[object Object]'
20
+ && Object.prototype.toString.call(b) === '[object Object]')
31
21
  {
32
- const values = new Set(a.concat(b))
33
- a.length = 0
34
- a.push(...values)
35
- return a
22
+ return assignObject(a, b)
36
23
  }
37
24
 
38
- /**
39
- * @param {Object} a
40
- * @param {Object} b
41
- *
42
- * @throws {TypeError} If a property in "a" is also in "b",
43
- * but the property in "a" is not configurable.
44
- *
45
- * @returns {Object} a
46
- */
47
- #assignObject(a, b)
25
+ return b
26
+ }
27
+
28
+ function assignArray(a, b)
29
+ {
30
+ const values = new Set(a.concat(b))
31
+ a.length = 0
32
+ a.push(...values)
33
+ return a
34
+ }
35
+
36
+ /**
37
+ * @param {Object} a
38
+ * @param {Object} b
39
+ *
40
+ * @throws {TypeError} If a property in "a" is also in "b",
41
+ * but the property in "a" is not configurable.
42
+ *
43
+ * @returns {Object} a
44
+ */
45
+ function assignObject(a, b)
46
+ {
47
+ for (const key of Object.getOwnPropertyNames(b))
48
48
  {
49
- for (const key of Object.getOwnPropertyNames(b))
49
+ if(Object.prototype.toString.call(a[key]) === '[object Object]'
50
+ && Object.prototype.toString.call(b[key]) === '[object Object]')
51
+ {
52
+ assignObject(a[key], b[key])
53
+ }
54
+ else if(Object.hasOwnProperty.call(a, key))
50
55
  {
51
- if(Object.prototype.toString.call(a[key]) === '[object Object]'
52
- && Object.prototype.toString.call(b[key]) === '[object Object]')
56
+ const descriptor_a = Object.getOwnPropertyDescriptor(a, key)
57
+
58
+ if(descriptor_a.configurable)
53
59
  {
54
- this.#assignObject(a[key], b[key])
60
+ assignPropertyDescriptor(a, b, key)
55
61
  }
56
- else if(Object.hasOwnProperty.call(a, key))
62
+ else if(descriptor_a.writable)
57
63
  {
58
- const descriptor_a = Object.getOwnPropertyDescriptor(a, key)
59
-
60
- if(descriptor_a.configurable)
61
- {
62
- this.#assignPropertyDescriptor(a, b, key)
63
- }
64
- else if(descriptor_a.writable)
65
- {
66
- descriptor_a.value = b[key]
67
- Object.defineProperty(a, key, descriptor_a)
68
- }
69
- else
70
- {
71
- continue
72
- }
64
+ descriptor_a.value = b[key]
65
+ Object.defineProperty(a, key, descriptor_a)
73
66
  }
74
67
  else
75
68
  {
76
- this.#assignPropertyDescriptor(a, b, key)
69
+ continue
77
70
  }
78
71
  }
79
-
80
- return a
72
+ else
73
+ {
74
+ assignPropertyDescriptor(a, b, key)
75
+ }
81
76
  }
82
77
 
83
- #assignPropertyDescriptor(a, b, key)
84
- {
85
- const descriptor_b = Object.getOwnPropertyDescriptor(b, key)
86
- descriptor_b.value = this.#assign(a[key], b[key])
87
- Object.defineProperty(a, key, descriptor_b)
88
- }
78
+ return a
79
+ }
80
+
81
+ function assignPropertyDescriptor(a, b, key)
82
+ {
83
+ const descriptor_b = Object.getOwnPropertyDescriptor(b, key)
84
+ descriptor_b.value = assignB2A(a[key], b[key])
85
+ Object.defineProperty(a, key, descriptor_b)
89
86
  }
package/assign.test.js CHANGED
@@ -4,25 +4,25 @@ import deepassign from '@superhero/deep/assign'
4
4
 
5
5
  suite('@superhero/deep/assign', () =>
6
6
  {
7
- test('Merges arrays correctly', () =>
7
+ test('Assigns arrays correctly', () =>
8
8
  {
9
9
  const
10
10
  a = [1, 2, 3],
11
11
  b = [3, 4, 5],
12
12
  expected = [1, 2, 3, 4, 5]
13
13
 
14
- deepassign.assign(a, b)
14
+ deepassign(a, b)
15
15
  assert.deepStrictEqual(a, expected, 'Arrays should merge with unique values')
16
16
  })
17
17
 
18
- test('Merges objects correctly', () =>
18
+ test('Assigns objects correctly', () =>
19
19
  {
20
20
  const
21
21
  a = { foo: 1, bar: { baz: 2 } },
22
- b = { bar: { qux: 3 }, hello: 'world' },
23
- expected = { foo: 1, bar: { baz: 2, qux: 3 }, hello: 'world' }
22
+ b = { foo: 2, bar: { qux: 3 }, hello: 'world' },
23
+ expected = { foo: 2, bar: { baz: 2, qux: 3 }, hello: 'world' }
24
24
 
25
- deepassign.assign(a, b)
25
+ deepassign(a, b)
26
26
  assert.deepStrictEqual(a, expected, 'Objects should deep merge')
27
27
  })
28
28
 
@@ -33,7 +33,7 @@ suite('@superhero/deep/assign', () =>
33
33
  b = { foo: 2 },
34
34
  expected = { foo: 2 }
35
35
 
36
- deepassign.assign(a, b)
36
+ deepassign(a, b)
37
37
  assert.deepStrictEqual(a, expected, 'Properties should be overwritten')
38
38
  })
39
39
 
@@ -44,7 +44,7 @@ suite('@superhero/deep/assign', () =>
44
44
  b = { foo: undefined },
45
45
  expected = { foo: 1 }
46
46
 
47
- deepassign.assign(a, b)
47
+ deepassign(a, b)
48
48
  assert.deepStrictEqual(a, expected, 'Undefined values should not overwrite existing properties')
49
49
  })
50
50
 
@@ -66,7 +66,7 @@ suite('@superhero/deep/assign', () =>
66
66
 
67
67
  const b = { foo: 2 }
68
68
 
69
- deepassign.assign(a, b)
69
+ deepassign(a, b)
70
70
 
71
71
  const descriptor_a = Object.getOwnPropertyDescriptor(a, 'foo')
72
72
 
@@ -90,7 +90,7 @@ suite('@superhero/deep/assign', () =>
90
90
 
91
91
  const b = { foo: 2 }
92
92
 
93
- deepassign.assign(a, b)
93
+ deepassign(a, b)
94
94
 
95
95
  const descriptor_a = Object.getOwnPropertyDescriptor(a, 'foo')
96
96
 
@@ -114,7 +114,7 @@ suite('@superhero/deep/assign', () =>
114
114
 
115
115
  const b = { foo: 2 }
116
116
 
117
- deepassign.assign(a, b)
117
+ deepassign(a, b)
118
118
 
119
119
  const descriptor_a = Object.getOwnPropertyDescriptor(a, 'foo')
120
120
 
@@ -141,7 +141,7 @@ suite('@superhero/deep/assign', () =>
141
141
  enumerable : false
142
142
  })
143
143
 
144
- deepassign.assign(a, b)
144
+ deepassign(a, b)
145
145
 
146
146
  const descriptor_a = Object.getOwnPropertyDescriptor(a, 'foo')
147
147
 
@@ -160,7 +160,7 @@ suite('@superhero/deep/assign', () =>
160
160
  b = { foo: [2, 3] },
161
161
  expected = { foo: [1, 2, 3] }
162
162
 
163
- deepassign.assign(a, b)
163
+ deepassign(a, b)
164
164
  assert.deepStrictEqual(a, expected, 'Nested arrays should merge with unique values')
165
165
  })
166
166
 
@@ -171,7 +171,7 @@ suite('@superhero/deep/assign', () =>
171
171
  b = { foo: { bar: { qux: 2 }}},
172
172
  expected = { foo: { bar: { baz: 1, qux: 2 }}}
173
173
 
174
- deepassign.assign(a, b)
174
+ deepassign(a, b)
175
175
  assert.deepStrictEqual(a, expected, 'Nested objects should deep merge')
176
176
  })
177
177
 
@@ -182,7 +182,7 @@ suite('@superhero/deep/assign', () =>
182
182
  b = { bar: 2 },
183
183
  expected = { foo: 1, bar: 2 }
184
184
 
185
- deepassign.assign(a, b)
185
+ deepassign(a, b)
186
186
  assert.deepStrictEqual(a, expected, 'Objects without conflicts should merge correctly')
187
187
  })
188
188
  })
package/clone.js CHANGED
@@ -1,9 +1,6 @@
1
- export default new class DeepClone
1
+ export default function clone(a, legacy = false)
2
2
  {
3
- clone(a, legacy = false)
4
- {
5
- return structuredClone && false === legacy
6
- ? structuredClone(a)
7
- : JSON.parse(JSON.stringify(a))
8
- }
3
+ return structuredClone && false === legacy
4
+ ? structuredClone(a)
5
+ : JSON.parse(JSON.stringify(a))
9
6
  }
package/clone.test.js CHANGED
@@ -8,8 +8,8 @@ suite('@superhero/deep/clone', () =>
8
8
  {
9
9
  const
10
10
  obj = { foo: 'bar', baz: 42 },
11
- result = deepclone.clone(obj),
12
- legacy = deepclone.clone(obj, true)
11
+ result = deepclone(obj),
12
+ legacy = deepclone(obj, true)
13
13
 
14
14
  assert.deepStrictEqual(result, obj, 'Cloned object should be equal to the original')
15
15
  assert.deepStrictEqual(legacy, obj, 'Cloned object should be equal to the original (legacy mode)')
@@ -23,8 +23,8 @@ suite('@superhero/deep/clone', () =>
23
23
  const obj = { foo: { bar: { baz: 'qux' } } }
24
24
 
25
25
  const
26
- result = deepclone.clone(obj),
27
- legacy = deepclone.clone(obj, true)
26
+ result = deepclone(obj),
27
+ legacy = deepclone(obj, true)
28
28
 
29
29
  assert.deepStrictEqual(result, obj, 'Cloned nested object should be equal to the original')
30
30
  assert.deepStrictEqual(legacy, obj, 'Cloned nested object should be equal to the original (legacy mode)')
@@ -38,8 +38,8 @@ suite('@superhero/deep/clone', () =>
38
38
  const arr = [1, 2, 3, [4, 5]]
39
39
 
40
40
  const
41
- result = deepclone.clone(arr),
42
- legacy = deepclone.clone(arr, true)
41
+ result = deepclone(arr),
42
+ legacy = deepclone(arr, true)
43
43
 
44
44
  assert.deepStrictEqual(result, arr, 'Cloned array should be equal to the original')
45
45
  assert.deepStrictEqual(legacy, arr, 'Cloned array should be equal to the original (legacy mode)')
@@ -63,7 +63,7 @@ suite('@superhero/deep/clone', () =>
63
63
  const obj = {}
64
64
  obj.self = obj
65
65
 
66
- const result = deepclone.clone(obj)
66
+ const result = deepclone(obj)
67
67
 
68
68
  assert.strictEqual(result.self, result, 'Circular references should be preserved in the clone')
69
69
  })
@@ -73,7 +73,7 @@ suite('@superhero/deep/clone', () =>
73
73
  const obj = Object.create(null)
74
74
  obj.foo = 'bar'
75
75
 
76
- const result = deepclone.clone(obj)
76
+ const result = deepclone(obj)
77
77
 
78
78
  assert.deepEqual(result, obj, 'Cloned object with null prototype should be equal to the original')
79
79
  assert.notStrictEqual(result, obj, 'Cloned object with null prototype should not share reference with the original')
package/freeze.js CHANGED
@@ -1,33 +1,30 @@
1
- export default new class DeepFreeze
1
+ export default function freeze(obj)
2
2
  {
3
- freeze(obj)
4
- {
5
- const seen = new WeakSet
6
- this.#freeze(obj, seen)
7
- }
3
+ const seen = new WeakSet
4
+ freezeIterator(obj, seen)
5
+ }
8
6
 
9
- #freeze(obj, seen)
10
- {
11
- const objType = Object.prototype.toString.call(obj)
7
+ function freezeIterator(obj, seen)
8
+ {
9
+ const objType = Object.prototype.toString.call(obj)
12
10
 
13
- if('[object Array]' === objType
14
- || '[object Object]' === objType)
11
+ if('[object Array]' === objType
12
+ || '[object Object]' === objType)
13
+ {
14
+ if(seen.has(obj))
15
15
  {
16
- if(seen.has(obj))
17
- {
18
- return
19
- }
20
- else
21
- {
22
- seen.add(obj)
23
- }
24
-
25
- for(const key of [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)])
26
- {
27
- this.#freeze(obj[key], seen)
28
- }
16
+ return
17
+ }
18
+ else
19
+ {
20
+ seen.add(obj)
21
+ }
29
22
 
30
- Object.freeze(obj)
23
+ for(const key of [...Object.getOwnPropertyNames(obj), ...Object.getOwnPropertySymbols(obj)])
24
+ {
25
+ freezeIterator(obj[key], seen)
31
26
  }
27
+
28
+ Object.freeze(obj)
32
29
  }
33
30
  }
package/freeze.test.js CHANGED
@@ -8,7 +8,7 @@ suite('@superhero/deep/freeze', () =>
8
8
  {
9
9
  const obj = { foo: 'bar' }
10
10
 
11
- deepfreeze.freeze(obj)
11
+ deepfreeze(obj)
12
12
 
13
13
  assert.throws(() => { obj.foo = 'baz' }, TypeError, 'Should not allow modifying a frozen object')
14
14
  assert.strictEqual(Object.isFrozen(obj), true, 'Object should be frozen')
@@ -18,7 +18,7 @@ suite('@superhero/deep/freeze', () =>
18
18
  {
19
19
  const obj = { foo: { bar: { baz: 'qux' } } }
20
20
 
21
- deepfreeze.freeze(obj)
21
+ deepfreeze(obj)
22
22
 
23
23
  assert.throws(() => { obj.foo.bar.baz = 'changed' }, TypeError, 'Should not allow modifying nested properties')
24
24
  assert.strictEqual(Object.isFrozen(obj.foo.bar), true, 'Nested object should be frozen')
@@ -30,7 +30,7 @@ suite('@superhero/deep/freeze', () =>
30
30
  const obj = {}
31
31
  obj.self = obj
32
32
 
33
- deepfreeze.freeze(obj)
33
+ deepfreeze(obj)
34
34
 
35
35
  assert.strictEqual(Object.isFrozen(obj), true, 'Object with circular reference should be frozen')
36
36
  assert.strictEqual(Object.isFrozen(obj.self), true, 'Circular reference should also be frozen')
@@ -41,7 +41,7 @@ suite('@superhero/deep/freeze', () =>
41
41
  const sym = Symbol('test')
42
42
  const obj = { [sym]: 'value' }
43
43
 
44
- deepfreeze.freeze(obj)
44
+ deepfreeze(obj)
45
45
 
46
46
  assert.throws(() => { obj[sym] = 'new value' }, TypeError, 'Should not allow modifying properties with symbols')
47
47
  assert.strictEqual(Object.isFrozen(obj), true, 'Object with symbols should be frozen')
@@ -51,7 +51,7 @@ suite('@superhero/deep/freeze', () =>
51
51
  {
52
52
  const obj = Object.freeze({ foo: 'bar' })
53
53
 
54
- deepfreeze.freeze(obj) // Should not throw an error
54
+ deepfreeze(obj) // Should not throw an error
55
55
  assert.strictEqual(Object.isFrozen(obj), true, 'Already frozen object should remain frozen')
56
56
  })
57
57
 
@@ -66,7 +66,7 @@ suite('@superhero/deep/freeze', () =>
66
66
  writable : true
67
67
  })
68
68
 
69
- deepfreeze.freeze(obj)
69
+ deepfreeze(obj)
70
70
 
71
71
  assert.throws(() => { obj.foo = 'baz' }, TypeError, 'Should not allow modifying non-enumerable properties')
72
72
  assert.strictEqual(Object.isFrozen(obj), true, 'Object with non-enumerable properties should be frozen')
@@ -76,7 +76,7 @@ suite('@superhero/deep/freeze', () =>
76
76
  {
77
77
  const arr = [1, 2, 3]
78
78
 
79
- deepfreeze.freeze(arr)
79
+ deepfreeze(arr)
80
80
 
81
81
  assert.throws(() => { arr[0] = 4 }, TypeError, 'Should not allow modifying an array')
82
82
  assert.strictEqual(Object.isFrozen(arr), true, 'Array should be frozen')
@@ -87,7 +87,7 @@ suite('@superhero/deep/freeze', () =>
87
87
  const obj = Object.create(null)
88
88
  obj.foo = 'bar'
89
89
 
90
- deepfreeze.freeze(obj)
90
+ deepfreeze(obj)
91
91
 
92
92
  assert.throws(() => { obj.foo = 'baz' }, TypeError, 'Should not allow modifying properties of objects with null prototype')
93
93
  assert.strictEqual(Object.isFrozen(obj), true, 'Object with null prototype should be frozen')
@@ -112,7 +112,7 @@ suite('@superhero/deep/freeze', () =>
112
112
  writable : true
113
113
  })
114
114
 
115
- deepfreeze.freeze(obj)
115
+ deepfreeze(obj)
116
116
 
117
117
  assert.throws(() => { obj.nested.baz = 'changed' }, TypeError, 'Should not allow modifying nested properties')
118
118
  assert.throws(() => { obj[sym] = 'new value' }, TypeError, 'Should not allow modifying symbol properties')
package/index.js CHANGED
@@ -1,4 +1,7 @@
1
- export * from '@superhero/deep/assign'
2
- export * from '@superhero/deep/clone'
3
- export * from '@superhero/deep/freeze'
4
- export * from '@superhero/deep/merge'
1
+ import assign from '@superhero/deep/assign'
2
+ import clone from '@superhero/deep/clone'
3
+ import freeze from '@superhero/deep/freeze'
4
+ import merge from '@superhero/deep/merge'
5
+
6
+ export { assign, clone, freeze, merge }
7
+ export default { assign, clone, freeze, merge }
package/index.test.js ADDED
@@ -0,0 +1,23 @@
1
+ import assert from 'assert'
2
+ import { suite, test } from 'node:test'
3
+ import deep from '@superhero/deep'
4
+ import { assign, clone, freeze, merge } from '@superhero/deep'
5
+
6
+ suite('@superhero/deep', () =>
7
+ {
8
+ test('All functions are accessible as a member to the default import object', () =>
9
+ {
10
+ assert.ok('function' === typeof deep.assign, 'Shold export the assign function')
11
+ assert.ok('function' === typeof deep.clone, 'Shold export the clone function')
12
+ assert.ok('function' === typeof deep.freeze, 'Shold export the freeze function')
13
+ assert.ok('function' === typeof deep.merge, 'Shold export the merge function')
14
+ })
15
+
16
+ test('All functions are accessible to import from the default import object', () =>
17
+ {
18
+ assert.ok('function' === typeof assign, 'Shold export the assign function')
19
+ assert.ok('function' === typeof clone, 'Shold export the clone function')
20
+ assert.ok('function' === typeof freeze, 'Shold export the freeze function')
21
+ assert.ok('function' === typeof merge, 'Shold export the merge function')
22
+ })
23
+ })
package/merge.js CHANGED
@@ -56,97 +56,94 @@
56
56
  * @example if array "a" with values [1, 1, 2, 2] is merged with array "b" with
57
57
  * values [2, 2, 3, 3], the new array will have values [1, 2, 3].
58
58
  */
59
- export default new class DeepMerge
59
+ export default function merge(a, b, ...c)
60
60
  {
61
- merge(a, b, ...c)
62
- {
63
- const
64
- seen = new WeakSet,
65
- output = this.#merge(a, b, seen)
61
+ const
62
+ seen = new WeakSet,
63
+ output = mergeAandB(a, b, seen)
64
+
65
+ return c.length
66
+ ? merge(output, ...c)
67
+ : output
68
+ }
66
69
 
67
- return c.length
68
- ? this.merge(output, ...c)
69
- : output
70
+ function mergeAandB(a, b, seen)
71
+ {
72
+ if(b === undefined)
73
+ {
74
+ return a
70
75
  }
71
76
 
72
- #merge(a, b, seen)
77
+ const
78
+ aType = Object.prototype.toString.call(a),
79
+ bType = Object.prototype.toString.call(b)
80
+
81
+ if('[object Array]' === aType
82
+ && '[object Array]' === bType)
73
83
  {
74
- if(b === undefined)
75
- {
76
- return a
77
- }
84
+ return mergeArray(a, b)
85
+ }
78
86
 
79
- const
80
- aType = Object.prototype.toString.call(a),
81
- bType = Object.prototype.toString.call(b)
87
+ if('[object Object]' === aType
88
+ && '[object Object]' === bType)
89
+ {
90
+ return mergeObject(a, b, seen)
91
+ }
82
92
 
83
- if('[object Array]' === aType
84
- && '[object Array]' === bType)
85
- {
86
- return this.#mergeArray(a, b)
87
- }
93
+ return b
94
+ }
88
95
 
89
- if('[object Object]' === aType
90
- && '[object Object]' === bType)
91
- {
92
- return this.#mergeObject(a, b, seen)
93
- }
96
+ function mergeArray(a, b)
97
+ {
98
+ return [...new Set(a.concat(b))]
99
+ }
94
100
 
101
+ function mergeObject(a, b, seen)
102
+ {
103
+ if(seen.has(a))
104
+ {
95
105
  return b
96
106
  }
97
107
 
98
- #mergeArray(a, b)
99
- {
100
- return [...new Set(a.concat(b))]
101
- }
108
+ seen.add(a)
102
109
 
103
- #mergeObject(a, b, seen)
110
+ const output = {}
111
+
112
+ for(const key of Object.getOwnPropertyNames(a))
104
113
  {
105
- if(seen.has(a))
114
+ if(key in b)
106
115
  {
107
- return b
116
+ continue
108
117
  }
109
-
110
- seen.add(a)
111
-
112
- const output = {}
113
-
114
- for(const key of Object.getOwnPropertyNames(a))
118
+ else
115
119
  {
116
- if(key in b)
117
- {
118
- continue
119
- }
120
- else
121
- {
122
- const descriptor = Object.getOwnPropertyDescriptor(a, key)
123
- Object.defineProperty(output, key, descriptor)
124
- }
120
+ const descriptor = Object.getOwnPropertyDescriptor(a, key)
121
+ Object.defineProperty(output, key, descriptor)
125
122
  }
123
+ }
126
124
 
127
- for(const key of Object.getOwnPropertyNames(b))
125
+ for(const key of Object.getOwnPropertyNames(b))
126
+ {
127
+ if(key in a)
128
128
  {
129
- if(key in a)
130
- {
131
- const
132
- descriptor_a = Object.getOwnPropertyDescriptor(a, key),
133
- descriptor_b = Object.getOwnPropertyDescriptor(b, key)
134
-
135
- Object.defineProperty(output, key,
136
- {
137
- configurable : descriptor_a.configurable && descriptor_b.configurable,
138
- enumerable : descriptor_a.enumerable && descriptor_b.enumerable,
139
- writable : descriptor_a.writable && descriptor_b.writable,
140
- value : this.#merge(a[key], b[key], seen)
141
- })
142
- }
143
- else
129
+ const
130
+ descriptor_a = Object.getOwnPropertyDescriptor(a, key),
131
+ descriptor_b = Object.getOwnPropertyDescriptor(b, key)
132
+
133
+ Object.defineProperty(output, key,
144
134
  {
145
- const descriptor = Object.getOwnPropertyDescriptor(b, key)
146
- Object.defineProperty(output, key, descriptor)
147
- }
135
+ configurable : descriptor_a.configurable && descriptor_b.configurable,
136
+ enumerable : descriptor_a.enumerable && descriptor_b.enumerable,
137
+ writable : descriptor_a.writable && descriptor_b.writable,
138
+ value : mergeAandB(a[key], b[key], seen)
139
+ })
140
+ }
141
+ else
142
+ {
143
+ const descriptor = Object.getOwnPropertyDescriptor(b, key)
144
+ Object.defineProperty(output, key, descriptor)
148
145
  }
149
-
150
- return output
151
146
  }
147
+
148
+ return output
152
149
  }
package/merge.test.js CHANGED
@@ -11,7 +11,7 @@ suite('@superhero/deep/merge', () =>
11
11
  b = [2, 3, 4],
12
12
  expected = [1, 2, 3, 4]
13
13
 
14
- const result = deepmerge.merge(a, b)
14
+ const result = deepmerge(a, b)
15
15
  assert.deepStrictEqual(result, expected, 'Arrays should merge with unique values')
16
16
  })
17
17
 
@@ -22,7 +22,7 @@ suite('@superhero/deep/merge', () =>
22
22
  b = [1, 2, 3],
23
23
  expected = [2, 3, 4, 1]
24
24
 
25
- const result = deepmerge.merge(a, b)
25
+ const result = deepmerge(a, b)
26
26
  assert.deepStrictEqual(result, expected, 'Order of values should be preserved')
27
27
  })
28
28
 
@@ -33,7 +33,7 @@ suite('@superhero/deep/merge', () =>
33
33
  b = [],
34
34
  expected = [1, 2, 3]
35
35
 
36
- const result = deepmerge.merge(a, b)
36
+ const result = deepmerge(a, b)
37
37
  assert.deepStrictEqual(result, expected, 'Merging with empty array should not alter values')
38
38
  })
39
39
 
@@ -44,7 +44,7 @@ suite('@superhero/deep/merge', () =>
44
44
  b = [2, 2, 3, 3],
45
45
  expected = [1, 2, 3]
46
46
 
47
- const result = deepmerge.merge(a, b)
47
+ const result = deepmerge(a, b)
48
48
  assert.deepStrictEqual(result, expected, 'Duplicate values should be removed')
49
49
  })
50
50
 
@@ -71,7 +71,7 @@ suite('@superhero/deep/merge', () =>
71
71
  })
72
72
 
73
73
  const
74
- result = deepmerge.merge(a, b),
74
+ result = deepmerge(a, b),
75
75
  descriptor = Object.getOwnPropertyDescriptor(result, 'foo')
76
76
 
77
77
  assert.strictEqual(descriptor.value, 2, 'Value should prioritize the second object')
@@ -103,7 +103,7 @@ suite('@superhero/deep/merge', () =>
103
103
  })
104
104
 
105
105
  const
106
- result = deepmerge.merge(a, b),
106
+ result = deepmerge(a, b),
107
107
  descriptor = Object.getOwnPropertyDescriptor(result, 'foo')
108
108
 
109
109
  assert.strictEqual(descriptor.value, 2, 'Value should prioritize the second object')
@@ -119,7 +119,7 @@ suite('@superhero/deep/merge', () =>
119
119
  b = { foo: { baz: 2 } },
120
120
  expected = { foo: { bar: 1, baz: 2 } }
121
121
 
122
- const result = deepmerge.merge(a, b)
122
+ const result = deepmerge(a, b)
123
123
  assert.deepStrictEqual(result, expected, 'Nested objects should merge correctly')
124
124
  })
125
125
 
@@ -132,7 +132,7 @@ suite('@superhero/deep/merge', () =>
132
132
  a.self = a
133
133
  b.self = b
134
134
 
135
- const result = deepmerge.merge(a, b)
135
+ const result = deepmerge(a, b)
136
136
 
137
137
  assert.strictEqual(result.self, b.self, 'Circular references should not merge further')
138
138
  })
@@ -146,8 +146,8 @@ suite('@superhero/deep/merge', () =>
146
146
  b.foo.bar = b
147
147
 
148
148
  const
149
- resultA = deepmerge.merge(a, b),
150
- resultB = deepmerge.merge(b, a)
149
+ resultA = deepmerge(a, b),
150
+ resultB = deepmerge(b, a)
151
151
 
152
152
  assert.strictEqual(resultA.foo.bar.foo.bar, b, 'Circular references should not interfare with the merged result')
153
153
  assert.strictEqual(resultB.foo.bar.foo.bar, 'baz', 'Circular references should not interfare with the merged result')
@@ -160,7 +160,7 @@ suite('@superhero/deep/merge', () =>
160
160
  b = { foo: 42 },
161
161
  expected = { foo: 42 }
162
162
 
163
- const result = deepmerge.merge(a, b)
163
+ const result = deepmerge(a, b)
164
164
  assert.deepStrictEqual(result, expected, 'Non-object types should replace with the second value')
165
165
  })
166
166
 
@@ -172,10 +172,10 @@ suite('@superhero/deep/merge', () =>
172
172
  c = { baz: 3 },
173
173
  expected = { foo: 1, bar: 2, baz: 3 }
174
174
 
175
- const resultA = deepmerge.merge(a, b, c)
175
+ const resultA = deepmerge(a, b, c)
176
176
  assert.deepStrictEqual(resultA, expected, 'Multiple objects should merge sequentially')
177
177
 
178
- const resultB = deepmerge.merge(a, b, undefined, c)
178
+ const resultB = deepmerge(a, b, undefined, c)
179
179
  assert.deepStrictEqual(resultB, expected, 'Ignore undefined attributes')
180
180
  })
181
181
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superhero/deep",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "description": "A collection of deep structure operations",
5
5
  "keywords": ["deep", "assign", "clone", "freeze", "merge"],
6
6
  "main": "index.js",