Skip to content
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
3 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
env:
node: true
mocha: true

rules:
# Possible Errors
Expand Down Expand Up @@ -45,7 +46,7 @@ rules:
## require parens for Constructor
new-parens: 2
## max 80 length
max-len: [2, 80, 2]
max-len: [2, 120, 2]
## max 2 consecutive empty lines
no-multiple-empty-lines: [2, {max: 2}]
## require newline at end of files
Expand Down
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ $ npm install json-merge-patch --save

## Usage

Applying patches:
### Applying patches:
```js
var source = {
"title": "Goodbye!",
Expand All @@ -53,7 +53,7 @@ var target = jsonmergepatch.apply(source, patch);
// }
```

Generating patches:
### Generating patches:
```js
var source = {
"title": "Goodbye!",
Expand All @@ -72,6 +72,41 @@ var patch = jsonmergepatch.generate(source, target);
// }
```


### Usage with Javascript objects

This library is primarily designed to work with JSON.
Nonetheless, it is possible to use Javascript objects if the method `toJSON()` is implemented, the library will then serialize your object using it.
```js
var patch = jsonmergepatch.generate(
{
"title": "Goodbye!"
},
{
toJSON: () {
return {
"title": "I am serialized"
}
},
}
);
// patch = {
// "title": "I am serialized",
// }
```

```js
var patch = jsonmergepatch.generate(
{},
{
date: new Date("2020-05-09T00:00:00.000")
}
);
// patch = {
// date: "2020-05-09T00:00:00.000"
// }
```

## API

#### jsonmergepatch.apply (`obj` Object, `patch` Object) : Object
Expand All @@ -87,10 +122,11 @@ Generates a `patch` Object from source and target Object.

Generates a `patch` Object by merging patch1 and patch2.


## Running tests

```sh
make test
npm test
```

# License
Expand Down
5 changes: 5 additions & 0 deletions lib/apply.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
'use strict';

var serialize = require('./utils').serialize;

module.exports = function apply(target, patch) {
patch = serialize(patch);
if (patch === null || typeof patch !== 'object' || Array.isArray(patch)) {
return patch;
}

target = serialize(target);
if (target === null || typeof target !== 'object' || Array.isArray(target)) {
target = {};
}
Expand Down
8 changes: 6 additions & 2 deletions lib/generate.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

var equal = require('deep-equal');
var serialize = require('./utils').serialize;

function arrayEquals(before, after) {
if (before.length !== after.length) {
Expand All @@ -15,6 +16,9 @@ function arrayEquals(before, after) {
}

module.exports = function generate(before, after) {
before = serialize(before);
after = serialize(after);

if (before === null || after === null ||
typeof before !== 'object' || typeof after !== 'object' ||
Array.isArray(before) !== Array.isArray(after)) {
Expand All @@ -40,7 +44,7 @@ module.exports = function generate(before, after) {
key = afterKeys[i];
if (beforeKeys.indexOf(key) === -1) {
newKeys[key] = true;
patch[key] = after[key];
patch[key] = serialize(after[key]);
}
}

Expand All @@ -58,7 +62,7 @@ module.exports = function generate(before, after) {
patch[key] = subPatch;
}
} else if (before[key] !== after[key]) {
patch[key] = after[key];
patch[key] = serialize(after[key]);
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

module.exports.serialize = function(value) {
return (value && typeof value.toJSON === 'function') ? value.toJSON() : value;
};
143 changes: 75 additions & 68 deletions test/lib/apply.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,108 +5,115 @@ var assert = require('chai').assert;
var apply = require('../../lib/apply');

describe('apply', function() {
it('should replace an attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {a: 'c'}),
it('should replace an attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {a: 'c'}),
{a: 'c'}
);
});
});

it('should add an attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {b: 'c'}),
it('should add an attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {b: 'c'}),
{a: 'b', b: 'c'}
);
});
});

it('should delete attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {a: null}),
it('should delete attribute', function() {
assert.deepEqual(
apply({a: 'b'}, {a: null}),
{}
);
});
});

it('should delete attribute without affecting others', function() {
assert.deepEqual(
apply({a: 'b', b: 'c'}, {a: null}),
it('should delete attribute without affecting others', function() {
assert.deepEqual(
apply({a: 'b', b: 'c'}, {a: null}),
{b: 'c'}
);
});
});

it('should replace array with a string', function() {
assert.deepEqual(
apply({a: ['b']}, {a: 'c'}),
it('should replace array with a string', function() {
assert.deepEqual(
apply({a: ['b']}, {a: 'c'}),
{a: 'c'}
);
});
});

it('should replace an string with an array', function() {
assert.deepEqual(
apply({a: 'c'}, {a: ['b']}),
it('should replace an string with an array', function() {
assert.deepEqual(
apply({a: 'c'}, {a: ['b']}),
{a: ['b']}
);
});
});

it('should apply recursively', function() {
assert.deepEqual(
apply({a: {b: 'c'}}, {a: {b: 'd', c: null}}),
it('should apply recursively', function() {
assert.deepEqual(
apply({a: {b: 'c'}}, {a: {b: 'd', c: null}}),
{a: {b: 'd'}}
);
});
});

it('should replace an object array with a number array', function() {
assert.deepEqual(
apply({a: [{b: 'c'}]}, {a: [1]}),
it('should replace an object array with a number array', function() {
assert.deepEqual(
apply({a: [{b: 'c'}]}, {a: [1]}),
{a: [1]}
);
});
});

it('should replace an array', function() {
assert.deepEqual(
apply(['a', 'b'], ['c', 'd']),
['c', 'd']
it('should replace an array', function() {
assert.deepEqual(
apply(['a', 'b'], ['c', 'd']),
['c', 'd']
);
});
});

it('should replace an object with an array', function() {
assert.deepEqual(
apply({a: 'b'}, ['c']),
['c']
it('should replace an object with an array', function() {
assert.deepEqual(
apply({a: 'b'}, ['c']),
['c']
);
});
});

it('should replace an object with null', function() {
assert.deepEqual(
apply({a: 'foo'}, null),
null
it('should replace an object with null', function() {
assert.deepEqual(
apply({a: 'foo'}, null),
null
);
});
});

it('should replace an object with a string', function() {
assert.deepEqual(
apply({a: 'foo'}, 'bar'),
'bar'
it('should replace with an object implementing toJSON() method', function() {
assert.deepEqual(
apply({a: 'foo'}, {a: new Date('2020-05-09T00:00:00.000Z')}),
{a: '2020-05-09T00:00:00.000Z'}
);
});
});

it('should not change null attributes', function() {
assert.deepEqual(
apply({e: null}, {a: 1}),
{e: null, a: 1}
it('should replace an object with a string', function() {
assert.deepEqual(
apply({a: 'foo'}, 'bar'),
'bar'
);
});
});

it('should not set an attribute to null', function() {
assert.deepEqual(
apply([1, 2], {a: 'b', c: null}),
{a: 'b'}
it('should not change null attributes', function() {
assert.deepEqual(
apply({e: null}, {a: 1}),
{e: null, a: 1}
);
});
});

it('should not set an attribute to null in a sub object', function() {
assert.deepEqual(
apply({}, {a: {bb: {ccc: null}}}),
{a: {bb: {}}}
);
});
it('should not set an attribute to null', function() {
assert.deepEqual(
apply([1, 2], {a: 'b', c: null}),
{a: 'b'}
);
});

it('should not set an attribute to null in a sub object', function() {
assert.deepEqual(
apply({}, {a: {bb: {ccc: null}}}),
{a: {bb: {}}}
);
});
});
Loading