json-diff-ts 2.1.0 → 2.2.1
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 +11 -8
- package/lib/jsonDiff.d.ts +1 -1
- package/lib/jsonDiff.js +56 -24
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -33,7 +33,8 @@ const oldData = {
|
|
|
33
33
|
characters: [
|
|
34
34
|
{ id: 'LUK', name: 'Luke Skywalker', force: true },
|
|
35
35
|
{ id: 'LEI', name: 'Leia Organa', force: true }
|
|
36
|
-
]
|
|
36
|
+
],
|
|
37
|
+
weapons: ['Lightsaber', 'Blaster']
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
const newData = {
|
|
@@ -42,7 +43,8 @@ const newData = {
|
|
|
42
43
|
characters: [
|
|
43
44
|
{ id: 'LUK', name: 'Luke Skywalker', force: true, rank: 'Commander' },
|
|
44
45
|
{ id: 'HAN', name: 'Han Solo', force: false }
|
|
45
|
-
]
|
|
46
|
+
],
|
|
47
|
+
weapons: ['Lightsaber', 'Blaster', 'Bowcaster']
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
const diffs = diff(oldData, newData, { characters: 'id' });
|
|
@@ -115,12 +117,6 @@ const expectedDiffs = [
|
|
|
115
117
|
|
|
116
118
|
Paths can be utilized to identify keys within nested arrays.
|
|
117
119
|
|
|
118
|
-
```javascript
|
|
119
|
-
const diffs = diff(oldData, newData, { characters.subarray: 'id' });
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Alternative Syntax
|
|
123
|
-
|
|
124
120
|
```javascript
|
|
125
121
|
const diffs = diff(oldData, newData, { 'characters.subarray': 'id' });
|
|
126
122
|
```
|
|
@@ -150,6 +146,12 @@ embeddedObjKeys.set(/^char\w+$/, 'id'); // instead of 'id' you can specify a fun
|
|
|
150
146
|
const diffs = diff(oldObj, newObj, embeddedObjKeys);
|
|
151
147
|
```
|
|
152
148
|
|
|
149
|
+
Compare string arrays by value instead of index
|
|
150
|
+
|
|
151
|
+
```javascript
|
|
152
|
+
const diffs = diff(oldObj, newObj, { stringArr: '$value' });
|
|
153
|
+
```
|
|
154
|
+
|
|
153
155
|
### `flattenChangeset`
|
|
154
156
|
|
|
155
157
|
Transforms a complex changeset into a flat list of atomic changes, each describable by a JSONPath.
|
|
@@ -250,6 +252,7 @@ Discover more about the company behind this project: [hololux](https://hololux.c
|
|
|
250
252
|
|
|
251
253
|
## Release Notes
|
|
252
254
|
|
|
255
|
+
- **v2.2.0:** Fix lodash-es decependency, exclude keys, compare string arrays by value
|
|
253
256
|
- **v2.1.0:** Resolves a problem related to JSON Path filters by replacing the single equal sign (=) with a double equal sign (==). This update maintains compatibility with existing flat changes. Allows to use either '' or '.' as root in the path.
|
|
254
257
|
- **v2.0.0:** json-diff-ts has been upgraded to an ECMAScript module! This major update brings optimizations and enhanced documentation. Additionally, a previously existing issue where all paths were treated as regex has been fixed. In this new version, you'll need to use a Map instead of a Record for regex paths. Please note that this is a breaking change if you were using regex paths in the previous versions.
|
|
255
258
|
- **v1.2.6:** Enhanced JSON Path handling for period-inclusive segments.
|
package/lib/jsonDiff.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface IFlatChange {
|
|
|
31
31
|
* @param {EmbeddedObjKeysType | EmbeddedObjKeysMapType} embeddedObjKeys - An optional parameter specifying keys of embedded objects.
|
|
32
32
|
* @returns {IChange[]} - An array of changes that transform the old object into the new object.
|
|
33
33
|
*/
|
|
34
|
-
export declare function diff(oldObj: any, newObj: any, embeddedObjKeys?: EmbeddedObjKeysType | EmbeddedObjKeysMapType): IChange[];
|
|
34
|
+
export declare function diff(oldObj: any, newObj: any, embeddedObjKeys?: EmbeddedObjKeysType | EmbeddedObjKeysMapType, keysToSkip?: string[]): IChange[];
|
|
35
35
|
/**
|
|
36
36
|
* Applies all changes in the changeset to the object.
|
|
37
37
|
*
|
package/lib/jsonDiff.js
CHANGED
|
@@ -13,7 +13,7 @@ export var Operation;
|
|
|
13
13
|
* @param {EmbeddedObjKeysType | EmbeddedObjKeysMapType} embeddedObjKeys - An optional parameter specifying keys of embedded objects.
|
|
14
14
|
* @returns {IChange[]} - An array of changes that transform the old object into the new object.
|
|
15
15
|
*/
|
|
16
|
-
export function diff(oldObj, newObj, embeddedObjKeys) {
|
|
16
|
+
export function diff(oldObj, newObj, embeddedObjKeys, keysToSkip) {
|
|
17
17
|
// Trim leading '.' from keys in embeddedObjKeys
|
|
18
18
|
if (embeddedObjKeys instanceof Map) {
|
|
19
19
|
embeddedObjKeys = new Map(Array.from(embeddedObjKeys.entries()).map(([key, value]) => [
|
|
@@ -25,7 +25,7 @@ export function diff(oldObj, newObj, embeddedObjKeys) {
|
|
|
25
25
|
embeddedObjKeys = Object.fromEntries(Object.entries(embeddedObjKeys).map(([key, value]) => [key.replace(/^\./, ''), value]));
|
|
26
26
|
}
|
|
27
27
|
// Compare old and new objects to generate a list of changes
|
|
28
|
-
return compare(oldObj, newObj, [], embeddedObjKeys, []);
|
|
28
|
+
return compare(oldObj, newObj, [], embeddedObjKeys, [], keysToSkip);
|
|
29
29
|
}
|
|
30
30
|
/**
|
|
31
31
|
* Applies all changes in the changeset to the object.
|
|
@@ -92,13 +92,31 @@ export const flattenChangeset = (obj, path = '$', embeddedKey) => {
|
|
|
92
92
|
}
|
|
93
93
|
else {
|
|
94
94
|
if (obj.changes || embeddedKey) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
95
|
+
if (embeddedKey) {
|
|
96
|
+
if (embeddedKey === '$index') {
|
|
97
|
+
path = `${path}[${obj.key}]`;
|
|
98
|
+
}
|
|
99
|
+
else if (embeddedKey === '$value') {
|
|
100
|
+
path = `${path}[?(@='${obj.key}')]`;
|
|
101
|
+
const valueType = getTypeOfObj(obj.value);
|
|
102
|
+
return [
|
|
103
|
+
{
|
|
104
|
+
...obj,
|
|
105
|
+
path,
|
|
106
|
+
valueType
|
|
107
|
+
}
|
|
108
|
+
];
|
|
109
|
+
}
|
|
110
|
+
else if (obj.type === Operation.ADD) {
|
|
111
|
+
// do nothing
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
path = filterExpression(path, embeddedKey, obj.key);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
path = append(path, obj.key);
|
|
119
|
+
}
|
|
102
120
|
return flattenChangeset(obj.changes || obj, path, obj.embeddedKey);
|
|
103
121
|
}
|
|
104
122
|
else {
|
|
@@ -145,8 +163,8 @@ export const unflattenChanges = (changes) => {
|
|
|
145
163
|
else {
|
|
146
164
|
for (let i = 1; i < segments.length; i++) {
|
|
147
165
|
const segment = segments[i];
|
|
148
|
-
// Matches JSONPath segments: "items[?(@.id=='123')]", items[?(@.id==123)], "items[2]"
|
|
149
|
-
const result = /^(
|
|
166
|
+
// Matches JSONPath segments: "items[?(@.id=='123')]", "items[?(@.id==123)]", "items[2]", "items[?(@='123')]"
|
|
167
|
+
const result = /^(.+?)\[\?\(@.?(?:([^=]*))?={1,2}'(.*)'\)\]$|^(.+?)\[(\d+)\]$/.exec(segment); //NOSONAR
|
|
150
168
|
// array
|
|
151
169
|
if (result) {
|
|
152
170
|
let key;
|
|
@@ -154,13 +172,13 @@ export const unflattenChanges = (changes) => {
|
|
|
154
172
|
let arrKey;
|
|
155
173
|
if (result[1]) {
|
|
156
174
|
key = result[1];
|
|
157
|
-
embeddedKey = result[2];
|
|
175
|
+
embeddedKey = result[2] || '$value';
|
|
158
176
|
arrKey = result[3];
|
|
159
177
|
}
|
|
160
178
|
else {
|
|
161
|
-
key = result[
|
|
179
|
+
key = result[4];
|
|
162
180
|
embeddedKey = '$index';
|
|
163
|
-
arrKey = Number(result[
|
|
181
|
+
arrKey = Number(result[4]);
|
|
164
182
|
}
|
|
165
183
|
// leaf
|
|
166
184
|
if (i === segments.length - 1) {
|
|
@@ -253,7 +271,7 @@ const getKey = (path) => {
|
|
|
253
271
|
const left = path[path.length - 1];
|
|
254
272
|
return left != null ? left : '$root';
|
|
255
273
|
};
|
|
256
|
-
const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
274
|
+
const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath, keysToSkip) => {
|
|
257
275
|
let changes = [];
|
|
258
276
|
const typeOfOldObj = getTypeOfObj(oldObj);
|
|
259
277
|
const typeOfNewObj = getTypeOfObj(newObj);
|
|
@@ -272,7 +290,7 @@ const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
|
272
290
|
})));
|
|
273
291
|
break;
|
|
274
292
|
case 'Object':
|
|
275
|
-
const diffs = compareObject(oldObj, newObj, path, embeddedObjKeys, keyPath);
|
|
293
|
+
const diffs = compareObject(oldObj, newObj, path, embeddedObjKeys, keyPath, false, keysToSkip);
|
|
276
294
|
if (diffs.length) {
|
|
277
295
|
if (path.length) {
|
|
278
296
|
changes.push({
|
|
@@ -287,7 +305,7 @@ const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
|
287
305
|
}
|
|
288
306
|
break;
|
|
289
307
|
case 'Array':
|
|
290
|
-
changes = changes.concat(compareArray(oldObj, newObj, path, embeddedObjKeys, keyPath));
|
|
308
|
+
changes = changes.concat(compareArray(oldObj, newObj, path, embeddedObjKeys, keyPath, keysToSkip));
|
|
291
309
|
break;
|
|
292
310
|
case 'Function':
|
|
293
311
|
break;
|
|
@@ -297,7 +315,7 @@ const compare = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
|
297
315
|
}
|
|
298
316
|
return changes;
|
|
299
317
|
};
|
|
300
|
-
const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath = false) => {
|
|
318
|
+
const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath = false, keysToSkip = []) => {
|
|
301
319
|
let k;
|
|
302
320
|
let newKeyPath;
|
|
303
321
|
let newPath;
|
|
@@ -305,13 +323,13 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
305
323
|
skipPath = false;
|
|
306
324
|
}
|
|
307
325
|
let changes = [];
|
|
308
|
-
const oldObjKeys = Object.keys(oldObj);
|
|
309
|
-
const newObjKeys = Object.keys(newObj);
|
|
326
|
+
const oldObjKeys = Object.keys(oldObj).filter((key) => keysToSkip.indexOf(key) === -1);
|
|
327
|
+
const newObjKeys = Object.keys(newObj).filter((key) => keysToSkip.indexOf(key) === -1);
|
|
310
328
|
const intersectionKeys = intersection(oldObjKeys, newObjKeys);
|
|
311
329
|
for (k of intersectionKeys) {
|
|
312
330
|
newPath = path.concat([k]);
|
|
313
331
|
newKeyPath = skipPath ? keyPath : keyPath.concat([k]);
|
|
314
|
-
const diffs = compare(oldObj[k], newObj[k], newPath, embeddedObjKeys, newKeyPath);
|
|
332
|
+
const diffs = compare(oldObj[k], newObj[k], newPath, embeddedObjKeys, newKeyPath, keysToSkip);
|
|
315
333
|
if (diffs.length) {
|
|
316
334
|
changes = changes.concat(diffs);
|
|
317
335
|
}
|
|
@@ -338,12 +356,12 @@ const compareObject = (oldObj, newObj, path, embeddedObjKeys, keyPath, skipPath
|
|
|
338
356
|
}
|
|
339
357
|
return changes;
|
|
340
358
|
};
|
|
341
|
-
const compareArray = (oldObj, newObj, path, embeddedObjKeys, keyPath) => {
|
|
359
|
+
const compareArray = (oldObj, newObj, path, embeddedObjKeys, keyPath, keysToSkip) => {
|
|
342
360
|
const left = getObjectKey(embeddedObjKeys, keyPath);
|
|
343
361
|
const uniqKey = left != null ? left : '$index';
|
|
344
362
|
const indexedOldObj = convertArrayToObj(oldObj, uniqKey);
|
|
345
363
|
const indexedNewObj = convertArrayToObj(newObj, uniqKey);
|
|
346
|
-
const diffs = compareObject(indexedOldObj, indexedNewObj, path, embeddedObjKeys, keyPath, true);
|
|
364
|
+
const diffs = compareObject(indexedOldObj, indexedNewObj, path, embeddedObjKeys, keyPath, true, keysToSkip);
|
|
347
365
|
if (diffs.length) {
|
|
348
366
|
return [
|
|
349
367
|
{
|
|
@@ -382,7 +400,12 @@ const getObjectKey = (embeddedObjKeys, keyPath) => {
|
|
|
382
400
|
};
|
|
383
401
|
const convertArrayToObj = (arr, uniqKey) => {
|
|
384
402
|
let obj = {};
|
|
385
|
-
if (uniqKey
|
|
403
|
+
if (uniqKey === '$value') {
|
|
404
|
+
arr.forEach((value) => {
|
|
405
|
+
obj[value] = value;
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
else if (uniqKey !== '$index') {
|
|
386
409
|
obj = keyBy(arr, uniqKey);
|
|
387
410
|
}
|
|
388
411
|
else {
|
|
@@ -426,6 +449,9 @@ const removeKey = (obj, key, embeddedKey) => {
|
|
|
426
449
|
}
|
|
427
450
|
};
|
|
428
451
|
const indexOfItemInArray = (arr, key, value) => {
|
|
452
|
+
if (key === '$value') {
|
|
453
|
+
return arr.indexOf(value);
|
|
454
|
+
}
|
|
429
455
|
for (let i = 0; i < arr.length; i++) {
|
|
430
456
|
const item = arr[i];
|
|
431
457
|
if (item && item[key] ? item[key].toString() === value.toString() : undefined) {
|
|
@@ -465,6 +491,12 @@ const applyArrayChange = (arr, change) => (() => {
|
|
|
465
491
|
if (change.embeddedKey === '$index') {
|
|
466
492
|
element = arr[subchange.key];
|
|
467
493
|
}
|
|
494
|
+
else if (change.embeddedKey === '$value') {
|
|
495
|
+
const index = arr.indexOf(subchange.key);
|
|
496
|
+
if (index !== -1) {
|
|
497
|
+
element = arr[index];
|
|
498
|
+
}
|
|
499
|
+
}
|
|
468
500
|
else {
|
|
469
501
|
element = find(arr, (el) => el[change.embeddedKey]?.toString() === subchange.key.toString());
|
|
470
502
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-diff-ts",
|
|
3
|
-
"version": "2.1
|
|
3
|
+
"version": "2.2.1",
|
|
4
4
|
"description": "A JSON diff tool for JavaScript written in TypeScript.",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"build": "tsc",
|
|
10
10
|
"format": "prettier --write \"src/**/*.ts\"",
|
|
11
11
|
"lint": "eslint 'src/**/*.ts'",
|
|
12
|
-
"test": "NODE_OPTIONS=--experimental-vm-modules
|
|
13
|
-
"test:watch": "NODE_OPTIONS=--experimental-vm-modules
|
|
12
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.mjs",
|
|
13
|
+
"test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch --config jest.config.mjs",
|
|
14
14
|
"prepare": "npm run build",
|
|
15
15
|
"prepublishOnly": "npm test && npm run lint",
|
|
16
16
|
"preversion": "npm run lint",
|
|
@@ -37,15 +37,17 @@
|
|
|
37
37
|
"url": "https://github.com/ltwlf/json-diff-ts/issues"
|
|
38
38
|
},
|
|
39
39
|
"homepage": "https://github.com/ltwlf/json-diff-ts#readme",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"lodash-es": "^4.17.21"
|
|
42
|
+
},
|
|
40
43
|
"devDependencies": {
|
|
41
44
|
"@jest/globals": "^29.7.0",
|
|
42
45
|
"@types/jest": "^29.5.7",
|
|
43
46
|
"@types/lodash-es": "^4.17.10",
|
|
44
47
|
"eslint": "^8.53.0",
|
|
45
|
-
"jest": "^29.
|
|
46
|
-
"lodash-es": "^4.17.21",
|
|
48
|
+
"jest": "^29.7.0",
|
|
47
49
|
"prettier": "^3.0.3",
|
|
48
50
|
"ts-jest": "^29.0.5",
|
|
49
|
-
"typescript": "^
|
|
51
|
+
"typescript": "^5.3.2"
|
|
50
52
|
}
|
|
51
53
|
}
|