UNPKG

safe-merge

Version:
127 lines (106 loc) 2.74 kB
function complex(o) { return !(o instanceof RegExp) && (o && (typeof(o) === 'object')); } function taint(source) { Object.defineProperty(source, '__visited', {value: true, enumerable: false, configurable: true}); } function untaint(source) { delete source.__visited; } function recopy(input) { var ptn = input.source , flags = ""; if(input.global) { flags += "g"; } if(input.ignoreCase) { flags += "i"; } if(input.multiline) { flags += "m"; } return new RegExp(ptn, flags); } function create(source, ...params) { if(source && typeof(source.clone) === 'function') { return source.clone(); }else if((source instanceof RegExp)) { return recopy(source); }else if(complex(source) && source.constructor && (source.constructor.name !== 'Object' && source.constructor.name !== 'Array')) { return source; }else if(Array.isArray(source)) { return source.slice(0); }else if(complex(source)) { return Object.assign.apply(null, [{}, source].concat(params)); } // simple type return source; } function merge(source, ...inputs) { // not a complex object - nothing to be done if(!complex(source)) { return source; } // we always create a copy const output = create(source); function recurse(source, output, key) { let val = source[key]; if(complex(val)) { if(val.__visited) { throw new Error( `cyclical reference detected on ${key}, cannot merge`); } let copy; if(complex(output[key])) { copy = create(val, output[key]); }else{ copy = create(val); } // if(Array.isArray(output[key]) && Array.isArray(val)) { // output[key] = output[key].concat(loop(val, copy)); // }else{ output[key] = loop(val, copy); // } }else{ output[key] = create(val); } } function iterate(source, output, key) { taint(source); try { recurse(source, output, key); }catch(e) { throw e; }finally { untaint(source); } } function loop(input, output) { // NOTE: input order when iterating arrays with for...in is not guaranteed // NOTE: however in practice it appears implementations do it in order for(let k in input) { // only merge own properties if(!input.hasOwnProperty(k)) { continue; } iterate(input, output, k); } return output; } // you can merge multiple objects with the source let input, i; for(i = 0;i < inputs.length;i++) { input = inputs[i]; // input is not a complex object, ignore it if(!complex(input)) { continue; } loop(input, output); } return output; } module.exports = merge;