json-diff-ts 1.1.0 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -109
- package/lib/index.js +12 -5
- package/lib/jsonCompare.js +29 -23
- package/lib/jsonDiff.d.ts +5 -3
- package/lib/jsonDiff.js +52 -23
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
# json-diff-ts
|
|
2
2
|
|
|
3
|
-

|
|
3
|
+

|
|
4
4
|
[](https://snyk.io/test/github/ltwlf/json-diff-ts?targetFile=package.json)
|
|
5
5
|
[](https://sonarcloud.io/dashboard?id=ltwlf_json-diff-ts)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The most compelling feature of this diff library is the support for array keys instead of just indexes and is compatible with JSONPath.
|
|
7
|
+
TypeScript diff tool with support for array keys instead of just indexes and compatible with JSONPath.
|
|
10
8
|
|
|
11
9
|
## Features
|
|
12
10
|
|
|
@@ -17,60 +15,70 @@ If a key is specified for an embedded array, the diff will be generated based on
|
|
|
17
15
|
#### Examples:
|
|
18
16
|
|
|
19
17
|
```javascript
|
|
18
|
+
var changesets = require('json-diff-ts');
|
|
19
|
+
var newObj, oldObj;
|
|
20
|
+
|
|
21
|
+
oldObj = {
|
|
22
|
+
name: 'joe',
|
|
23
|
+
age: 55,
|
|
24
|
+
coins: [2, 5],
|
|
25
|
+
children: [
|
|
26
|
+
{ name: 'kid1', age: 1 },
|
|
27
|
+
{ name: 'kid2', age: 2 }
|
|
28
|
+
]
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
newObj = {
|
|
32
|
+
name: 'smith',
|
|
33
|
+
coins: [2, 5, 1],
|
|
34
|
+
children: [
|
|
35
|
+
{ name: 'kid3', age: 3 },
|
|
36
|
+
{ name: 'kid1', age: 0 },
|
|
37
|
+
{ name: 'kid2', age: 2 }
|
|
38
|
+
]
|
|
39
|
+
};
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
name: 'joe',
|
|
26
|
-
age: 55,
|
|
27
|
-
coins: [2, 5],
|
|
28
|
-
children: [
|
|
29
|
-
{name: 'kid1', age: 1},
|
|
30
|
-
{name: 'kid2', age: 2}
|
|
31
|
-
]};
|
|
32
|
-
|
|
33
|
-
newObj = {
|
|
34
|
-
name: 'smith',
|
|
35
|
-
coins: [2, 5, 1],
|
|
36
|
-
children: [
|
|
37
|
-
{name: 'kid3', age: 3},
|
|
38
|
-
{name: 'kid1', age: 0},
|
|
39
|
-
{name: 'kid2', age: 2}
|
|
40
|
-
]};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# Assume children is an array of child object and the child object has 'name' as its primary key
|
|
44
|
-
diffs = changesets.diff(oldObj, newObj, {children: 'name'}); // keys can also be hierarchical e.g. {children: 'name', 'children.grandChildren', 'age'}
|
|
41
|
+
// Assume children is an array of child object and the child object has 'name' as its primary key
|
|
42
|
+
// keys can also be hierarchical e.g. {children: 'name', 'children.grandChildren', 'age'}
|
|
43
|
+
// or use functions that return the key of an object e.g. {children: function(obj) { return obj.key; }}
|
|
44
|
+
diffs = changesets.diff(oldObj, newObj, { children: 'name' });
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
46
|
+
expect(diffs).to.eql([
|
|
47
|
+
{
|
|
48
|
+
type: 'update',
|
|
49
|
+
key: 'name',
|
|
50
|
+
value: 'smith',
|
|
51
|
+
oldValue: 'joe'
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: 'update',
|
|
55
|
+
key: 'coins',
|
|
56
|
+
embededKey: '$index',
|
|
57
|
+
changes: [{ type: 'add', key: '2', value: 1 }]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
type: 'update',
|
|
61
|
+
key: 'children',
|
|
62
|
+
embededKey: 'name',
|
|
63
|
+
changes: [
|
|
64
|
+
{
|
|
65
|
+
type: 'update',
|
|
66
|
+
key: 'kid1',
|
|
67
|
+
changes: [{ type: 'update', key: 'age', value: 0, oldValue: 1 }]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'add',
|
|
71
|
+
key: 'kid3',
|
|
72
|
+
value: { name: 'kid3', age: 3 }
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'remove',
|
|
78
|
+
key: 'age',
|
|
79
|
+
value: 55
|
|
80
|
+
}
|
|
81
|
+
]);
|
|
74
82
|
```
|
|
75
83
|
|
|
76
84
|
### flattenChangeset
|
|
@@ -87,7 +95,7 @@ const changeset = unflattenChanges(flatChanges.slice(1, 5));
|
|
|
87
95
|
// ...
|
|
88
96
|
```
|
|
89
97
|
|
|
90
|
-
The **flatChange**
|
|
98
|
+
The **flatChange** format will look like this:
|
|
91
99
|
|
|
92
100
|
```javascript
|
|
93
101
|
[
|
|
@@ -132,58 +140,65 @@ The **flatChange** formant will look like this:
|
|
|
132
140
|
#### Examples:
|
|
133
141
|
|
|
134
142
|
```javascript
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
{name: 'kid2', age: 2}
|
|
144
|
-
]};
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Assume children is an array of child object and the child object has 'name' as its primary key
|
|
148
|
-
diffs = [
|
|
149
|
-
{
|
|
150
|
-
type: 'update', key: 'name', value: 'smith', oldValue: 'joe'
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
type: 'update', key: 'coins', embededKey: '$index', changes: [
|
|
154
|
-
{type: 'add', key: '2', value: 1 }
|
|
155
|
-
]
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
type: 'update',
|
|
159
|
-
key: 'children',
|
|
160
|
-
embededKey: 'name', // The key property name of the elements in an array
|
|
161
|
-
changes: [
|
|
162
|
-
{
|
|
163
|
-
type: 'update', key: 'kid1', changes: [
|
|
164
|
-
{type: 'update', key: 'age', value: 0, oldValue: 1 }
|
|
165
|
-
]
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
type: 'add', key: 'kid3', value: {name: 'kid3', age: 3 }
|
|
169
|
-
}
|
|
170
|
-
]
|
|
171
|
-
},
|
|
172
|
-
{
|
|
173
|
-
type: 'remove', key: 'age', value: 55
|
|
174
|
-
}
|
|
143
|
+
var changesets = require('json-diff-ts');
|
|
144
|
+
var oldObj = {
|
|
145
|
+
name: 'joe',
|
|
146
|
+
age: 55,
|
|
147
|
+
coins: [2, 5],
|
|
148
|
+
children: [
|
|
149
|
+
{ name: 'kid1', age: 1 },
|
|
150
|
+
{ name: 'kid2', age: 2 }
|
|
175
151
|
]
|
|
152
|
+
};
|
|
176
153
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
154
|
+
// Assume children is an array of child object and the child object has 'name' as its primary key
|
|
155
|
+
diffs = [
|
|
156
|
+
{
|
|
157
|
+
type: 'update',
|
|
158
|
+
key: 'name',
|
|
159
|
+
value: 'smith',
|
|
160
|
+
oldValue: 'joe'
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
type: 'update',
|
|
164
|
+
key: 'coins',
|
|
165
|
+
embededKey: '$index',
|
|
166
|
+
changes: [{ type: 'add', key: '2', value: 1 }]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
type: 'update',
|
|
170
|
+
key: 'children',
|
|
171
|
+
embededKey: 'name', // The key property name of the elements in an array
|
|
172
|
+
changes: [
|
|
173
|
+
{
|
|
174
|
+
type: 'update',
|
|
175
|
+
key: 'kid1',
|
|
176
|
+
changes: [{ type: 'update', key: 'age', value: 0, oldValue: 1 }]
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: 'add',
|
|
180
|
+
key: 'kid3',
|
|
181
|
+
value: { name: 'kid3', age: 3 }
|
|
182
|
+
}
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
type: 'remove',
|
|
187
|
+
key: 'age',
|
|
188
|
+
value: 55
|
|
189
|
+
}
|
|
190
|
+
];
|
|
186
191
|
|
|
192
|
+
changesets.applyChanges(oldObj, diffs);
|
|
193
|
+
expect(oldObj).to.eql({
|
|
194
|
+
name: 'smith',
|
|
195
|
+
coins: [2, 5, 1],
|
|
196
|
+
children: [
|
|
197
|
+
{ name: 'kid3', age: 3 },
|
|
198
|
+
{ name: 'kid1', age: 0 },
|
|
199
|
+
{ name: 'kid2', age: 2 }
|
|
200
|
+
]
|
|
201
|
+
});
|
|
187
202
|
```
|
|
188
203
|
|
|
189
204
|
### revertChange
|
|
@@ -192,7 +207,7 @@ The **flatChange** formant will look like this:
|
|
|
192
207
|
|
|
193
208
|
```javascript
|
|
194
209
|
|
|
195
|
-
var changesets = require('diff-
|
|
210
|
+
var changesets = require('json-diff-ts');
|
|
196
211
|
|
|
197
212
|
var newObj = {
|
|
198
213
|
name: 'smith',
|
|
@@ -203,7 +218,7 @@ The **flatChange** formant will look like this:
|
|
|
203
218
|
{name: 'kid2', age: 2}
|
|
204
219
|
]};
|
|
205
220
|
|
|
206
|
-
|
|
221
|
+
// Assume children is an array of child object and the child object has 'name' as its primary key
|
|
207
222
|
diffs = [
|
|
208
223
|
{
|
|
209
224
|
type: 'update', key: 'name', value: 'smith', oldValue: 'joe'
|
|
@@ -258,10 +273,19 @@ npm run test
|
|
|
258
273
|
```
|
|
259
274
|
|
|
260
275
|
## Contact
|
|
276
|
+
|
|
261
277
|
Blog: https://blog.leitwolf.io
|
|
262
278
|
|
|
263
279
|
Twitter: [@cglessner](https://twitter.com/cglessner)
|
|
264
280
|
|
|
281
|
+
## Changelog
|
|
282
|
+
|
|
283
|
+
- v1.2.3 Update outdated dependencies; update TypeScript version to 4.5.2
|
|
284
|
+
- v1.2.2 Add support for functions to resove object keys (PR by [Abraxxa](https://github.com/abraxxa))
|
|
285
|
+
|
|
286
|
+
## Credits
|
|
287
|
+
|
|
288
|
+
This project was based on https://www.npmjs.com/package/diff-json (viruschidai@gmail.com)
|
|
265
289
|
|
|
266
290
|
## Licence
|
|
267
291
|
|
|
@@ -275,7 +299,7 @@ The above copyright notice and this permission notice shall be included in all c
|
|
|
275
299
|
|
|
276
300
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
277
301
|
|
|
278
|
-
The project is based on diff-json (https://www.npmjs.com/package/diff-json). Copyright 2013 viruschidai@gmail.com. for additional details.
|
|
302
|
+
The project is based on diff-json (https://www.npmjs.com/package/diff-json). Copyright 2013 viruschidai@gmail.com. for additional details.
|
|
279
303
|
|
|
280
304
|
**Original License**
|
|
281
305
|
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
function
|
|
3
|
-
|
|
4
|
-
}
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
5
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
|
|
7
|
-
|
|
13
|
+
__exportStar(require("./jsonDiff"), exports);
|
|
14
|
+
__exportStar(require("./jsonCompare"), exports);
|
package/lib/jsonCompare.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compare = exports.applyChangelist = exports.enrich = exports.createContainer = exports.createValue = exports.CompareOperation = void 0;
|
|
3
4
|
const lodash_1 = require("lodash");
|
|
4
5
|
const jsonDiff_1 = require("./jsonDiff");
|
|
5
6
|
var CompareOperation;
|
|
@@ -7,54 +8,57 @@ var CompareOperation;
|
|
|
7
8
|
CompareOperation["CONTAINER"] = "CONTAINER";
|
|
8
9
|
CompareOperation["UNCHANGED"] = "UNCHANGED";
|
|
9
10
|
})(CompareOperation = exports.CompareOperation || (exports.CompareOperation = {}));
|
|
10
|
-
|
|
11
|
-
exports.
|
|
11
|
+
const createValue = (value) => ({ type: CompareOperation.UNCHANGED, value });
|
|
12
|
+
exports.createValue = createValue;
|
|
13
|
+
const createContainer = (value) => ({
|
|
12
14
|
type: CompareOperation.CONTAINER,
|
|
13
15
|
value
|
|
14
16
|
});
|
|
15
|
-
exports.
|
|
16
|
-
|
|
17
|
+
exports.createContainer = createContainer;
|
|
18
|
+
const enrich = (object) => {
|
|
19
|
+
const objectType = (0, jsonDiff_1.getTypeOfObj)(object);
|
|
17
20
|
switch (objectType) {
|
|
18
21
|
case 'Object':
|
|
19
|
-
return lodash_1.keys(object)
|
|
20
|
-
.map((key) => ({ key, value: exports.enrich(object[key]) }))
|
|
22
|
+
return (0, lodash_1.keys)(object)
|
|
23
|
+
.map((key) => ({ key, value: (0, exports.enrich)(object[key]) }))
|
|
21
24
|
.reduce((accumulator, entry) => {
|
|
22
25
|
accumulator.value[entry.key] = entry.value;
|
|
23
26
|
return accumulator;
|
|
24
|
-
}, exports.createContainer({}));
|
|
27
|
+
}, (0, exports.createContainer)({}));
|
|
25
28
|
case 'Array':
|
|
26
|
-
return lodash_1.chain(object)
|
|
27
|
-
.map(value => exports.enrich(value))
|
|
29
|
+
return (0, lodash_1.chain)(object)
|
|
30
|
+
.map(value => (0, exports.enrich)(value))
|
|
28
31
|
.reduce((accumulator, value) => {
|
|
29
32
|
accumulator.value.push(value);
|
|
30
33
|
return accumulator;
|
|
31
|
-
}, exports.createContainer([]))
|
|
34
|
+
}, (0, exports.createContainer)([]))
|
|
32
35
|
.value();
|
|
33
36
|
case 'Function':
|
|
34
37
|
return undefined;
|
|
35
38
|
case 'Date':
|
|
36
39
|
default:
|
|
37
40
|
// Primitive value
|
|
38
|
-
return exports.createValue(object);
|
|
41
|
+
return (0, exports.createValue)(object);
|
|
39
42
|
}
|
|
40
43
|
};
|
|
41
|
-
exports.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
.map(entry => (Object.assign(Object.assign({}, entry), { path: lodash_1.replace(entry.path,
|
|
45
|
-
.map(entry => (Object.assign(Object.assign({}, entry), { path: lodash_1.replace(entry.path, /(?<
|
|
46
|
-
.map(entry => (Object.assign(Object.assign({}, entry), { path: lodash_1.replace(entry.path,
|
|
47
|
-
.map(entry => (Object.assign(Object.assign({}, entry), { path: lodash_1.replace(entry.path,
|
|
48
|
-
.map(entry => (Object.assign(Object.assign({}, entry), { path: lodash_1.replace(entry.path, /
|
|
44
|
+
exports.enrich = enrich;
|
|
45
|
+
const applyChangelist = (object, changelist) => {
|
|
46
|
+
(0, lodash_1.chain)(changelist)
|
|
47
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, '$.', '.') })))
|
|
48
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, /(\[(?<array>\d)\]\.)/g, 'ARRVAL_START$<array>ARRVAL_END') })))
|
|
49
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, /(?<dot>\.)/g, '.value$<dot>') })))
|
|
50
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, /\./, '') })))
|
|
51
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, /ARRVAL_START/g, '.value[') })))
|
|
52
|
+
.map(entry => (Object.assign(Object.assign({}, entry), { path: (0, lodash_1.replace)(entry.path, /ARRVAL_END/g, '].value.') })))
|
|
49
53
|
.value()
|
|
50
54
|
.forEach(entry => {
|
|
51
55
|
switch (entry.type) {
|
|
52
56
|
case jsonDiff_1.Operation.ADD:
|
|
53
57
|
case jsonDiff_1.Operation.UPDATE:
|
|
54
|
-
lodash_1.set(object, entry.path, { type: entry.type, value: entry.value, oldValue: entry.oldValue });
|
|
58
|
+
(0, lodash_1.set)(object, entry.path, { type: entry.type, value: entry.value, oldValue: entry.oldValue });
|
|
55
59
|
break;
|
|
56
60
|
case jsonDiff_1.Operation.REMOVE:
|
|
57
|
-
lodash_1.set(object, entry.path, { type: entry.type, value: undefined, oldValue: entry.value });
|
|
61
|
+
(0, lodash_1.set)(object, entry.path, { type: entry.type, value: undefined, oldValue: entry.value });
|
|
58
62
|
break;
|
|
59
63
|
default:
|
|
60
64
|
throw new Error();
|
|
@@ -62,6 +66,8 @@ exports.applyChangelist = (object, changelist) => {
|
|
|
62
66
|
});
|
|
63
67
|
return object;
|
|
64
68
|
};
|
|
65
|
-
exports.
|
|
66
|
-
|
|
69
|
+
exports.applyChangelist = applyChangelist;
|
|
70
|
+
const compare = (oldObject, newObject) => {
|
|
71
|
+
return (0, exports.applyChangelist)((0, exports.enrich)(oldObject), (0, jsonDiff_1.flattenChangeset)((0, jsonDiff_1.diff)(oldObject, newObject)));
|
|
67
72
|
};
|
|
73
|
+
exports.compare = compare;
|
package/lib/jsonDiff.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Dictionary } from 'lodash';
|
|
2
|
+
declare type FunctionKey = (obj: any) => any;
|
|
2
3
|
export declare const getTypeOfObj: (obj: any) => string;
|
|
3
|
-
export declare const diff: (oldObj: any, newObj: any, embeddedObjKeys?: Dictionary<string>) => IChange[];
|
|
4
|
+
export declare const diff: (oldObj: any, newObj: any, embeddedObjKeys?: Dictionary<string | FunctionKey>) => IChange[];
|
|
4
5
|
export declare const applyChangeset: (obj: any, changeset: Changeset) => any;
|
|
5
6
|
export declare const revertChangeset: (obj: any, changeset: Changeset) => any;
|
|
6
7
|
export declare enum Operation {
|
|
@@ -11,7 +12,7 @@ export declare enum Operation {
|
|
|
11
12
|
export interface IChange {
|
|
12
13
|
type: Operation;
|
|
13
14
|
key: string;
|
|
14
|
-
embeddedKey?: string;
|
|
15
|
+
embeddedKey?: string | FunctionKey;
|
|
15
16
|
value?: any | any[];
|
|
16
17
|
oldValue?: any;
|
|
17
18
|
changes?: IChange[];
|
|
@@ -25,5 +26,6 @@ export interface IFlatChange {
|
|
|
25
26
|
value?: any;
|
|
26
27
|
oldValue?: any;
|
|
27
28
|
}
|
|
28
|
-
export declare const flattenChangeset: (obj:
|
|
29
|
+
export declare const flattenChangeset: (obj: Changeset | IChange, path?: string, embeddedKey?: string | FunctionKey) => IFlatChange[];
|
|
29
30
|
export declare const unflattenChanges: (changes: IFlatChange | IFlatChange[]) => IChange[];
|
|
31
|
+
export {};
|
package/lib/jsonDiff.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.unflattenChanges = exports.flattenChangeset = exports.Operation = exports.revertChangeset = exports.applyChangeset = exports.diff = exports.getTypeOfObj = void 0;
|
|
3
4
|
const lodash_1 = require("lodash");
|
|
4
|
-
|
|
5
|
+
const getTypeOfObj = (obj) => {
|
|
5
6
|
if (typeof obj === 'undefined') {
|
|
6
7
|
return 'undefined';
|
|
7
8
|
}
|
|
@@ -10,14 +11,15 @@ exports.getTypeOfObj = (obj) => {
|
|
|
10
11
|
}
|
|
11
12
|
return Object.prototype.toString.call(obj).match(/^\[object\s(.*)\]$/)[1];
|
|
12
13
|
};
|
|
14
|
+
exports.getTypeOfObj = getTypeOfObj;
|
|
13
15
|
const getKey = (path) => {
|
|
14
16
|
const left = path[path.length - 1];
|
|
15
17
|
return left != null ? left : '$root';
|
|
16
18
|
};
|
|
17
19
|
const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
18
20
|
let changes = [];
|
|
19
|
-
const typeOfOldObj = exports.getTypeOfObj(oldObj);
|
|
20
|
-
const typeOfNewObj = exports.getTypeOfObj(newObj);
|
|
21
|
+
const typeOfOldObj = (0, exports.getTypeOfObj)(oldObj);
|
|
22
|
+
const typeOfNewObj = (0, exports.getTypeOfObj)(newObj);
|
|
21
23
|
// if type of object changes, consider it as old obj has been deleted and a new object has been added
|
|
22
24
|
if (typeOfOldObj !== typeOfNewObj) {
|
|
23
25
|
changes.push({ type: Operation.REMOVE, key: getKey(path), value: oldObj });
|
|
@@ -64,7 +66,7 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
64
66
|
let changes = [];
|
|
65
67
|
const oldObjKeys = Object.keys(oldObj);
|
|
66
68
|
const newObjKeys = Object.keys(newObj);
|
|
67
|
-
const intersectionKeys = lodash_1.intersection(oldObjKeys, newObjKeys);
|
|
69
|
+
const intersectionKeys = (0, lodash_1.intersection)(oldObjKeys, newObjKeys);
|
|
68
70
|
for (k of intersectionKeys) {
|
|
69
71
|
newPath = path.concat([k]);
|
|
70
72
|
newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
|
|
@@ -73,7 +75,7 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
73
75
|
changes = changes.concat(diffs);
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
|
-
const addedKeys = lodash_1.difference(newObjKeys, oldObjKeys);
|
|
78
|
+
const addedKeys = (0, lodash_1.difference)(newObjKeys, oldObjKeys);
|
|
77
79
|
for (k of addedKeys) {
|
|
78
80
|
newPath = path.concat([k]);
|
|
79
81
|
newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
|
|
@@ -83,7 +85,7 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
83
85
|
value: newObj[k]
|
|
84
86
|
});
|
|
85
87
|
}
|
|
86
|
-
const deletedKeys = lodash_1.difference(oldObjKeys, newObjKeys);
|
|
88
|
+
const deletedKeys = (0, lodash_1.difference)(oldObjKeys, newObjKeys);
|
|
87
89
|
for (k of deletedKeys) {
|
|
88
90
|
newPath = path.concat([k]);
|
|
89
91
|
newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
|
|
@@ -96,7 +98,7 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
96
98
|
return changes;
|
|
97
99
|
};
|
|
98
100
|
const compareArray = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
99
|
-
const left = embeddedObjKeys
|
|
101
|
+
const left = getObjectKey(embeddedObjKeys, keyPath);
|
|
100
102
|
const uniqKey = left != null ? left : '$index';
|
|
101
103
|
const indexedOldObj = convertArrayToObj(oldObj, uniqKey);
|
|
102
104
|
const indexedNewObj = convertArrayToObj(newObj, uniqKey);
|
|
@@ -115,10 +117,25 @@ const compareArray = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
|
115
117
|
return [];
|
|
116
118
|
}
|
|
117
119
|
};
|
|
120
|
+
const getObjectKey = (embeddedObjKeys, keyPath) => {
|
|
121
|
+
if (embeddedObjKeys != null) {
|
|
122
|
+
const path = keyPath.join('.');
|
|
123
|
+
const key = embeddedObjKeys[path];
|
|
124
|
+
if (key != null) {
|
|
125
|
+
return key;
|
|
126
|
+
}
|
|
127
|
+
for (const regex in embeddedObjKeys) {
|
|
128
|
+
if (path.match(new RegExp(regex))) {
|
|
129
|
+
return embeddedObjKeys[regex];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
};
|
|
118
135
|
const convertArrayToObj = (arr, uniqKey) => {
|
|
119
136
|
let obj = {};
|
|
120
137
|
if (uniqKey !== '$index') {
|
|
121
|
-
obj = lodash_1.keyBy(arr, uniqKey);
|
|
138
|
+
obj = (0, lodash_1.keyBy)(arr, uniqKey);
|
|
122
139
|
}
|
|
123
140
|
else {
|
|
124
141
|
for (let i = 0; i < arr.length; i++) {
|
|
@@ -202,9 +219,9 @@ const applyArrayChange = (arr, change) => (() => {
|
|
|
202
219
|
element = arr[subchange.key];
|
|
203
220
|
}
|
|
204
221
|
else {
|
|
205
|
-
element = lodash_1.find(arr, el => el[change.embeddedKey].toString() === subchange.key.toString());
|
|
222
|
+
element = (0, lodash_1.find)(arr, el => el[change.embeddedKey].toString() === subchange.key.toString());
|
|
206
223
|
}
|
|
207
|
-
result.push(exports.applyChangeset(element, subchange.changes));
|
|
224
|
+
result.push((0, exports.applyChangeset)(element, subchange.changes));
|
|
208
225
|
}
|
|
209
226
|
}
|
|
210
227
|
return result;
|
|
@@ -214,7 +231,7 @@ const applyBranchChange = (obj, change) => {
|
|
|
214
231
|
return applyArrayChange(obj, change);
|
|
215
232
|
}
|
|
216
233
|
else {
|
|
217
|
-
return exports.applyChangeset(obj, change.changes);
|
|
234
|
+
return (0, exports.applyChangeset)(obj, change.changes);
|
|
218
235
|
}
|
|
219
236
|
};
|
|
220
237
|
const revertLeafChange = (obj, change, embeddedKey = '$index') => {
|
|
@@ -240,9 +257,9 @@ const revertArrayChange = (arr, change) => (() => {
|
|
|
240
257
|
element = arr[+subchange.key];
|
|
241
258
|
}
|
|
242
259
|
else {
|
|
243
|
-
element = lodash_1.find(arr, el => el[change.embeddedKey].toString() === subchange.key);
|
|
260
|
+
element = (0, lodash_1.find)(arr, el => el[change.embeddedKey].toString() === subchange.key);
|
|
244
261
|
}
|
|
245
|
-
result.push(exports.revertChangeset(element, subchange.changes));
|
|
262
|
+
result.push((0, exports.revertChangeset)(element, subchange.changes));
|
|
246
263
|
}
|
|
247
264
|
}
|
|
248
265
|
return result;
|
|
@@ -252,11 +269,12 @@ const revertBranchChange = (obj, change) => {
|
|
|
252
269
|
return revertArrayChange(obj, change);
|
|
253
270
|
}
|
|
254
271
|
else {
|
|
255
|
-
return exports.revertChangeset(obj, change.changes);
|
|
272
|
+
return (0, exports.revertChangeset)(obj, change.changes);
|
|
256
273
|
}
|
|
257
274
|
};
|
|
258
|
-
|
|
259
|
-
exports.
|
|
275
|
+
const diff = (oldObj, newObj, embeddedObjKeys) => compare(oldObj, newObj, [], embeddedObjKeys, []);
|
|
276
|
+
exports.diff = diff;
|
|
277
|
+
const applyChangeset = (obj, changeset) => {
|
|
260
278
|
if (changeset) {
|
|
261
279
|
changeset.forEach(change => (change.value !== null && change.value !== undefined) || change.type === Operation.REMOVE
|
|
262
280
|
? applyLeafChange(obj, change, change.embeddedKey)
|
|
@@ -264,7 +282,8 @@ exports.applyChangeset = (obj, changeset) => {
|
|
|
264
282
|
}
|
|
265
283
|
return obj;
|
|
266
284
|
};
|
|
267
|
-
exports.
|
|
285
|
+
exports.applyChangeset = applyChangeset;
|
|
286
|
+
const revertChangeset = (obj, changeset) => {
|
|
268
287
|
if (changeset) {
|
|
269
288
|
changeset
|
|
270
289
|
.reverse()
|
|
@@ -272,15 +291,16 @@ exports.revertChangeset = (obj, changeset) => {
|
|
|
272
291
|
}
|
|
273
292
|
return obj;
|
|
274
293
|
};
|
|
294
|
+
exports.revertChangeset = revertChangeset;
|
|
275
295
|
var Operation;
|
|
276
296
|
(function (Operation) {
|
|
277
297
|
Operation["REMOVE"] = "REMOVE";
|
|
278
298
|
Operation["ADD"] = "ADD";
|
|
279
299
|
Operation["UPDATE"] = "UPDATE";
|
|
280
300
|
})(Operation = exports.Operation || (exports.Operation = {}));
|
|
281
|
-
|
|
301
|
+
const flattenChangeset = (obj, path = '$', embeddedKey) => {
|
|
282
302
|
if (Array.isArray(obj)) {
|
|
283
|
-
return obj.reduce((memo, change) => [...memo, ...exports.flattenChangeset(change, path, embeddedKey)], []);
|
|
303
|
+
return obj.reduce((memo, change) => [...memo, ...(0, exports.flattenChangeset)(change, path, embeddedKey)], []);
|
|
284
304
|
}
|
|
285
305
|
else {
|
|
286
306
|
if (obj.changes || embeddedKey) {
|
|
@@ -291,17 +311,18 @@ exports.flattenChangeset = (obj, path = '$', embeddedKey) => {
|
|
|
291
311
|
? path
|
|
292
312
|
: `${path}[?(@.${embeddedKey}='${obj.key}')]`
|
|
293
313
|
: (path = `${path}.${obj.key}`);
|
|
294
|
-
return exports.flattenChangeset(obj.changes || obj, path, obj.embeddedKey);
|
|
314
|
+
return (0, exports.flattenChangeset)(obj.changes || obj, path, obj.embeddedKey);
|
|
295
315
|
}
|
|
296
316
|
else {
|
|
297
|
-
const valueType = exports.getTypeOfObj(obj.value);
|
|
317
|
+
const valueType = (0, exports.getTypeOfObj)(obj.value);
|
|
298
318
|
return [
|
|
299
319
|
Object.assign(Object.assign({}, obj), { path: valueType === 'Object' || path.endsWith(`[${obj.key}]`) ? path : `${path}.${obj.key}`, valueType })
|
|
300
320
|
];
|
|
301
321
|
}
|
|
302
322
|
}
|
|
303
323
|
};
|
|
304
|
-
exports.
|
|
324
|
+
exports.flattenChangeset = flattenChangeset;
|
|
325
|
+
const unflattenChanges = (changes) => {
|
|
305
326
|
if (!Array.isArray(changes)) {
|
|
306
327
|
changes = [changes];
|
|
307
328
|
}
|
|
@@ -309,7 +330,14 @@ exports.unflattenChanges = (changes) => {
|
|
|
309
330
|
changes.forEach(change => {
|
|
310
331
|
const obj = {};
|
|
311
332
|
let ptr = obj;
|
|
312
|
-
const segments = change.path.split(/(
|
|
333
|
+
const segments = change.path.split(/([^@])\./).reduce((acc, curr, i) => {
|
|
334
|
+
const x = Math.floor(i / 2);
|
|
335
|
+
if (!acc[x]) {
|
|
336
|
+
acc[x] = '';
|
|
337
|
+
}
|
|
338
|
+
acc[x] += curr;
|
|
339
|
+
return acc;
|
|
340
|
+
}, []);
|
|
313
341
|
// $.childern[@.name='chris'].age
|
|
314
342
|
// =>
|
|
315
343
|
// $
|
|
@@ -409,3 +437,4 @@ exports.unflattenChanges = (changes) => {
|
|
|
409
437
|
});
|
|
410
438
|
return changesArr;
|
|
411
439
|
};
|
|
440
|
+
exports.unflattenChanges = unflattenChanges;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-diff-ts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.3",
|
|
4
4
|
"description": "A diff tool for JavaScript based on https://www.npmjs.com/package/diff-json written in TypeScript.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/ltwlf/json-diff-ts#readme",
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@types/jest": "^
|
|
40
|
+
"@types/jest": "^27.0.3",
|
|
41
41
|
"@types/lodash": "^4.14.149",
|
|
42
42
|
"jest": "^24.9.0",
|
|
43
|
-
"prettier": "^
|
|
43
|
+
"prettier": "^2.5.1",
|
|
44
44
|
"ts-jest": "^24.2.0",
|
|
45
|
-
"tslint": "^
|
|
45
|
+
"tslint": "^6.1.3",
|
|
46
46
|
"tslint-config-prettier": "^1.18.0",
|
|
47
|
-
"typescript": "^
|
|
47
|
+
"typescript": "^4.5.2"
|
|
48
48
|
},
|
|
49
49
|
"peerDependencies": {
|
|
50
50
|
"lodash": "^4.x"
|