merge-anything 2.3.5 → 2.4.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
@@ -119,7 +119,6 @@ merge(
119
119
  {extensions: [concatArrays]}, // pass your extensions like so
120
120
  {nested: {prop: {array: ['a']}}},
121
121
  {nested: {prop: {array: ['b']}}},
122
- {array: ['b']}
123
122
  )
124
123
  // returns {nested: {prop: {array: ['a', 'b']}}},
125
124
  ```
@@ -157,15 +156,15 @@ Please note that each extension-function receives an `originVal` and `newVal` an
157
156
  Be careful for JavaScript object reference. Any property that's nested will be reactive and linked between the original and the merged objects! Down below we'll show how to prevent this.
158
157
 
159
158
  ```js
160
- const original = {airport: {airplane: '🛫'}}
161
- const new = {country: {location: 'Brussels'}}
162
- const merged = merge(original, merged)
159
+ const original = {airport: {airplane: 'dep. 🛫'}}
160
+ const extraInfo = {airport: {location: 'Brussels'}}
161
+ const merged = merge(original, extraInfo)
163
162
 
164
163
  // we change the airplane from departuring 🛫 to landing 🛬
165
- original.airport.airplane = '🛬'
166
- // This will change the value for `original` AND `merged`!!!
167
- original.airport.airplane === '🛬' // true
168
- merged.airport.airplane === '🛬' // true
164
+ merged.airport.airplane = 'lan. 🛬'
165
+ (merged.airport.airplane === 'lan. 🛬') // true
166
+ // However, `original` was also modified!
167
+ (original.airport.airplane === 'lan. 🛬') // true
169
168
  ```
170
169
 
171
170
  The key rule to remember is:
@@ -175,21 +174,23 @@ The key rule to remember is:
175
174
  However, **there is a really easy solution**. We can just copy the merge result to get rid of any reactivity. For this we can use the [copy-anything](https://github.com/mesqueeb/copy-anything) library. This library also makes sure that _special class instances do not break_, so you can use it without fear of breaking stuff!
176
175
 
177
176
  See below how we integrate 'copy-anything':
177
+
178
178
  ```js
179
179
  import copy from 'copy-anything'
180
180
 
181
- const original = {airport: {airplane: '🛫'}}
182
- const new = {country: {location: 'Brussels'}}
183
- const merged = merge(original, merged)
184
- const mergedNotReactive = copy(merged)
181
+ const original = {airport: {airplane: 'dep. 🛫'}}
182
+ const extraInfo = {airport: {location: 'Brussels'}}
183
+ const merged = copy(merge(original, extraInfo))
185
184
 
186
185
  // we change the airplane from departuring 🛫 to landing 🛬
187
- original.airport.airplane = '🛬'
188
- // `original` and `mergedNotReactive` are not linked anymore!
189
- original.airport.airplane === '🛬' // true
190
- mergedNotReactive.airport.airplane === '🛫' // true
186
+ merged.airport.airplane = 'lan. 🛬'
187
+ (merged.airport.airplane === 'lan. 🛬') // true
188
+ // `original` won't be modified!
189
+ (original.airport.airplane === 'lan. 🛬') // true
191
190
  ```
192
191
 
192
+ You can then play around where you want to place the `copy()` function.
193
+
193
194
  ## Source code
194
195
 
195
196
  It is literally just going through an object recursively and assigning the values to a new object like below. However, it's wrapped to allow extra params etc. The code below is the basic integration, that will make you understand the basics how it works.
package/dist/index.cjs.js CHANGED
@@ -4,6 +4,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var isWhat = require('is-what');
6
6
 
7
+ function assignProp(carry, key, newVal, originalObject) {
8
+ var propType = originalObject.propertyIsEnumerable(key)
9
+ ? 'enumerable'
10
+ : 'nonenumerable';
11
+ if (propType === 'enumerable')
12
+ carry[key] = newVal;
13
+ if (propType === 'nonenumerable') {
14
+ Object.defineProperty(carry, key, {
15
+ value: newVal,
16
+ enumerable: false,
17
+ writable: true,
18
+ configurable: true
19
+ });
20
+ }
21
+ }
7
22
  function mergeRecursively(origin, newComer, extensions) {
8
23
  // work directly on newComer if its not an object
9
24
  if (!isWhat.isPlainObject(newComer)) {
@@ -16,21 +31,27 @@ function mergeRecursively(origin, newComer, extensions) {
16
31
  return newComer;
17
32
  }
18
33
  // define newObject to merge all values upon
19
- var newObject = (isWhat.isPlainObject(origin))
20
- ? Object.keys(origin)
21
- .reduce(function (carry, key) {
22
- var targetVal = origin[key];
34
+ var newObject = {};
35
+ if (isWhat.isPlainObject(origin)) {
36
+ var props_1 = Object.getOwnPropertyNames(origin);
37
+ var symbols_1 = Object.getOwnPropertySymbols(origin);
38
+ newObject = props_1.concat(symbols_1).reduce(function (carry, key) {
23
39
  // @ts-ignore
24
- if (!Object.keys(newComer).includes(key))
25
- carry[key] = targetVal;
40
+ var targetVal = origin[key];
41
+ if ((!isWhat.isSymbol(key) && !Object.getOwnPropertyNames(newComer).includes(key)) ||
42
+ (isWhat.isSymbol(key) && !Object.getOwnPropertySymbols(newComer).includes(key))) {
43
+ assignProp(carry, key, targetVal, origin);
44
+ }
26
45
  return carry;
27
- }, {})
28
- : {};
29
- return Object.keys(newComer)
30
- .reduce(function (carry, key) {
46
+ }, {});
47
+ }
48
+ var props = Object.getOwnPropertyNames(newComer);
49
+ var symbols = Object.getOwnPropertySymbols(newComer);
50
+ var result = props.concat(symbols).reduce(function (carry, key) {
31
51
  // re-define the origin and newComer as targetVal and newVal
32
52
  var newVal = newComer[key];
33
53
  var targetVal = (isWhat.isPlainObject(origin))
54
+ // @ts-ignore
34
55
  ? origin[key]
35
56
  : undefined;
36
57
  // extend merge rules
@@ -39,20 +60,14 @@ function mergeRecursively(origin, newComer, extensions) {
39
60
  newVal = extend(targetVal, newVal);
40
61
  });
41
62
  }
42
- // early return when targetVal === undefined
43
- if (targetVal === undefined) {
44
- carry[key] = newVal;
45
- return carry;
46
- }
47
63
  // When newVal is an object do the merge recursively
48
- if (isWhat.isPlainObject(newVal)) {
49
- carry[key] = mergeRecursively(targetVal, newVal, extensions);
50
- return carry;
64
+ if (targetVal !== undefined && isWhat.isPlainObject(newVal)) {
65
+ newVal = mergeRecursively(targetVal, newVal, extensions);
51
66
  }
52
- // all the rest
53
- carry[key] = newVal;
67
+ assignProp(carry, key, newVal, newComer);
54
68
  return carry;
55
69
  }, newObject);
70
+ return result;
56
71
  }
57
72
  /**
58
73
  * Merge anything recursively.
@@ -63,7 +78,7 @@ function mergeRecursively(origin, newComer, extensions) {
63
78
  * @param {...any[]} newComers
64
79
  * @returns the result
65
80
  */
66
- function merge (origin) {
81
+ function merge(origin) {
67
82
  var newComers = [];
68
83
  for (var _i = 1; _i < arguments.length; _i++) {
69
84
  newComers[_i - 1] = arguments[_i];
package/dist/index.esm.js CHANGED
@@ -1,5 +1,20 @@
1
- import { isArray, isPlainObject } from 'is-what';
1
+ import { isArray, isPlainObject, isSymbol } from 'is-what';
2
2
 
3
+ function assignProp(carry, key, newVal, originalObject) {
4
+ var propType = originalObject.propertyIsEnumerable(key)
5
+ ? 'enumerable'
6
+ : 'nonenumerable';
7
+ if (propType === 'enumerable')
8
+ carry[key] = newVal;
9
+ if (propType === 'nonenumerable') {
10
+ Object.defineProperty(carry, key, {
11
+ value: newVal,
12
+ enumerable: false,
13
+ writable: true,
14
+ configurable: true
15
+ });
16
+ }
17
+ }
3
18
  function mergeRecursively(origin, newComer, extensions) {
4
19
  // work directly on newComer if its not an object
5
20
  if (!isPlainObject(newComer)) {
@@ -12,21 +27,27 @@ function mergeRecursively(origin, newComer, extensions) {
12
27
  return newComer;
13
28
  }
14
29
  // define newObject to merge all values upon
15
- var newObject = (isPlainObject(origin))
16
- ? Object.keys(origin)
17
- .reduce(function (carry, key) {
18
- var targetVal = origin[key];
30
+ var newObject = {};
31
+ if (isPlainObject(origin)) {
32
+ var props_1 = Object.getOwnPropertyNames(origin);
33
+ var symbols_1 = Object.getOwnPropertySymbols(origin);
34
+ newObject = props_1.concat(symbols_1).reduce(function (carry, key) {
19
35
  // @ts-ignore
20
- if (!Object.keys(newComer).includes(key))
21
- carry[key] = targetVal;
36
+ var targetVal = origin[key];
37
+ if ((!isSymbol(key) && !Object.getOwnPropertyNames(newComer).includes(key)) ||
38
+ (isSymbol(key) && !Object.getOwnPropertySymbols(newComer).includes(key))) {
39
+ assignProp(carry, key, targetVal, origin);
40
+ }
22
41
  return carry;
23
- }, {})
24
- : {};
25
- return Object.keys(newComer)
26
- .reduce(function (carry, key) {
42
+ }, {});
43
+ }
44
+ var props = Object.getOwnPropertyNames(newComer);
45
+ var symbols = Object.getOwnPropertySymbols(newComer);
46
+ var result = props.concat(symbols).reduce(function (carry, key) {
27
47
  // re-define the origin and newComer as targetVal and newVal
28
48
  var newVal = newComer[key];
29
49
  var targetVal = (isPlainObject(origin))
50
+ // @ts-ignore
30
51
  ? origin[key]
31
52
  : undefined;
32
53
  // extend merge rules
@@ -35,20 +56,14 @@ function mergeRecursively(origin, newComer, extensions) {
35
56
  newVal = extend(targetVal, newVal);
36
57
  });
37
58
  }
38
- // early return when targetVal === undefined
39
- if (targetVal === undefined) {
40
- carry[key] = newVal;
41
- return carry;
42
- }
43
59
  // When newVal is an object do the merge recursively
44
- if (isPlainObject(newVal)) {
45
- carry[key] = mergeRecursively(targetVal, newVal, extensions);
46
- return carry;
60
+ if (targetVal !== undefined && isPlainObject(newVal)) {
61
+ newVal = mergeRecursively(targetVal, newVal, extensions);
47
62
  }
48
- // all the rest
49
- carry[key] = newVal;
63
+ assignProp(carry, key, newVal, newComer);
50
64
  return carry;
51
65
  }, newObject);
66
+ return result;
52
67
  }
53
68
  /**
54
69
  * Merge anything recursively.
@@ -59,7 +74,7 @@ function mergeRecursively(origin, newComer, extensions) {
59
74
  * @param {...any[]} newComers
60
75
  * @returns the result
61
76
  */
62
- function merge (origin) {
77
+ function merge(origin) {
63
78
  var newComers = [];
64
79
  for (var _i = 1; _i < arguments.length; _i++) {
65
80
  newComers[_i - 1] = arguments[_i];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "merge-anything",
3
- "version": "2.3.5",
3
+ "version": "2.4.0",
4
4
  "description": "Merge objects & other types recursively. A simple & small integration.",
5
5
  "main": "dist/index.cjs.js",
6
6
  "module": "dist/index.esm.js",
package/src/merge.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { isArray, isPlainObject } from 'is-what'
1
+ import { isArray, isPlainObject, isSymbol } from 'is-what'
2
2
 
3
3
  type Extension = (param1: any, param2: any) => any
4
4
 
@@ -6,6 +6,21 @@ interface IConfig {
6
6
  extensions: Extension[]
7
7
  }
8
8
 
9
+ function assignProp (carry, key, newVal, originalObject) {
10
+ const propType = originalObject.propertyIsEnumerable(key)
11
+ ? 'enumerable'
12
+ : 'nonenumerable'
13
+ if (propType === 'enumerable') carry[key] = newVal
14
+ if (propType === 'nonenumerable') {
15
+ Object.defineProperty(carry, key, {
16
+ value: newVal,
17
+ enumerable: false,
18
+ writable: true,
19
+ configurable: true
20
+ })
21
+ }
22
+ }
23
+
9
24
  function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
10
25
  // work directly on newComer if its not an object
11
26
  if (!isPlainObject(newComer)) {
@@ -18,20 +33,31 @@ function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
18
33
  return newComer
19
34
  }
20
35
  // define newObject to merge all values upon
21
- const newObject = (isPlainObject(origin))
22
- ? Object.keys(origin)
36
+ let newObject = {}
37
+ if (isPlainObject(origin)) {
38
+ const props = Object.getOwnPropertyNames(origin)
39
+ const symbols = Object.getOwnPropertySymbols(origin)
40
+ newObject = [...props, ...symbols]
23
41
  .reduce((carry, key) => {
24
- const targetVal = origin[key]
25
42
  // @ts-ignore
26
- if (!Object.keys(newComer).includes(key)) carry[key] = targetVal
43
+ const targetVal = origin[key]
44
+ if (
45
+ (!isSymbol(key) && !Object.getOwnPropertyNames(newComer).includes(key)) ||
46
+ (isSymbol(key) && !Object.getOwnPropertySymbols(newComer).includes(key))
47
+ ) {
48
+ assignProp(carry, key, targetVal, origin)
49
+ }
27
50
  return carry
28
51
  }, {})
29
- : {}
30
- return Object.keys(newComer)
52
+ }
53
+ const props = Object.getOwnPropertyNames(newComer)
54
+ const symbols = Object.getOwnPropertySymbols(newComer)
55
+ let result = [...props, ...symbols]
31
56
  .reduce((carry, key) => {
32
57
  // re-define the origin and newComer as targetVal and newVal
33
58
  let newVal = newComer[key]
34
59
  const targetVal = (isPlainObject(origin))
60
+ // @ts-ignore
35
61
  ? origin[key]
36
62
  : undefined
37
63
  // extend merge rules
@@ -40,20 +66,14 @@ function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
40
66
  newVal = extend(targetVal, newVal)
41
67
  })
42
68
  }
43
- // early return when targetVal === undefined
44
- if (targetVal === undefined) {
45
- carry[key] = newVal
46
- return carry
47
- }
48
69
  // When newVal is an object do the merge recursively
49
- if (isPlainObject(newVal)) {
50
- carry[key] = mergeRecursively(targetVal, newVal, extensions)
51
- return carry
70
+ if (targetVal !== undefined && isPlainObject(newVal)) {
71
+ newVal = mergeRecursively(targetVal, newVal, extensions)
52
72
  }
53
- // all the rest
54
- carry[key] = newVal
73
+ assignProp(carry, key, newVal, newComer)
55
74
  return carry
56
75
  }, newObject)
76
+ return result
57
77
  }
58
78
 
59
79
  /**
@@ -65,7 +85,7 @@ function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
65
85
  * @param {...any[]} newComers
66
86
  * @returns the result
67
87
  */
68
- export default function (origin: IConfig | any, ...newComers: any[]) {
88
+ export default function merge (origin: IConfig | any, ...newComers: any[]) {
69
89
  let extensions = null
70
90
  let base = origin
71
91
  if (isPlainObject(origin) && origin.extensions && Object.keys(origin).length === 1) {
package/test/index.js CHANGED
@@ -372,11 +372,58 @@ test('works with unlimited depth', t => {
372
372
  })
373
373
 
374
374
  test('symbols as keys', t => {
375
+ let res, x, y
375
376
  const mySymbol = Symbol('mySymbol')
376
- const x = { value: 42, [mySymbol]: 'hello' }
377
- const y = { other: 33 }
378
- const res = merge(x, y)
377
+ x = { value: 42, [mySymbol]: 'hello' }
378
+ y = { other: 33 }
379
+ res = merge(x, y)
379
380
  t.is(res.value, 42)
380
381
  t.is(res.other, 33)
381
382
  t.is(res[mySymbol], 'hello')
383
+ x = { value: 42 }
384
+ y = { other: 33, [mySymbol]: 'hello' }
385
+ res = merge(x, y)
386
+ t.is(res.value, 42)
387
+ t.is(res.other, 33)
388
+ t.is(res[mySymbol], 'hello')
389
+ })
390
+
391
+ test('nonenumerable keys', t => {
392
+ let x, y, res
393
+ const mySymbol = Symbol('mySymbol')
394
+ x = { value: 42 }
395
+ y = { other: 33 }
396
+ Object.defineProperty(x, 'xid', {
397
+ value: 1,
398
+ writable: true,
399
+ enumerable: false,
400
+ configurable: true
401
+ })
402
+ Object.defineProperty(x, mySymbol, {
403
+ value: 'original',
404
+ writable: true,
405
+ enumerable: false,
406
+ configurable: true
407
+ })
408
+ Object.defineProperty(y, 'yid', {
409
+ value: 2,
410
+ writable: true,
411
+ enumerable: false,
412
+ configurable: true
413
+ })
414
+ Object.defineProperty(y, mySymbol, {
415
+ value: 'new',
416
+ writable: true,
417
+ enumerable: false,
418
+ configurable: true
419
+ })
420
+ res = merge(x, y)
421
+ t.is(res.value, 42)
422
+ t.is(res.other, 33)
423
+ t.is(res.xid, 1)
424
+ t.is(res.yid, 2)
425
+ t.is(res[mySymbol], 'new')
426
+ t.is(Object.keys(res).length, 2)
427
+ t.true(Object.keys(res).includes('value'))
428
+ t.true(Object.keys(res).includes('other'))
382
429
  })
package/types/merge.d.ts CHANGED
@@ -11,5 +11,5 @@ interface IConfig {
11
11
  * @param {...any[]} newComers
12
12
  * @returns the result
13
13
  */
14
- export default function (origin: IConfig | any, ...newComers: any[]): any;
14
+ export default function merge(origin: IConfig | any, ...newComers: any[]): any;
15
15
  export {};