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 +17 -16
- package/dist/index.cjs.js +36 -21
- package/dist/index.esm.js +37 -22
- package/package.json +1 -1
- package/src/merge.ts +38 -18
- package/test/index.js +50 -3
- package/types/merge.d.ts +1 -1
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
|
|
162
|
-
const merged = merge(original,
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
original
|
|
168
|
-
|
|
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
|
|
183
|
-
const merged = merge(original,
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
original
|
|
190
|
-
|
|
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 =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
30
|
-
|
|
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
|
-
|
|
50
|
-
return carry;
|
|
64
|
+
if (targetVal !== undefined && isWhat.isPlainObject(newVal)) {
|
|
65
|
+
newVal = mergeRecursively(targetVal, newVal, extensions);
|
|
51
66
|
}
|
|
52
|
-
|
|
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
|
|
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 =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
46
|
-
return carry;
|
|
60
|
+
if (targetVal !== undefined && isPlainObject(newVal)) {
|
|
61
|
+
newVal = mergeRecursively(targetVal, newVal, extensions);
|
|
47
62
|
}
|
|
48
|
-
|
|
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
|
|
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
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
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
return carry
|
|
70
|
+
if (targetVal !== undefined && isPlainObject(newVal)) {
|
|
71
|
+
newVal = mergeRecursively(targetVal, newVal, extensions)
|
|
52
72
|
}
|
|
53
|
-
|
|
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
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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