Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow falsey values to be merged #172

Merged
merged 1 commit into from
Oct 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
})