| 63 | // This function handles the presence of circular references by bailing out when encountering an |
| 64 | // object that is already on the "stack" of items being processed. Accepts an optional replacer |
| 65 | export function canonicalize( |
| 66 | obj: any, |
| 67 | stack: Array<any> | null, replacementStack: Array<any> | null, |
| 68 | replacer: (k: string, v: any) => any, |
| 69 | key?: string |
| 70 | ) { |
| 71 | stack = stack || []; |
| 72 | replacementStack = replacementStack || []; |
| 73 | |
| 74 | if (replacer) { |
| 75 | obj = replacer(key === undefined ? '' : key, obj); |
| 76 | } |
| 77 | |
| 78 | let i; |
| 79 | |
| 80 | for (i = 0; i < stack.length; i += 1) { |
| 81 | if (stack[i] === obj) { |
| 82 | return replacementStack[i]; |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | let canonicalizedObj: any; |
| 87 | |
| 88 | if ('[object Array]' === Object.prototype.toString.call(obj)) { |
| 89 | stack.push(obj); |
| 90 | canonicalizedObj = new Array(obj.length); |
| 91 | replacementStack.push(canonicalizedObj); |
| 92 | for (i = 0; i < obj.length; i += 1) { |
| 93 | canonicalizedObj[i] = canonicalize(obj[i], stack, replacementStack, replacer, String(i)); |
| 94 | } |
| 95 | stack.pop(); |
| 96 | replacementStack.pop(); |
| 97 | return canonicalizedObj; |
| 98 | } |
| 99 | |
| 100 | if (obj && obj.toJSON) { |
| 101 | obj = obj.toJSON(); |
| 102 | } |
| 103 | |
| 104 | if (typeof obj === 'object' && obj !== null) { |
| 105 | stack.push(obj); |
| 106 | canonicalizedObj = {}; |
| 107 | replacementStack.push(canonicalizedObj); |
| 108 | const sortedKeys = []; |
| 109 | let key; |
| 110 | for (key in obj) { |
| 111 | /* istanbul ignore else */ |
| 112 | if (Object.prototype.hasOwnProperty.call(obj, key)) { |
| 113 | sortedKeys.push(key); |
| 114 | } |
| 115 | } |
| 116 | sortedKeys.sort(); |
| 117 | for (i = 0; i < sortedKeys.length; i += 1) { |
| 118 | key = sortedKeys[i]; |
| 119 | canonicalizedObj[key] = canonicalize(obj[key], stack, replacementStack, replacer, key); |
| 120 | } |
| 121 | stack.pop(); |
| 122 | replacementStack.pop(); |