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.
Files changed (3) hide show
  1. package/index.js +153 -0
  2. package/package.json +43 -0
  3. 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 [![Build Status](https://travis-ci.org/schnittstabil/merge-options.svg?branch=master)](https://travis-ci.org/schnittstabil/merge-options) [![Coverage Status](https://coveralls.io/repos/schnittstabil/merge-options/badge.svg?branch=master&service=github)](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)