Skip to content

Commit

Permalink
[Fix] utils: merge: fix crash when source is a truthy primitive…
Browse files Browse the repository at this point in the history
… & no options are provided
  • Loading branch information
ljharb committed Sep 17, 2018
1 parent 42de207 commit 4c8dcaf
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 10 deletions.
50 changes: 40 additions & 10 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ var hexTable = (function () {
var has = Object.prototype.hasOwnProperty;

exports.arrayToObject = function (source, options) {
var obj = options.plainObjects ? Object.create(null) : {};
var obj = options && options.plainObjects ? Object.create(null) : {};
for (var i = 0; i < source.length; ++i) {
if (typeof source[i] !== 'undefined') {
obj[i] = source[i];
Expand All @@ -22,16 +22,30 @@ exports.arrayToObject = function (source, options) {
return obj;
};

exports.merge = function (target, source, options) {
var isArray = Array.isArray;

var arrayToObject = function arrayToObject(source, options) {
var obj = options && options.plainObjects ? Object.create(null) : {};
for (var i = 0; i < source.length; ++i) {
if (typeof source[i] !== 'undefined') {
obj[i] = source[i];
}
}

return obj;
};

exports.merge = function merge(target, source, options) {
/* eslint no-param-reassign: 0 */
if (!source) {
return target;
}

if (typeof source !== 'object') {
if (Array.isArray(target)) {
if (isArray(target)) {
target.push(source);
} else if (typeof target === 'object') {
if (options.plainObjects || options.allowPrototypes || !has.call(Object.prototype, source)) {
} else if (target && typeof target === 'object') {
if ((options && (options.plainObjects || options.allowPrototypes)) || !has.call(Object.prototype, source)) {
target[source] = true;
}
} else {
Expand All @@ -41,20 +55,36 @@ exports.merge = function (target, source, options) {
return target;
}

if (typeof target !== 'object') {
if (!target || typeof target !== 'object') {
return [target].concat(source);
}

var mergeTarget = target;
if (Array.isArray(target) && !Array.isArray(source)) {
mergeTarget = exports.arrayToObject(target, options);
if (isArray(target) && !isArray(source)) {
mergeTarget = arrayToObject(target, options);
}

if (isArray(target) && isArray(source)) {
source.forEach(function (item, i) {
if (has.call(target, i)) {
var targetItem = target[i];
if (targetItem && typeof targetItem === 'object' && item && typeof item === 'object') {
target[i] = merge(targetItem, item, options);
} else {
target.push(item);
}
} else {
target[i] = item;
}
});
return target;
}

return Object.keys(source).reduce(function (acc, key) {
var value = source[key];

if (has.call(acc, key)) {
acc[key] = exports.merge(acc[key], value, options);
acc[key] = merge(acc[key], value, options);
} else {
acc[key] = value;
}
Expand Down Expand Up @@ -132,7 +162,7 @@ exports.compact = function (obj, references) {

refs.push(obj);

if (Array.isArray(obj)) {
if (isArray(obj)) {
var compacted = [];

for (var i = 0; i < obj.length; ++i) {
Expand Down
16 changes: 16 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,21 @@ var utils = require('../lib/utils');

test('merge()', function (t) {
t.deepEqual(utils.merge({ a: 'b' }, { a: 'c' }), { a: ['b', 'c'] }, 'merges two objects with the same key');

var oneMerged = utils.merge({ foo: 'bar' }, { foo: { first: '123' } });
t.deepEqual(oneMerged, { foo: ['bar', { first: '123' }] }, 'merges a standalone and an object into an array');

var twoMerged = utils.merge({ foo: ['bar', { first: '123' }] }, { foo: { second: '456' } });
t.deepEqual(twoMerged, { foo: { 0: 'bar', 1: { first: '123' }, second: '456' } }, 'merges a standalone and two objects into an array');

var sandwiched = utils.merge({ foo: ['bar', { first: '123', second: '456' }] }, { foo: 'baz' });
t.deepEqual(sandwiched, { foo: ['bar', { first: '123', second: '456' }, 'baz'] }, 'merges an object sandwiched by two standalones into an array');

var nestedArrays = utils.merge({ foo: ['baz'] }, { foo: ['bar', 'xyzzy'] });
t.deepEqual(nestedArrays, { foo: ['baz', 'bar', 'xyzzy'] });

var noOptionsNonObjectSource = utils.merge({ foo: 'baz' }, 'bar');
t.deepEqual(noOptionsNonObjectSource, { foo: 'baz', bar: true });

t.end();
});

0 comments on commit 4c8dcaf

Please sign in to comment.