/** * @license Apache-2.0 * * Copyright (c) 2018 The Stdlib Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; // MODULES // var objectKeys = require( './../../keys' ); var isObject = require( '@stdlib/assert/is-object' ); var hasOwnProp = require( '@stdlib/assert/has-own-property' ); var isBuffer = require( '@stdlib/assert/is-buffer' ); var isFunction = require( '@stdlib/assert/is-function' ); var typeOf = require( './../../type-of' ); var deepCopy = require( './../../copy' ); // MAIN // /** * Merges a source object into a target object. * * @private * @param {Object} target - target object * @param {Object} source - source object * @param {number} level - merge level * @param {boolean} copy - indicates whether to perform a deep copy of merged values * @param {(boolean|Function)} override - defines the merge strategy * @param {boolean} extend - indicates whether new properties can be added to the target object */ function deepMerge( target, source, level, copy, override, extend ) { var hasProp; var isFunc; var name; var keys; var curr; var key; var val; var tmp; var i; // Determine if we were provided a custom override strategy: isFunc = isFunction( override ); // Decrement the level: level -= 1; // Loop through the source keys and implement the merge strategy... keys = objectKeys( source ); for ( i = 0; i < keys.length; i++ ) { key = keys[ i ]; hasProp = hasOwnProp( target, key ); // Can we add new properties to the target? if ( !hasProp && !extend ) { continue; } val = source[ key ]; if ( hasProp ) { curr = target[ key ]; name = typeOf( curr ); // Should we recurse to perform a deep(er) merge? (only if both the current value and the proposed value are objects and the level is > 0) if ( !isBuffer( curr ) && name === 'object' && isObject( val ) && level ) { deepMerge( curr, val, level, copy, override, extend ); continue; } // Should we apply a custom merge (override) strategy? if ( isFunc ) { tmp = override( curr, val, key ); // WARNING: the following check does NOT prevent shared (leaky) nested references. We only check for top-level reference equality. We will assume that the user knows best, given their having provided a custom override strategy. if ( copy && tmp !== curr && tmp === val ) { tmp = deepCopy( tmp ); } target[ key ] = tmp; } // Are we allowed to override an existing target value? else if ( override ) { if ( copy ) { target[ key ] = deepCopy( val ); } else { target[ key ] = val; } } } // New property to be added to target object. Should we deep copy the source value? else if ( copy ) { target[ key ] = deepCopy( val ); } // Perform a simple assignment... else { target[ key ] = val; } } } // EXPORTS // module.exports = deepMerge;