123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- '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;
- };
|