codekingpro/portable-devtools
115k
1import Diff from './base.js';
2import { tokenize } from './line.js';
3class JsonDiff extends Diff {
4 constructor() {
5 super(...arguments);
6 this.tokenize = tokenize;
7 }
8 get useLongestToken() {
9 // Discriminate between two lines of pretty-printed, serialized JSON where one of them has a
10 // dangling comma and the other doesn't. Turns out including the dangling comma yields the nicest output:
11 return true;
12 }
13 castInput(value, options) {
14 const { undefinedReplacement, stringifyReplacer = (k, v) => typeof v === 'undefined' ? undefinedReplacement : v } = options;
15 return typeof value === 'string' ? value : JSON.stringify(canonicalize(value, null, null, stringifyReplacer), null, ' ');
16 }
17 equals(left, right, options) {
18 return super.equals(left.replace(/,([\r\n])/g, '$1'), right.replace(/,([\r\n])/g, '$1'), options);
19 }
20}
21export const jsonDiff = new JsonDiff();
22export function diffJson(oldStr, newStr, options) {
23 return jsonDiff.diff(oldStr, newStr, options);
24}
25// This function handles the presence of circular references by bailing out when encountering an
26// object that is already on the "stack" of items being processed. Accepts an optional replacer
27export function canonicalize(obj, stack, replacementStack, replacer, key) {
28 stack = stack || [];
29 replacementStack = replacementStack || [];
30 if (replacer) {
31 obj = replacer(key === undefined ? '' : key, obj);
32 }
33 let i;
34 for (i = 0; i < stack.length; i += 1) {
35 if (stack[i] === obj) {
36 return replacementStack[i];
37 }
38 }
39 let canonicalizedObj;
40 if ('[object Array]' === Object.prototype.toString.call(obj)) {
41 stack.push(obj);
42 canonicalizedObj = new Array(obj.length);
43 replacementStack.push(canonicalizedObj);
44 for (i = 0; i < obj.length; i += 1) {
45 canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, String(i));
46 }
47 stack.pop();
48 replacementStack.pop();
49 return canonicalizedObj;
50 }
51 if (obj && obj.toJSON) {
52 obj = obj.toJSON();
53 }
54 if (typeof obj === 'object' && obj !== null) {
55 stack.push(obj);
56 canonicalizedObj = {};
57 replacementStack.push(canonicalizedObj);
58 const sortedKeys = [];
59 let key;
60 for (key in obj) {
61 /* istanbul ignore else */
62 if (Object.prototype.hasOwnProperty.call(obj, key)) {
63 sortedKeys.push(key);
64 }
65 }
66 sortedKeys.sort();
67 for (i = 0; i < sortedKeys.length; i += 1) {
68 key = sortedKeys[i];
69 canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack, replacer, key);
70 }
71 stack.pop();
72 replacementStack.pop();
73 }
74 else {
75 canonicalizedObj = obj;
76 }
77 return canonicalizedObj;
78}
79 