├── .gitignore ├── LICENSE.md ├── README.md ├── bower.json ├── component.json ├── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Released under the MIT License: 2 | 3 | implementation: Copyright (C) 2012-2013 Kurt Milam - http://xioup.com 4 | - source: https://gist.github.com/1868955NPM 5 | packaging: Copyright (C) 2012-2014 Pierre-Yves Gérardy 6 | - github: https://github.com/pygy/underscodeDeepExtend 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to 10 | deal in the Software without restriction, including without limitation the 11 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | sell copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | IN THE SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # _.deepExtend · [![Gitter](https://img.shields.io/gitter/room/xioup/chat.js.svg)](https://gitter.im/xioup/chat) [![GitHub stars](https://img.shields.io/github/stars/xioup/underscoredeepextend.svg?style=social)](https://github.com/xioup/underscoredeepextend) [![npm](https://img.shields.io/npm/dt/underscore-deep-extend.svg)](https://www.npmjs.com/package/underscore-deep-extend) 2 | 3 | A deep extend implementation with added sugar for [underscore](http://underscorejs.org/), [lodash](http://lodash.com/) and friends. 4 | 5 | Based conceptually on the [_.extend()](http://underscorejs.org/#extend) function in underscore.js. 6 | 7 | [![npm version](https://badge.fury.io/js/underscore-deep-extend.svg)](http://badge.fury.io/js/underscore-deep-extend) 8 | [![Bower version](https://badge.fury.io/bo/underscore-deep-extend.svg)](https://badge.fury.io/bo/underscore-deep-extend) 9 | [![](https://david-dm.org/xioup/underscoredeepextend.svg)](https://david-dm.org/xioup/underscoredeepextend) 10 | [![](https://david-dm.org/xioup/underscoredeepextend/dev-status.svg)](https://david-dm.org/xioup/underscoredeepextend?type=dev) 11 | 12 | ## Installation: 13 | 14 | npm install underscore-deep-extend 15 | 16 | ## Dependency: 17 | 18 | One of 19 | 20 | - [underscore.js](http://underscorejs.org/) 21 | - [lodash.js](http://lodash.com/) 22 | - another clone that provides `_.each`, `_.is(Array|Date|Null|Object|String|Undefined)`, `_.clone` and `_.reject`. 23 | 24 | ## Usage: 25 | 26 | Load it, either as is (in the browser), as an AMD module, or as a CommonJS/Node.js module), then mix it in with the parent library (which must be explicitly injected): 27 | 28 | _.mixin( { deepExtend: underscoreDeepExtend( _ ) } ) 29 | 30 | Call it like this: 31 | 32 | const myObj = _.deepExtend( grandparent, child, grandchild, greatgrandchild ) 33 | 34 | Or this: 35 | 36 | _.deepExtend( myObj, child, grandchild, greatgrandchild ) 37 | 38 | ## Notes 39 | 40 | ### Keep it DRY. 41 | 42 | This function is especially useful if you're working with JSON config documents. It allows you to create a default 43 | config document with the most common settings, then override those settings for specific cases. It accepts any 44 | number of objects as arguments, giving you fine-grained control over your config document hierarchy. 45 | 46 | ### Special Features and Considerations: 47 | 48 | - parentRE allows you to concatenate strings. example: 49 | 50 | ``` Javascript 51 | const obj = _.deepExtend( { url: "www.example.com" } 52 | , { url: "http://#{_}/path/to/file.html" } 53 | ) 54 | 55 | console.log( obj.url ) 56 | ``` 57 | output: `http://www.example.com/path/to/file.html` 58 | 59 | - parentRE also acts as a placeholder, which can be useful when you need to change one value in an array, while 60 | leaving the others untouched. example: 61 | 62 | ``` Javascript 63 | const arr = _.deepExtend( [ 100, { id: 1234 }, true, "foo", [ 250, 500] ] 64 | , [ "#{_}", "#{_}", false, "#{_}", "#{_}" ] 65 | ) 66 | console.log( arr ) 67 | ``` 68 | output: `[ 100, { id: 1234 }, false, "foo", [ 250, 500 ] ]` 69 | 70 | - The previous example can also be written like this: 71 | 72 | ``` Javascript 73 | const arr = _.deepExtend( [ 100, { id: 1234 }, true, "foo", [ 250, 500 ] ] 74 | , [ "#{_}", {}, false, "#{_}", [ ] ] 75 | ) 76 | console.log( arr ) 77 | ``` 78 | output: `[ 100, { id: 1234 }, false, "foo", [ 250, 500 ] ]` 79 | 80 | - And also like this: 81 | 82 | ``` Javascript 83 | const arr = _.deepExtend( [ 100, { id: 1234 }, true, "foo", [ 250, 500 ] ] 84 | , [ "#{_}", {}, false ] 85 | ) 86 | console.log( arr ) 87 | ``` 88 | output: `[ 100, { id: 1234 }, false, "foo", [ 250, 500 ] ]` 89 | 90 | - Array order is important. example: 91 | 92 | ``` Javascript 93 | const arr = _.deepExtend( [ 1, 2, 3, 4 ] 94 | , [ 1, 4, 3, 2 ] 95 | ) 96 | 97 | console.log( arr ) 98 | ``` 99 | output: `[ 1, 4, 3, 2 ]` 100 | 101 | 102 | - You can remove an array element set in a parent object by setting the same index value to null in a child object. Example: 103 | 104 | ``` Javascript 105 | const obj = _.deepExtend( { arr: [ 1, 2, 3, 4 ] } 106 | , { arr: [ "#{_}", null ] } 107 | ) 108 | 109 | console.log( obj.arr ) 110 | ``` 111 | output: `[ 1, 3, 4 ]` 112 | 113 | ## License 114 | 115 | Copyright (C) 2012-2017 Kurt Milam - http://xioup.com 116 | 117 | License: MIT 118 | 119 | Original source: https://gist.github.com/1868955 120 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "underscore-deep-extend", 3 | "version": "1.1.5", 4 | "main": "index.js", 5 | "repository": { 6 | "type": "git", 7 | "url": "git://github.com/kurtmilam/underscoreDeepExtend.git" 8 | }, 9 | "ignore": [ 10 | ".gitignore", 11 | "component.json", 12 | "package.json", 13 | "README.md" 14 | ] 15 | } -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "underscore-deep-extend", 3 | "version": "1.1.5", 4 | "repo": "kurtmilam/underscoreDeepExtend", 5 | "description": "A deepExtend implementation for underscore, lodash and friends.", 6 | "keywords": ["underscore", "lodash", "deep extend"], 7 | "scripts": ["index.js"], 8 | "license": "MIT" 9 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /* implementation: Copyright (C) 2012-2016 Kurt Milam - http://xioup.com | Source: https://gist.github.com/1868955 2 | * NPM packaging: Copyright (C) 2012-2016 Pierre-Yves Gérardy | https://github.com/pygy/underscoreDeepExtend 3 | * 4 | * 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: 5 | * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | * 7 | * 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. 8 | **/ 9 | 10 | ( // Module boilerplate to support browser globals, node.js and AMD. 11 | (typeof module !== "undefined" && function (m) { module.exports = m(); }) || 12 | (typeof define === "function" && function (m) { define('underscoreDeepExtend', m); }) || 13 | (function (m) { window['underscoreDeepExtend'] = m(); }) 14 | )(function () { return function(_) { 15 | 16 | return function underscoreDeepExtend(obj) { 17 | var parentRE = /#{\s*?_\s*?}/, 18 | source, 19 | 20 | isAssign = function (oProp, sProp) { 21 | return (_.isUndefined(oProp) || _.isNull(oProp) || _.isFunction(oProp) || _.isNull(sProp) || _.isDate(sProp)); 22 | }, 23 | 24 | procAssign = function (oProp, sProp, propName) { 25 | // Perform a straight assignment 26 | // Assign for object properties & return for array members 27 | return obj[propName] = _.clone(sProp); 28 | }, 29 | 30 | hasRegex = function (oProp, sProp) { 31 | return ( _.isString(sProp) && parentRE.test(sProp) ); 32 | }, 33 | 34 | procRegex = function (oProp, sProp, propName) { 35 | // Perform a string.replace using parentRE if oProp is a string 36 | if (!_.isString(oProp)) { 37 | // We're being optimistic at the moment 38 | // throw new Error('Trying to combine a string with a non-string (' + propName + ')'); 39 | } 40 | // Assign for object properties & return for array members 41 | return obj[propName] = sProp.replace(parentRE, oProp); 42 | }, 43 | 44 | hasArray = function (oProp, sProp) { 45 | return (_.isArray(oProp) || _.isArray(sProp)); 46 | }, 47 | 48 | procArray = function (oProp, sProp, propName) { 49 | // extend oProp if both properties are arrays 50 | if (!_.isArray(oProp) || !_.isArray(sProp)){ 51 | throw new Error('Trying to combine an array with a non-array (' + propName + ')'); 52 | } 53 | var tmp = _.deepExtend(obj[propName], sProp); 54 | // Assign for object properties & return for array members 55 | return obj[propName] = _.reject(tmp, _.isNull); 56 | }, 57 | 58 | hasObject = function (oProp, sProp) { 59 | return (_.isObject(oProp) || _.isObject(sProp)); 60 | }, 61 | 62 | procObject = function (oProp, sProp, propName) { 63 | // extend oProp if both properties are objects 64 | if (!_.isObject(oProp) || !_.isObject(sProp)){ 65 | throw new Error('Trying to combine an object with a non-object (' + propName + ')'); 66 | } 67 | // Assign for object properties & return for array members 68 | return obj[propName] = _.deepExtend(oProp, sProp); 69 | }, 70 | 71 | procMain = function(propName) { 72 | var oProp = obj[propName], 73 | sProp = source[propName]; 74 | 75 | // The order of the 'if' statements is critical 76 | 77 | // Cases in which we want to perform a straight assignment 78 | if ( isAssign(oProp, sProp) ) { 79 | procAssign(oProp, sProp, propName); 80 | } 81 | // sProp is a string that contains parentRE 82 | else if ( hasRegex(oProp, sProp) ) { 83 | procRegex(oProp, sProp, propName); 84 | } 85 | // At least one property is an array 86 | else if ( hasArray(oProp, sProp) ){ 87 | procArray(oProp, sProp, propName); 88 | } 89 | // At least one property is an object 90 | else if ( hasObject(oProp, sProp) ){ 91 | procObject(oProp, sProp, propName); 92 | } 93 | // Everything else 94 | else { 95 | // Let's be optimistic and perform a straight assignment 96 | procAssign(oProp, sProp, propName); 97 | } 98 | }, 99 | 100 | procAll = function(src) { 101 | source = src; 102 | Object.keys(source).forEach(procMain); 103 | }; 104 | 105 | _.each(Array.prototype.slice.call(arguments, 1), procAll); 106 | 107 | return obj; 108 | }; 109 | 110 | }; 111 | }); 112 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "underscore-deep-extend", 3 | "description": "A deepExtend implementation for underscore, lodash and friends.", 4 | "keywords": [ 5 | "underscore", 6 | "lodash", 7 | "deep extend" 8 | ], 9 | "author": { 10 | "name": "Kurt Milam", 11 | "url": "http://xioup.com" 12 | }, 13 | "contributors": [ 14 | { 15 | "name": "Pierre-Yves Gérardy", 16 | "url": "https://github.com/pygy" 17 | }, 18 | { 19 | "name": "Kurt Milam", 20 | "url": "https://github.com/kurtmilam" 21 | } 22 | ], 23 | "homepage": "https://github.com/kurtmilam/underscoreDeepExtend", 24 | "bugs": { 25 | "url": "https://github.com/kurtmilam/underscoreDeepExtend/issues" 26 | }, 27 | "license": "MIT", 28 | "version": "1.1.5", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/kurtmilam/underscoreDeepExtend.git" 32 | }, 33 | "main": "index" 34 | } 35 | --------------------------------------------------------------------------------