merge-options 0.0.42
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/index.js +153 -0
- package/package.json +43 -0
- package/readme.md +96 -0
package/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
var isOptionObject = require('is-plain-obj');
|
|
3
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
4
|
+
var propIsEnumerable = Object.propertyIsEnumerable;
|
|
5
|
+
var globalThis = this;
|
|
6
|
+
var defaultMergeOpts = {
|
|
7
|
+
concatArrays: false
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
function getEnumerableOwnPropertyKeys(value) {
|
|
11
|
+
var keys = [];
|
|
12
|
+
|
|
13
|
+
for (var key in value) {
|
|
14
|
+
if (hasOwnProperty.call(value, key)) {
|
|
15
|
+
keys.push(key);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (Object.getOwnPropertySymbols) {
|
|
20
|
+
var symbols = Object.getOwnPropertySymbols(value);
|
|
21
|
+
|
|
22
|
+
for (var i = 0; i < symbols.length; i++) {
|
|
23
|
+
if (propIsEnumerable.call(value, symbols[i])) {
|
|
24
|
+
keys.push(symbols[i]);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return keys;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function clone(value) {
|
|
33
|
+
if (Array.isArray(value)) {
|
|
34
|
+
return cloneArray(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isOptionObject(value)) {
|
|
38
|
+
return cloneOptionObject(value);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function cloneArray(array) {
|
|
45
|
+
var result = array.slice(0, 0);
|
|
46
|
+
|
|
47
|
+
getEnumerableOwnPropertyKeys(array).forEach(function (key) {
|
|
48
|
+
result[key] = clone(array[key]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function cloneOptionObject(obj) {
|
|
55
|
+
var result = Object.getPrototypeOf(obj) === null ? Object.create(null) : {};
|
|
56
|
+
|
|
57
|
+
getEnumerableOwnPropertyKeys(obj).forEach(function (key) {
|
|
58
|
+
result[key] = clone(obj[key]);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @param merged {already cloned}
|
|
66
|
+
* @return {cloned Object}
|
|
67
|
+
*/
|
|
68
|
+
function mergeKeys(merged, source, keys, mergeOpts) {
|
|
69
|
+
keys.forEach(function (key) {
|
|
70
|
+
if (key in merged) {
|
|
71
|
+
merged[key] = merge(merged[key], source[key], mergeOpts);
|
|
72
|
+
} else {
|
|
73
|
+
merged[key] = clone(source[key]);
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return merged;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param merged {already cloned}
|
|
82
|
+
* @return {cloned Object}
|
|
83
|
+
*
|
|
84
|
+
* see [Array.prototype.concat ( ...arguments )](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat)
|
|
85
|
+
*/
|
|
86
|
+
function concatArrays(merged, source, mergeOpts) {
|
|
87
|
+
var result = merged.slice(0, 0);
|
|
88
|
+
var resultIndex = 0;
|
|
89
|
+
|
|
90
|
+
[merged, source].forEach(function (array) {
|
|
91
|
+
var indices = [];
|
|
92
|
+
|
|
93
|
+
// result.concat(array) with cloning
|
|
94
|
+
for (var k = 0; k < array.length; k++) {
|
|
95
|
+
if (!hasOwnProperty.call(array, k)) {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
indices.push(String(k));
|
|
100
|
+
|
|
101
|
+
if (array === merged) {
|
|
102
|
+
// already cloned
|
|
103
|
+
result[resultIndex++] = array[k];
|
|
104
|
+
} else {
|
|
105
|
+
result[resultIndex++] = clone(array[k]);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// merge non-index keys
|
|
110
|
+
result = mergeKeys(result, array, getEnumerableOwnPropertyKeys(array).filter(function (key) {
|
|
111
|
+
return indices.indexOf(key) === -1;
|
|
112
|
+
}), mergeOpts);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param merged {already cloned}
|
|
120
|
+
* @return {cloned Object}
|
|
121
|
+
*/
|
|
122
|
+
function merge(merged, source, mergeOpts) {
|
|
123
|
+
if (mergeOpts.concatArrays && Array.isArray(merged) && Array.isArray(source)) {
|
|
124
|
+
return concatArrays(merged, source, mergeOpts);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (!isOptionObject(source) || !isOptionObject(merged)) {
|
|
128
|
+
return clone(source);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return mergeKeys(merged, source, getEnumerableOwnPropertyKeys(source), mergeOpts);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
module.exports = function () {
|
|
135
|
+
var mergeOpts = merge(clone(defaultMergeOpts), (this !== globalThis && this) || {}, defaultMergeOpts);
|
|
136
|
+
var merged = {};
|
|
137
|
+
|
|
138
|
+
for (var i = 0; i < arguments.length; i++) {
|
|
139
|
+
var option = arguments[i];
|
|
140
|
+
|
|
141
|
+
if (option === undefined) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!isOptionObject(option)) {
|
|
146
|
+
throw new TypeError('`' + option + '` is not an Option Object');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
merged = merge(merged, option, mergeOpts);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return merged;
|
|
153
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "merge-options",
|
|
3
|
+
"version": "0.0.42",
|
|
4
|
+
"description": "Merge Option Objects",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": "schnittstabil/merge-options",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Michael Mayer",
|
|
9
|
+
"email": "michael@schnittstabil.de",
|
|
10
|
+
"url": "schnittstabil.de"
|
|
11
|
+
},
|
|
12
|
+
"engines": {
|
|
13
|
+
"node": ">=0.10.0"
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "xo && nyc ava && nyc report --reporter=text",
|
|
17
|
+
"clean": "rimraf .nyc_output/ coverage/",
|
|
18
|
+
"coverage-html": "nyc ava && nyc report --reporter=html",
|
|
19
|
+
"coveralls": "nyc report --reporter=text-lcov | coveralls"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"index.js"
|
|
23
|
+
],
|
|
24
|
+
"keywords": [
|
|
25
|
+
"merge",
|
|
26
|
+
"options",
|
|
27
|
+
"deep",
|
|
28
|
+
"plain",
|
|
29
|
+
"object",
|
|
30
|
+
"extend",
|
|
31
|
+
"clone"
|
|
32
|
+
],
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"ava": "*",
|
|
35
|
+
"coveralls": "^2.11.4",
|
|
36
|
+
"nyc": "^3.2.2",
|
|
37
|
+
"rimraf": "^2.4.3",
|
|
38
|
+
"xo": "*"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"is-plain-obj": "^1.0.0"
|
|
42
|
+
}
|
|
43
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# merge-options [](https://travis-ci.org/schnittstabil/merge-options) [](https://coveralls.io/github/schnittstabil/merge-options?branch=master)
|
|
2
|
+
|
|
3
|
+
> Merge Option Objects
|
|
4
|
+
|
|
5
|
+
`merge-options` considers [plain objects](https://github.com/sindresorhus/is-plain-obj) as *Option Objects*, everything else as *Option Values*.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
$ npm install --save merge-options
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
const mergeOptions = require('merge-options');
|
|
17
|
+
|
|
18
|
+
mergeOptions({foo: 0}, {bar: 1}, {baz: 2}, {bar: 3})
|
|
19
|
+
//=> {foo: 0, bar: 3, baz: 2}
|
|
20
|
+
|
|
21
|
+
mergeOptions({nested: {unicorns: 'none'}}, {nested: {unicorns: 'many'}})
|
|
22
|
+
//=> {nested: {unicorns: 'many'}}
|
|
23
|
+
|
|
24
|
+
mergeOptions({[Symbol.for('key')]: 0}, {[Symbol.for('key')]: 42})
|
|
25
|
+
//=> {Symbol(key): 42}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
|
|
30
|
+
### mergeOptions(option1, ...options)<br/>mergeOptions.call(config, option1, ...options)
|
|
31
|
+
|
|
32
|
+
`mergeOptions` recursively merges one or more *Option Objects* into a new one and returns that. The `options` are merged in order, thus *Option Values* of additional `options` take precedence over previous ones.
|
|
33
|
+
|
|
34
|
+
The merging does not alter the passed `option` arguments, taking roughly the following steps:
|
|
35
|
+
* recursively cloning<sup><a href="#note1">[1]</a></sup> *Option Objects* and [arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) until reaching *Option Values*
|
|
36
|
+
* copying<sup><a href="#note1">[1]</a></sup> references to *Option Values* to the result object
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
```js
|
|
40
|
+
const defaultOpts = {
|
|
41
|
+
fn: () => false, // functions are Option Values
|
|
42
|
+
promise: Promise.reject(new Error()), // all non-plain objects are Option Values
|
|
43
|
+
array: ['foo'], // arrays are Option Values
|
|
44
|
+
nested: {unicorns: 'none'} // {…} is plain, therefore an Option Object
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const opts = {
|
|
48
|
+
fn: () => true, // [1]
|
|
49
|
+
promise: Promise.resolve('bar'), // [2]
|
|
50
|
+
array: ['baz'], // [3]
|
|
51
|
+
nested: {unicorns: 'many'} // [4]
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
mergeOptions(defaultOpts, opts)
|
|
55
|
+
//=>
|
|
56
|
+
{
|
|
57
|
+
fn: [Function], // === [1]
|
|
58
|
+
promise: Promise { 'bar' }, // === [2]
|
|
59
|
+
array: ['baz'], // !== [3] (arrays are cloned)
|
|
60
|
+
nested: {unicorns: 'many'} // !== [4] (Option Objects are cloned)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
#### config
|
|
65
|
+
|
|
66
|
+
Type: `object`
|
|
67
|
+
|
|
68
|
+
##### config.concatArrays
|
|
69
|
+
|
|
70
|
+
Type: `boolean`<br/>Default: `false`
|
|
71
|
+
|
|
72
|
+
Concatenate arrays:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
mergeOptions({src: ['src/**']}, {src: ['test/**']})
|
|
76
|
+
//=> {src: ['test/**']}
|
|
77
|
+
|
|
78
|
+
mergeOptions.call({concatArrays: true}, {src: ['src/**']}, {src: ['test/**']})
|
|
79
|
+
//=> {src: ['src/**', 'test/**']}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## Related
|
|
84
|
+
|
|
85
|
+
* See [object-assign](https://github.com/sindresorhus/object-assign) if you need a ES2015 Object.assign() ponyfill
|
|
86
|
+
* See [deep-assign](https://github.com/sindresorhus/deep-assign) if you need to do Object.assign() recursively
|
|
87
|
+
|
|
88
|
+
## Notes
|
|
89
|
+
|
|
90
|
+
<ol>
|
|
91
|
+
<li id="note1">copying and cloning take only enumerable own properties into account</li>
|
|
92
|
+
</ol>
|
|
93
|
+
|
|
94
|
+
## License
|
|
95
|
+
|
|
96
|
+
MIT © [Michael Mayer](http://schnittstabil.de)
|