deepmerge.js (3389B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var objectKeys = require( './../../keys' ); 24 var isObject = require( '@stdlib/assert/is-object' ); 25 var hasOwnProp = require( '@stdlib/assert/has-own-property' ); 26 var isBuffer = require( '@stdlib/assert/is-buffer' ); 27 var isFunction = require( '@stdlib/assert/is-function' ); 28 var typeOf = require( './../../type-of' ); 29 var deepCopy = require( './../../copy' ); 30 31 32 // MAIN // 33 34 /** 35 * Merges a source object into a target object. 36 * 37 * @private 38 * @param {Object} target - target object 39 * @param {Object} source - source object 40 * @param {number} level - merge level 41 * @param {boolean} copy - indicates whether to perform a deep copy of merged values 42 * @param {(boolean|Function)} override - defines the merge strategy 43 * @param {boolean} extend - indicates whether new properties can be added to the target object 44 */ 45 function deepMerge( target, source, level, copy, override, extend ) { 46 var hasProp; 47 var isFunc; 48 var name; 49 var keys; 50 var curr; 51 var key; 52 var val; 53 var tmp; 54 var i; 55 56 // Determine if we were provided a custom override strategy: 57 isFunc = isFunction( override ); 58 59 // Decrement the level: 60 level -= 1; 61 62 // Loop through the source keys and implement the merge strategy... 63 keys = objectKeys( source ); 64 for ( i = 0; i < keys.length; i++ ) { 65 key = keys[ i ]; 66 hasProp = hasOwnProp( target, key ); 67 68 // Can we add new properties to the target? 69 if ( !hasProp && !extend ) { 70 continue; 71 } 72 val = source[ key ]; 73 74 if ( hasProp ) { 75 curr = target[ key ]; 76 name = typeOf( curr ); 77 78 // 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) 79 if ( 80 !isBuffer( curr ) && 81 name === 'object' && 82 isObject( val ) && 83 level 84 ) { 85 deepMerge( curr, val, level, copy, override, extend ); 86 continue; 87 } 88 // Should we apply a custom merge (override) strategy? 89 if ( isFunc ) { 90 tmp = override( curr, val, key ); 91 92 // 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. 93 if ( copy && tmp !== curr && tmp === val ) { 94 tmp = deepCopy( tmp ); 95 } 96 target[ key ] = tmp; 97 } 98 // Are we allowed to override an existing target value? 99 else if ( override ) { 100 if ( copy ) { 101 target[ key ] = deepCopy( val ); 102 } else { 103 target[ key ] = val; 104 } 105 } 106 } 107 // New property to be added to target object. Should we deep copy the source value? 108 else if ( copy ) { 109 target[ key ] = deepCopy( val ); 110 } 111 // Perform a simple assignment... 112 else { 113 target[ key ] = val; 114 } 115 } 116 } 117 118 119 // EXPORTS // 120 121 module.exports = deepMerge;