merge-anything 2.2.3 → 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
@@ -66,6 +66,7 @@ const c = merge(a, b)
66
66
  // However, be careful with JavaScript object references. See below: A note on JavaScript object references
67
67
 
68
68
  // arrays get overwritten
69
+ // (for "concat" logic, see Extensions below)
69
70
  merge({array: ['a']}, {array: ['b']}) // returns {array: ['b']}
70
71
 
71
72
  // empty objects merge into objects
@@ -83,15 +84,54 @@ merge-anything properly keeps special objects intact like dates, regex, function
83
84
 
84
85
  However, it's **very important** you understand how to work around JavaScript object references. Please be sure to read [a note on JavaScript object references](#a-note-on-javascript-object-references) down below.
85
86
 
86
- ## Extend merge rules
87
+ ## Extensions & custom rules
87
88
 
88
- merge-anything can be really powerful because every step of the way **you can define rules to extend the overwrite logic.**
89
+ There might be times you need to tweak the logic when two things are merged, eg. you need arrays to be *concatenated* instead of *overwritten*. This is possible through an extension!
89
90
 
90
- ### Concat arrays
91
+ To keep the source code _as small as possible_ I opted for an extionsion system where you can import just the logic you need.
91
92
 
92
- Eg. merge-anything will overwrite arrays by default but you could change this logic to make it so it will concat the arrays.
93
+ ### Concat arrays extension
93
94
 
94
- To do so your first parameter you pass has to be an object that looks like `{extensions: []}` and include an array of functions. In these functions you can change the value that will be overwriting the origin. See how to do this below:
95
+ ```js
96
+ import { merge, concatArrays } from 'merge-anything'
97
+
98
+ merge(
99
+ {extensions: [concatArrays]}, // pass your extensions like so
100
+ {array: ['a']},
101
+ {array: ['b']}
102
+ )
103
+ // returns {array: ['a', 'b']}
104
+ ```
105
+
106
+ All extensions get triggered at the top level and at every single nested prop. Let's look at two more examples to clarify this:
107
+
108
+ ```js
109
+ // top level:
110
+ merge(
111
+ {extensions: [concatArrays]},
112
+ ['a'],
113
+ ['b']
114
+ )
115
+ // returns ['a', 'b']
116
+
117
+ // nested props:
118
+ merge(
119
+ {extensions: [concatArrays]}, // pass your extensions like so
120
+ {nested: {prop: {array: ['a']}}},
121
+ {nested: {prop: {array: ['b']}}},
122
+ )
123
+ // returns {nested: {prop: {array: ['a', 'b']}}},
124
+ ```
125
+
126
+ Super simple!
127
+
128
+ ### Making your own extension / custom rule
129
+
130
+ The `concatArrays` extension imported above is actually just a function that takes both values it's trying to merge, and returns a new value. An "extension" function is triggered at the top level and on any nested props that should be merged.
131
+
132
+ This means that merge-anything can be really powerful because every step of the way **you can define rules to extend the overwrite logic.** Let's look at the `concatArrays` function as an example to see how you can write your own custom rules:
133
+
134
+ You have to define a function that receives two parameters. The first is the original value and the second is the new value that's being merged onto the original. When concatenating arrays, you can simply check if the value is an array or not and concatenate if it is.
95
135
 
96
136
  ```js
97
137
  function concatArrays (originVal, newVal) {
@@ -102,29 +142,29 @@ function concatArrays (originVal, newVal) {
102
142
  return newVal // always return newVal as fallback!!
103
143
  }
104
144
  merge(
105
- {extensions: [concatArrays]}, // pass your extensions like so
145
+ {extensions: [concatArrays]}, // pass your custom functions like so
106
146
  {array: ['a']},
107
147
  {array: ['b']}
108
148
  )
109
149
  // results in {array: ['a', 'b']}
110
150
  ```
111
151
 
112
- Please note that each extension-function receives an `originVal` and `newVal` and **has** to return the `newVal` on fallback no matter what (in case your condition check fails or something)!
152
+ Please note that each extension-function receives an `originVal` and `newVal` and **has** to return the `newVal` on fallback no matter what. Otherwise there might be cases that the original value is overwritten with `undefined`.
113
153
 
114
154
  ## A note on JavaScript object references
115
155
 
116
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.
117
157
 
118
158
  ```js
119
- const original = {airport: {airplane: '🛫'}}
120
- const new = {country: {location: 'Brussels'}}
121
- const merged = merge(original, merged)
159
+ const original = {airport: {airplane: 'dep. 🛫'}}
160
+ const extraInfo = {airport: {location: 'Brussels'}}
161
+ const merged = merge(original, extraInfo)
122
162
 
123
163
  // we change the airplane from departuring 🛫 to landing 🛬
124
- original.airport.airplane = '🛬'
125
- // This will change the value for `original` AND `merged`!!!
126
- original.airport.airplane === '🛬' // true
127
- 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
128
168
  ```
129
169
 
130
170
  The key rule to remember is:
@@ -134,21 +174,23 @@ The key rule to remember is:
134
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!
135
175
 
136
176
  See below how we integrate 'copy-anything':
177
+
137
178
  ```js
138
179
  import copy from 'copy-anything'
139
180
 
140
- const original = {airport: {airplane: '🛫'}}
141
- const new = {country: {location: 'Brussels'}}
142
- const merged = merge(original, merged)
143
- const mergedNotReactive = copy(merged)
181
+ const original = {airport: {airplane: 'dep. 🛫'}}
182
+ const extraInfo = {airport: {location: 'Brussels'}}
183
+ const merged = copy(merge(original, extraInfo))
144
184
 
145
185
  // we change the airplane from departuring 🛫 to landing 🛬
146
- original.airport.airplane = '🛬'
147
- // `original` and `mergedNotReactive` are not linked anymore!
148
- original.airport.airplane === '🛬' // true
149
- 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
150
190
  ```
151
191
 
192
+ You can then play around where you want to place the `copy()` function.
193
+
152
194
  ## Source code
153
195
 
154
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
@@ -1,7 +1,24 @@
1
1
  'use strict';
2
2
 
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
3
5
  var isWhat = require('is-what');
4
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
+ }
5
22
  function mergeRecursively(origin, newComer, extensions) {
6
23
  // work directly on newComer if its not an object
7
24
  if (!isWhat.isPlainObject(newComer)) {
@@ -14,21 +31,27 @@ function mergeRecursively(origin, newComer, extensions) {
14
31
  return newComer;
15
32
  }
16
33
  // define newObject to merge all values upon
17
- var newObject = (isWhat.isPlainObject(origin))
18
- ? Object.keys(origin)
19
- .reduce(function (carry, key) {
20
- 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) {
21
39
  // @ts-ignore
22
- if (!Object.keys(newComer).includes(key))
23
- 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
+ }
24
45
  return carry;
25
- }, {})
26
- : {};
27
- return Object.keys(newComer)
28
- .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) {
29
51
  // re-define the origin and newComer as targetVal and newVal
30
52
  var newVal = newComer[key];
31
53
  var targetVal = (isWhat.isPlainObject(origin))
54
+ // @ts-ignore
32
55
  ? origin[key]
33
56
  : undefined;
34
57
  // extend merge rules
@@ -37,20 +60,14 @@ function mergeRecursively(origin, newComer, extensions) {
37
60
  newVal = extend(targetVal, newVal);
38
61
  });
39
62
  }
40
- // early return when targetVal === undefined
41
- if (targetVal === undefined) {
42
- carry[key] = newVal;
43
- return carry;
44
- }
45
63
  // When newVal is an object do the merge recursively
46
- if (isWhat.isPlainObject(newVal)) {
47
- carry[key] = mergeRecursively(targetVal, newVal, extensions);
48
- return carry;
64
+ if (targetVal !== undefined && isWhat.isPlainObject(newVal)) {
65
+ newVal = mergeRecursively(targetVal, newVal, extensions);
49
66
  }
50
- // all the rest
51
- carry[key] = newVal;
67
+ assignProp(carry, key, newVal, newComer);
52
68
  return carry;
53
69
  }, newObject);
70
+ return result;
54
71
  }
55
72
  /**
56
73
  * Merge anything recursively.
@@ -61,7 +78,7 @@ function mergeRecursively(origin, newComer, extensions) {
61
78
  * @param {...any[]} newComers
62
79
  * @returns the result
63
80
  */
64
- function index (origin) {
81
+ function merge(origin) {
65
82
  var newComers = [];
66
83
  for (var _i = 1; _i < arguments.length; _i++) {
67
84
  newComers[_i - 1] = arguments[_i];
@@ -77,4 +94,14 @@ function index (origin) {
77
94
  }, base);
78
95
  }
79
96
 
80
- module.exports = index;
97
+ function concatArrays(originVal, newVal) {
98
+ if (isWhat.isArray(originVal) && isWhat.isArray(newVal)) {
99
+ // concat logic
100
+ return originVal.concat(newVal);
101
+ }
102
+ return newVal; // always return newVal as fallback!!
103
+ }
104
+
105
+ exports.merge = merge;
106
+ exports.concatArrays = concatArrays;
107
+ exports.default = merge;
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 index (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];
@@ -75,4 +90,13 @@ function index (origin) {
75
90
  }, base);
76
91
  }
77
92
 
78
- export default index;
93
+ function concatArrays(originVal, newVal) {
94
+ if (isArray(originVal) && isArray(newVal)) {
95
+ // concat logic
96
+ return originVal.concat(newVal);
97
+ }
98
+ return newVal; // always return newVal as fallback!!
99
+ }
100
+
101
+ export default merge;
102
+ export { merge, concatArrays };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "merge-anything",
3
- "version": "2.2.3",
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",
@@ -44,15 +44,15 @@
44
44
  "homepage": "https://github.com/mesqueeb/merge-anything#readme",
45
45
  "devDependencies": {
46
46
  "@ava/babel-preset-stage-4": "^2.0.0",
47
- "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
48
- "@babel/preset-env": "^7.1.0",
49
- "ava": "1.0.0-beta.8",
50
- "copy-anything": "^1.1.1",
47
+ "@babel/plugin-proposal-object-rest-spread": "^7.5.4",
48
+ "@babel/preset-env": "^7.5.4",
49
+ "ava": "^1.4.1",
50
+ "copy-anything": "^1.2.4",
51
51
  "rollup": "^0.65.2",
52
- "rollup-plugin-typescript2": "^0.19.2",
53
- "typescript": "^3.0.3"
52
+ "rollup-plugin-typescript2": "^0.21.2",
53
+ "typescript": "^3.5.3"
54
54
  },
55
55
  "dependencies": {
56
- "is-what": "^3.1.1"
56
+ "is-what": "^3.2.4"
57
57
  }
58
58
  }
@@ -0,0 +1,9 @@
1
+ import { isArray } from 'is-what'
2
+
3
+ export function concatArrays (originVal, newVal) {
4
+ if (isArray(originVal) && isArray(newVal)) {
5
+ // concat logic
6
+ return originVal.concat(newVal)
7
+ }
8
+ return newVal // always return newVal as fallback!!
9
+ }
package/src/index.ts CHANGED
@@ -1,78 +1,5 @@
1
- import { isArray, isPlainObject } from 'is-what'
1
+ import merge from './merge'
2
+ import { concatArrays } from './extensions'
2
3
 
3
- type Extension = (param1: any, param2: any) => any
4
-
5
- interface IConfig {
6
- extensions: Extension[]
7
- }
8
-
9
- function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
10
- // work directly on newComer if its not an object
11
- if (!isPlainObject(newComer)) {
12
- // extend merge rules
13
- if (extensions && isArray(extensions)) {
14
- extensions.forEach(extend => {
15
- newComer = extend(origin, newComer)
16
- })
17
- }
18
- return newComer
19
- }
20
- // define newObject to merge all values upon
21
- const newObject = (isPlainObject(origin))
22
- ? Object.keys(origin)
23
- .reduce((carry, key) => {
24
- const targetVal = origin[key]
25
- // @ts-ignore
26
- if (!Object.keys(newComer).includes(key)) carry[key] = targetVal
27
- return carry
28
- }, {})
29
- : {}
30
- return Object.keys(newComer)
31
- .reduce((carry, key) => {
32
- // re-define the origin and newComer as targetVal and newVal
33
- let newVal = newComer[key]
34
- const targetVal = (isPlainObject(origin))
35
- ? origin[key]
36
- : undefined
37
- // extend merge rules
38
- if (extensions && isArray(extensions)) {
39
- extensions.forEach(extend => {
40
- newVal = extend(targetVal, newVal)
41
- })
42
- }
43
- // early return when targetVal === undefined
44
- if (targetVal === undefined) {
45
- carry[key] = newVal
46
- return carry
47
- }
48
- // When newVal is an object do the merge recursively
49
- if (isPlainObject(newVal)) {
50
- carry[key] = mergeRecursively(targetVal, newVal, extensions)
51
- return carry
52
- }
53
- // all the rest
54
- carry[key] = newVal
55
- return carry
56
- }, newObject)
57
- }
58
-
59
- /**
60
- * Merge anything recursively.
61
- * Objects get merged, special objects (classes etc.) are re-assigned "as is".
62
- * Basic types overwrite objects or other basic types.
63
- *
64
- * @param {(IConfig | any)} origin
65
- * @param {...any[]} newComers
66
- * @returns the result
67
- */
68
- export default function (origin: IConfig | any, ...newComers: any[]) {
69
- let extensions = null
70
- let base = origin
71
- if (isPlainObject(origin) && origin.extensions && Object.keys(origin).length === 1) {
72
- base = {}
73
- extensions = origin.extensions
74
- }
75
- return newComers.reduce((result, newComer) => {
76
- return mergeRecursively(result, newComer, extensions)
77
- }, base)
78
- }
4
+ export { merge, concatArrays }
5
+ export default merge
package/src/merge.ts ADDED
@@ -0,0 +1,98 @@
1
+ import { isArray, isPlainObject, isSymbol } from 'is-what'
2
+
3
+ type Extension = (param1: any, param2: any) => any
4
+
5
+ interface IConfig {
6
+ extensions: Extension[]
7
+ }
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
+
24
+ function mergeRecursively(origin: any, newComer: any, extensions: Extension[]) {
25
+ // work directly on newComer if its not an object
26
+ if (!isPlainObject(newComer)) {
27
+ // extend merge rules
28
+ if (extensions && isArray(extensions)) {
29
+ extensions.forEach(extend => {
30
+ newComer = extend(origin, newComer)
31
+ })
32
+ }
33
+ return newComer
34
+ }
35
+ // define newObject to merge all values upon
36
+ let newObject = {}
37
+ if (isPlainObject(origin)) {
38
+ const props = Object.getOwnPropertyNames(origin)
39
+ const symbols = Object.getOwnPropertySymbols(origin)
40
+ newObject = [...props, ...symbols]
41
+ .reduce((carry, key) => {
42
+ // @ts-ignore
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
+ }
50
+ return carry
51
+ }, {})
52
+ }
53
+ const props = Object.getOwnPropertyNames(newComer)
54
+ const symbols = Object.getOwnPropertySymbols(newComer)
55
+ let result = [...props, ...symbols]
56
+ .reduce((carry, key) => {
57
+ // re-define the origin and newComer as targetVal and newVal
58
+ let newVal = newComer[key]
59
+ const targetVal = (isPlainObject(origin))
60
+ // @ts-ignore
61
+ ? origin[key]
62
+ : undefined
63
+ // extend merge rules
64
+ if (extensions && isArray(extensions)) {
65
+ extensions.forEach(extend => {
66
+ newVal = extend(targetVal, newVal)
67
+ })
68
+ }
69
+ // When newVal is an object do the merge recursively
70
+ if (targetVal !== undefined && isPlainObject(newVal)) {
71
+ newVal = mergeRecursively(targetVal, newVal, extensions)
72
+ }
73
+ assignProp(carry, key, newVal, newComer)
74
+ return carry
75
+ }, newObject)
76
+ return result
77
+ }
78
+
79
+ /**
80
+ * Merge anything recursively.
81
+ * Objects get merged, special objects (classes etc.) are re-assigned "as is".
82
+ * Basic types overwrite objects or other basic types.
83
+ *
84
+ * @param {(IConfig | any)} origin
85
+ * @param {...any[]} newComers
86
+ * @returns the result
87
+ */
88
+ export default function merge (origin: IConfig | any, ...newComers: any[]) {
89
+ let extensions = null
90
+ let base = origin
91
+ if (isPlainObject(origin) && origin.extensions && Object.keys(origin).length === 1) {
92
+ base = {}
93
+ extensions = origin.extensions
94
+ }
95
+ return newComers.reduce((result, newComer) => {
96
+ return mergeRecursively(result, newComer, extensions)
97
+ }, base)
98
+ }
package/test/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import test from 'ava'
2
2
  import merge from '../dist/index.cjs'
3
+ import { concatArrays } from '../dist/index.cjs'
3
4
  import copy from 'copy-anything'
4
5
  import { isDate, isFunction, isString, isArray, isObject } from 'is-what'
5
6
 
@@ -160,7 +161,7 @@ test('Extend conversion', t => {
160
161
  })
161
162
  test('Extend concat arrays', t => {
162
163
  let res, origin, target
163
- function concatArrays (originVal, targetVal) {
164
+ function concatArr (originVal, targetVal) {
164
165
  if (isArray(originVal) && isArray(targetVal)) {
165
166
  return originVal.concat(targetVal)
166
167
  }
@@ -174,6 +175,22 @@ test('Extend concat arrays', t => {
174
175
  someArray: ['b'],
175
176
  a: {b: {c: ['y']}}
176
177
  }
178
+ res = merge({extensions: [concatArr]}, origin, target)
179
+ t.deepEqual(res, {someArray: ['a', 'b'], a: {b: {c: ['x', 'y']}}})
180
+ // also works on base lvl
181
+ res = merge({extensions: [concatArr]}, ['a'], ['b'])
182
+ t.deepEqual(res, ['a', 'b'])
183
+ })
184
+ test('import concat array extension', t => {
185
+ let res, origin, target
186
+ origin = {
187
+ someArray: ['a'],
188
+ a: {b: {c: ['x']}}
189
+ }
190
+ target = {
191
+ someArray: ['b'],
192
+ a: {b: {c: ['y']}}
193
+ }
177
194
  res = merge({extensions: [concatArrays]}, origin, target)
178
195
  t.deepEqual(res, {someArray: ['a', 'b'], a: {b: {c: ['x', 'y']}}})
179
196
  // also works on base lvl
@@ -353,3 +370,60 @@ test('works with unlimited depth', t => {
353
370
  t.deepEqual(t3, {t3: 'new'})
354
371
  t.deepEqual(t4, {t4: 'new', t3: {}})
355
372
  })
373
+
374
+ test('symbols as keys', t => {
375
+ let res, x, y
376
+ const mySymbol = Symbol('mySymbol')
377
+ x = { value: 42, [mySymbol]: 'hello' }
378
+ y = { other: 33 }
379
+ res = merge(x, y)
380
+ t.is(res.value, 42)
381
+ t.is(res.other, 33)
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'))
429
+ })
@@ -0,0 +1 @@
1
+ export declare function concatArrays(originVal: any, newVal: any): any;
package/types/index.d.ts CHANGED
@@ -1,15 +1,4 @@
1
- declare type Extension = (param1: any, param2: any) => any;
2
- interface IConfig {
3
- extensions: Extension[];
4
- }
5
- /**
6
- * Merge anything recursively.
7
- * Objects get merged, special objects (classes etc.) are re-assigned "as is".
8
- * Basic types overwrite objects or other basic types.
9
- *
10
- * @param {(IConfig | any)} origin
11
- * @param {...any[]} newComers
12
- * @returns the result
13
- */
14
- export default function (origin: IConfig | any, ...newComers: any[]): any;
15
- export {};
1
+ import merge from './merge';
2
+ import { concatArrays } from './extensions';
3
+ export { merge, concatArrays };
4
+ export default merge;
@@ -0,0 +1,15 @@
1
+ declare type Extension = (param1: any, param2: any) => any;
2
+ interface IConfig {
3
+ extensions: Extension[];
4
+ }
5
+ /**
6
+ * Merge anything recursively.
7
+ * Objects get merged, special objects (classes etc.) are re-assigned "as is".
8
+ * Basic types overwrite objects or other basic types.
9
+ *
10
+ * @param {(IConfig | any)} origin
11
+ * @param {...any[]} newComers
12
+ * @returns the result
13
+ */
14
+ export default function merge(origin: IConfig | any, ...newComers: any[]): any;
15
+ export {};