Skip to content

Commit

Permalink
Allow falsey values to be merged
Browse files Browse the repository at this point in the history
Fixes #170
  • Loading branch information
TehShrike committed Oct 21, 2019
1 parent 5063a96 commit 3c39fb3
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 10 deletions.
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# [4.2.1](https://github.com/TehShrike/deepmerge/releases/tag/v4.2.1)

- Fix: falsey values can now be merged. [#170](https://github.com/TehShrike/deepmerge/issues/170)

# [4.2.0](https://github.com/TehShrike/deepmerge/releases/tag/v4.2.0)

- Properties are now only overwritten if they exist on the target object and are enumerable. [#164](https://github.com/TehShrike/deepmerge/pull/164)
Expand Down
20 changes: 11 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,21 @@ function getKeys(target) {
return Object.keys(target).concat(getEnumerableOwnPropertySymbols(target))
}

// Protects from prototype poisoning and unexpected merging up the prototype chain.
function propertyIsUnsafe(target, key) {
function propertyIsOnObject(object, property) {
try {
return (key in target) // Properties are safe to merge if they don't exist in the target yet,
&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
} catch (unused) {
// Counterintuitively, it's safe to merge any property on a target that causes the `in` operator to throw.
// This happens when trying to copy an object in the source over a plain string in the target.
return property in object
} catch(_) {
return false
}
}

// Protects from prototype poisoning and unexpected merging up the prototype chain.
function propertyIsUnsafe(target, key) {
return propertyIsOnObject(target, key) // Properties are safe to merge if they don't exist in the target yet,
&& !(Object.hasOwnProperty.call(target, key) // unsafe if they exist up the prototype chain,
&& Object.propertyIsEnumerable.call(target, key)) // and also unsafe if they're nonenumerable.
}

function mergeObject(target, source, options) {
var destination = {}
if (options.isMergeableObject(target)) {
Expand All @@ -61,7 +63,7 @@ function mergeObject(target, source, options) {
return
}

if (!options.isMergeableObject(source[key]) || !target[key]) {
if (!options.isMergeableObject(source[key]) || !propertyIsOnObject(target, key)) {
destination[key] = cloneUnlessOtherwiseSpecified(source[key], options)
} else {
destination[key] = getMergeFunction(key, options)(target[key], source[key], options)
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Merges the enumerable properties of two or more objects deeply.

> UMD bundle is 667B minified+gzipped
> UMD bundle is 723B minified+gzipped
## Getting Started

Expand Down
30 changes: 30 additions & 0 deletions test/merge.js
Original file line number Diff line number Diff line change
Expand Up @@ -637,3 +637,33 @@ test('copy symbol keys in target that do exist on the target', function(t) {
t.equal(res[mySymbol], 'value1')
t.end()
})

test('Falsey properties should be mergeable', function(t) {
var uniqueValue = {}

var target = {
wat: false
}

var source = {
wat: false
}

var customMergeWasCalled = false

var result = merge(target, source, {
isMergeableObject: function() {
return true
},
customMerge: function() {
return function() {
customMergeWasCalled = true
return uniqueValue
}
}
})

t.equal(result.wat, uniqueValue)
t.ok(customMergeWasCalled, 'custom merge function was called')
t.end()
})

0 comments on commit 3c39fb3

Please sign in to comment.