2015-03-09 05:39:23 +00:00
|
|
|
/**
|
2017-06-17 01:11:19 +00:00
|
|
|
* Copyright (c) 2013-present, Facebook, Inc.
|
2015-03-09 05:39:23 +00:00
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2015-03-09 05:39:23 +00:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* requiresPolyfills: Array.isArray
|
|
|
|
*/
|
|
|
|
|
2017-06-17 01:11:19 +00:00
|
|
|
'use strict';
|
2015-03-09 05:39:23 +00:00
|
|
|
|
2018-05-10 22:44:52 +00:00
|
|
|
const invariant = require('fbjs/lib/invariant');
|
2015-03-09 05:39:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum number of levels to traverse. Will catch circular structures.
|
|
|
|
* @const
|
|
|
|
*/
|
2018-05-10 22:44:52 +00:00
|
|
|
const MAX_MERGE_DEPTH = 36;
|
2015-03-09 05:39:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* We won't worry about edge cases like new String('x') or new Boolean(true).
|
2017-06-17 01:11:19 +00:00
|
|
|
* Functions and Dates are considered terminals, and arrays are not.
|
2015-03-09 05:39:23 +00:00
|
|
|
* @param {*} o The item/object/value to test.
|
|
|
|
* @return {boolean} true iff the argument is a terminal.
|
|
|
|
*/
|
2018-05-10 22:44:52 +00:00
|
|
|
const isTerminal = function(o) {
|
2017-06-17 01:11:19 +00:00
|
|
|
return typeof o !== 'object' || o instanceof Date || o === null;
|
2015-03-09 05:39:23 +00:00
|
|
|
};
|
|
|
|
|
2018-05-10 22:44:52 +00:00
|
|
|
const mergeHelpers = {
|
2015-03-09 05:39:23 +00:00
|
|
|
|
|
|
|
MAX_MERGE_DEPTH: MAX_MERGE_DEPTH,
|
|
|
|
|
|
|
|
isTerminal: isTerminal,
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Converts null/undefined values into empty object.
|
|
|
|
*
|
|
|
|
* @param {?Object=} arg Argument to be normalized (nullable optional)
|
|
|
|
* @return {!Object}
|
|
|
|
*/
|
|
|
|
normalizeMergeArg: function(arg) {
|
|
|
|
return arg === undefined || arg === null ? {} : arg;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If merging Arrays, a merge strategy *must* be supplied. If not, it is
|
|
|
|
* likely the caller's fault. If this function is ever called with anything
|
|
|
|
* but `one` and `two` being `Array`s, it is the fault of the merge utilities.
|
|
|
|
*
|
|
|
|
* @param {*} one Array to merge into.
|
|
|
|
* @param {*} two Array to merge from.
|
|
|
|
*/
|
|
|
|
checkMergeArrayArgs: function(one, two) {
|
|
|
|
invariant(
|
|
|
|
Array.isArray(one) && Array.isArray(two),
|
|
|
|
'Tried to merge arrays, instead got %s and %s.',
|
|
|
|
one,
|
|
|
|
two
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*} one Object to merge into.
|
|
|
|
* @param {*} two Object to merge from.
|
|
|
|
*/
|
|
|
|
checkMergeObjectArgs: function(one, two) {
|
|
|
|
mergeHelpers.checkMergeObjectArg(one);
|
|
|
|
mergeHelpers.checkMergeObjectArg(two);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*} arg
|
|
|
|
*/
|
|
|
|
checkMergeObjectArg: function(arg) {
|
|
|
|
invariant(
|
|
|
|
!isTerminal(arg) && !Array.isArray(arg),
|
|
|
|
'Tried to merge an object, instead got %s.',
|
|
|
|
arg
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {*} arg
|
|
|
|
*/
|
|
|
|
checkMergeIntoObjectArg: function(arg) {
|
|
|
|
invariant(
|
|
|
|
(!isTerminal(arg) || typeof arg === 'function') && !Array.isArray(arg),
|
|
|
|
'Tried to merge into an object, instead got %s.',
|
|
|
|
arg
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks that a merge was not given a circular object or an object that had
|
|
|
|
* too great of depth.
|
|
|
|
*
|
|
|
|
* @param {number} Level of recursion to validate against maximum.
|
|
|
|
*/
|
|
|
|
checkMergeLevel: function(level) {
|
|
|
|
invariant(
|
|
|
|
level < MAX_MERGE_DEPTH,
|
|
|
|
'Maximum deep merge depth exceeded. You may be attempting to merge ' +
|
|
|
|
'circular structures in an unsupported way.'
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks that the supplied merge strategy is valid.
|
|
|
|
*
|
|
|
|
* @param {string} Array merge strategy.
|
|
|
|
*/
|
|
|
|
checkArrayStrategy: function(strategy) {
|
|
|
|
invariant(
|
|
|
|
strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
|
|
|
|
'You must provide an array strategy to deep merge functions to ' +
|
|
|
|
'instruct the deep merge how to resolve merging two arrays.'
|
|
|
|
);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set of possible behaviors of merge algorithms when encountering two Arrays
|
|
|
|
* that must be merged together.
|
|
|
|
* - `clobber`: The left `Array` is ignored.
|
|
|
|
* - `indexByIndex`: The result is achieved by recursively deep merging at
|
|
|
|
* each index. (not yet supported.)
|
|
|
|
*/
|
2017-06-17 01:11:19 +00:00
|
|
|
ArrayStrategies: {
|
|
|
|
Clobber: 'Clobber',
|
|
|
|
Concat: 'Concat',
|
|
|
|
IndexByIndex: 'IndexByIndex',
|
|
|
|
},
|
2015-03-09 05:39:23 +00:00
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = mergeHelpers;
|