diff options
Diffstat (limited to 'node_modules/merge-options')
| -rw-r--r-- | node_modules/merge-options/index.js | 163 | ||||
| -rw-r--r-- | node_modules/merge-options/license | 21 | ||||
| -rw-r--r-- | node_modules/merge-options/package.json | 42 | ||||
| -rw-r--r-- | node_modules/merge-options/readme.md | 102 |
4 files changed, 328 insertions, 0 deletions
diff --git a/node_modules/merge-options/index.js b/node_modules/merge-options/index.js new file mode 100644 index 00000000..8eb954de --- /dev/null +++ b/node_modules/merge-options/index.js @@ -0,0 +1,163 @@ +'use strict'; +const isOptionObject = require('is-plain-obj'); + +const hasOwnProperty = Object.prototype.hasOwnProperty; +const propIsEnumerable = Object.propertyIsEnumerable; +const defineProperty = (obj, name, value) => Object.defineProperty(obj, name, { + value, + writable: true, + enumerable: true, + configurable: true +}); + +const globalThis = this; +const defaultMergeOpts = { + concatArrays: false +}; + +const getEnumerableOwnPropertyKeys = value => { + const keys = []; + + for (const key in value) { + if (hasOwnProperty.call(value, key)) { + keys.push(key); + } + } + + /* istanbul ignore else */ + if (Object.getOwnPropertySymbols) { + const symbols = Object.getOwnPropertySymbols(value); + + for (let i = 0; i < symbols.length; i++) { + if (propIsEnumerable.call(value, symbols[i])) { + keys.push(symbols[i]); + } + } + } + + return keys; +}; + +function clone(value) { + if (Array.isArray(value)) { + return cloneArray(value); + } + + if (isOptionObject(value)) { + return cloneOptionObject(value); + } + + return value; +} + +function cloneArray(array) { + const result = array.slice(0, 0); + + getEnumerableOwnPropertyKeys(array).forEach(key => { + defineProperty(result, key, clone(array[key])); + }); + + return result; +} + +function cloneOptionObject(obj) { + const result = Object.getPrototypeOf(obj) === null ? Object.create(null) : {}; + + getEnumerableOwnPropertyKeys(obj).forEach(key => { + defineProperty(result, key, clone(obj[key])); + }); + + return result; +} + +/** + * @param merged {already cloned} + * @return {cloned Object} + */ +const mergeKeys = (merged, source, keys, mergeOpts) => { + keys.forEach(key => { + // Do not recurse into prototype chain of merged + if (key in merged && merged[key] !== Object.getPrototypeOf(merged)) { + defineProperty(merged, key, merge(merged[key], source[key], mergeOpts)); + } else { + defineProperty(merged, key, clone(source[key])); + } + }); + + return merged; +}; + +/** + * @param merged {already cloned} + * @return {cloned Object} + * + * see [Array.prototype.concat ( ...arguments )](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.concat) + */ +const concatArrays = (merged, source, mergeOpts) => { + let result = merged.slice(0, 0); + let resultIndex = 0; + + [merged, source].forEach(array => { + const indices = []; + + // `result.concat(array)` with cloning + for (let k = 0; k < array.length; k++) { + if (!hasOwnProperty.call(array, k)) { + continue; + } + + indices.push(String(k)); + + if (array === merged) { + // Already cloned + defineProperty(result, resultIndex++, array[k]); + } else { + defineProperty(result, resultIndex++, clone(array[k])); + } + } + + // Merge non-index keys + result = mergeKeys(result, array, getEnumerableOwnPropertyKeys(array).filter(key => { + return indices.indexOf(key) === -1; + }), mergeOpts); + }); + + return result; +}; + +/** + * @param merged {already cloned} + * @return {cloned Object} + */ +function merge(merged, source, mergeOpts) { + if (mergeOpts.concatArrays && Array.isArray(merged) && Array.isArray(source)) { + return concatArrays(merged, source, mergeOpts); + } + + if (!isOptionObject(source) || !isOptionObject(merged)) { + return clone(source); + } + + return mergeKeys(merged, source, getEnumerableOwnPropertyKeys(source), mergeOpts); +} + +module.exports = function () { + const mergeOpts = merge(clone(defaultMergeOpts), (this !== globalThis && this) || {}, defaultMergeOpts); + let merged = {foobar: {}}; + + for (let i = 0; i < arguments.length; i++) { + const option = arguments[i]; + + if (option === undefined) { + continue; + } + + if (!isOptionObject(option)) { + throw new TypeError('`' + option + '` is not an Option Object'); + } + + merged = merge(merged, {foobar: option}, mergeOpts); + } + + return merged.foobar; +}; diff --git a/node_modules/merge-options/license b/node_modules/merge-options/license new file mode 100644 index 00000000..527cbd86 --- /dev/null +++ b/node_modules/merge-options/license @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016-2018 Michael Mayer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/merge-options/package.json b/node_modules/merge-options/package.json new file mode 100644 index 00000000..85a57174 --- /dev/null +++ b/node_modules/merge-options/package.json @@ -0,0 +1,42 @@ +{ + "name": "merge-options", + "version": "1.0.1", + "description": "Merge Option Objects", + "license": "MIT", + "repository": "schnittstabil/merge-options", + "author": { + "name": "Michael Mayer", + "email": "michael@schnittstabil.de", + "url": "schnittstabil.de" + }, + "engines": { + "node": ">=4" + }, + "scripts": { + "test": "xo && nyc ava", + "clean": "rimraf .nyc_output/ coverage/", + "coverage-html": "nyc ava && nyc report --reporter=html" + }, + "files": [ + "index.js" + ], + "keywords": [ + "merge", + "options", + "deep", + "plain", + "object", + "extend", + "clone" + ], + "devDependencies": { + "ava": "^0.25", + "coveralls": "^3.0", + "nyc": "^11.7", + "rimraf": "^2.5", + "xo": "^0.20" + }, + "dependencies": { + "is-plain-obj": "^1.1" + } +} diff --git a/node_modules/merge-options/readme.md b/node_modules/merge-options/readme.md new file mode 100644 index 00000000..c9c07c87 --- /dev/null +++ b/node_modules/merge-options/readme.md @@ -0,0 +1,102 @@ +# merge-options [](https://travis-ci.org/schnittstabil/merge-options) [](https://coveralls.io/github/schnittstabil/merge-options?branch=master) [](https://github.com/sindresorhus/xo) + + +> Merge Option Objects + +`merge-options` considers [plain objects](https://github.com/sindresorhus/is-plain-obj) as *Option Objects*, everything else as *Option Values*. + +## Install + +``` +$ npm install --save merge-options +``` + +## Usage + +```js +const mergeOptions = require('merge-options'); + +mergeOptions({foo: 0}, {bar: 1}, {baz: 2}, {bar: 3}) +//=> {foo: 0, bar: 3, baz: 2} + +mergeOptions({nested: {unicorns: 'none'}}, {nested: {unicorns: 'many'}}) +//=> {nested: {unicorns: 'many'}} + +mergeOptions({[Symbol.for('key')]: 0}, {[Symbol.for('key')]: 42}) +//=> {Symbol(key): 42} +``` + +## API + +### mergeOptions(option1, ...options)<br/>mergeOptions.call(config, option1, ...options)<br/>mergeOptions.apply(config, [option1, ...options]) + +`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. + +The merging does not alter the passed `option` arguments, taking roughly the following steps: +* 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* +* copying<sup><a href="#note1">[1]</a></sup> references to *Option Values* to the result object + + +```js +const defaultOpts = { + fn: () => false, // functions are Option Values + promise: Promise.reject(new Error()), // all non-plain objects are Option Values + array: ['foo'], // arrays are Option Values + nested: {unicorns: 'none'} // {…} is plain, therefore an Option Object +}; + +const opts = { + fn: () => true, // [1] + promise: Promise.resolve('bar'), // [2] + array: ['baz'], // [3] + nested: {unicorns: 'many'} // [4] +}; + +mergeOptions(defaultOpts, opts) +//=> +{ + fn: [Function], // === [1] + promise: Promise { 'bar' }, // === [2] + array: ['baz'], // !== [3] (arrays are cloned) + nested: {unicorns: 'many'} // !== [4] (Option Objects are cloned) +} +``` + +#### config + +Type: `object` + +##### config.concatArrays + +Type: `boolean`<br/>Default: `false` + +Concatenate arrays: + +```js +mergeOptions({src: ['src/**']}, {src: ['test/**']}) +//=> {src: ['test/**']} + +// Via call +mergeOptions.call({concatArrays: true}, {src: ['src/**']}, {src: ['test/**']}) +//=> {src: ['src/**', 'test/**']} + +// Via apply +mergeOptions.apply({concatArrays: true}, [{src: ['src/**']}, {src: ['test/**']}]) +//=> {src: ['src/**', 'test/**']} +``` + + +## Related + +* See [object-assign](https://github.com/sindresorhus/object-assign) if you need a ES2015 Object.assign() ponyfill +* See [deep-assign](https://github.com/sindresorhus/deep-assign) if you need to do Object.assign() recursively + +## Notes + +<ol> + <li id="note1">copying and cloning take only enumerable own properties into account</li> +</ol> + +## License + +MIT © [Michael Mayer](http://schnittstabil.de) |
