shell.js (13243B)
1 // GENERATED, DO NOT EDIT 2 // file: deepEqual.js 3 // Copyright 2019 Ron Buckton. All rights reserved. 4 // This code is governed by the BSD license found in the LICENSE file. 5 /*--- 6 description: > 7 Compare two values structurally 8 defines: [assert.deepEqual] 9 ---*/ 10 11 assert.deepEqual = function(actual, expected, message) { 12 var format = assert.deepEqual.format; 13 var mustBeTrue = assert.deepEqual._compare(actual, expected); 14 15 // format can be slow when `actual` or `expected` are large objects, like for 16 // example the global object, so only call it when the assertion will fail. 17 if (mustBeTrue !== true) { 18 message = `Expected ${format(actual)} to be structurally equal to ${format(expected)}. ${(message || '')}`; 19 } 20 21 assert(mustBeTrue, message); 22 }; 23 24 (function() { 25 let getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 26 let join = arr => arr.join(', '); 27 function stringFromTemplate(strings, subs) { 28 let parts = strings.map((str, i) => `${i === 0 ? '' : subs[i - 1]}${str}`); 29 return parts.join(''); 30 } 31 function escapeKey(key) { 32 if (typeof key === 'symbol') return `[${String(key)}]`; 33 if (/^[a-zA-Z0-9_$]+$/.test(key)) return key; 34 return assert._formatIdentityFreeValue(key); 35 } 36 37 assert.deepEqual.format = function(value, seen) { 38 let basic = assert._formatIdentityFreeValue(value); 39 if (basic) return basic; 40 switch (value === null ? 'null' : typeof value) { 41 case 'string': 42 case 'bigint': 43 case 'number': 44 case 'boolean': 45 case 'undefined': 46 case 'null': 47 assert(false, 'values without identity should use basic formatting'); 48 break; 49 case 'symbol': 50 case 'function': 51 case 'object': 52 break; 53 default: 54 return typeof value; 55 } 56 57 if (!seen) { 58 seen = { 59 counter: 0, 60 map: new Map() 61 }; 62 } 63 let usage = seen.map.get(value); 64 if (usage) { 65 usage.used = true; 66 return `ref #${usage.id}`; 67 } 68 usage = { id: ++seen.counter, used: false }; 69 seen.map.set(value, usage); 70 71 // Properly communicating multiple references requires deferred rendering of 72 // all identity-bearing values until the outermost format call finishes, 73 // because the current value can also in appear in a not-yet-visited part of 74 // the object graph (which, when visited, will update the usage object). 75 // 76 // To preserve readability of the desired output formatting, we accomplish 77 // this deferral using tagged template literals. 78 // Evaluation closes over the usage object and returns a function that accepts 79 // "mapper" arguments for rendering the corresponding substitution values and 80 // returns an object with only a toString method which will itself be invoked 81 // when trying to use the result as a string in assert.deepEqual. 82 // 83 // For convenience, any absent mapper is presumed to be `String`, and the 84 // function itself has a toString method that self-invokes with no mappers 85 // (allowing returning the function directly when every mapper is `String`). 86 function lazyResult(strings, ...subs) { 87 function acceptMappers(...mappers) { 88 function toString() { 89 let renderings = subs.map((sub, i) => (mappers[i] || String)(sub)); 90 let rendered = stringFromTemplate(strings, renderings); 91 if (usage.used) rendered += ` as #${usage.id}`; 92 return rendered; 93 } 94 95 return { toString }; 96 } 97 98 acceptMappers.toString = () => String(acceptMappers()); 99 return acceptMappers; 100 } 101 102 let format = assert.deepEqual.format; 103 function lazyString(strings, ...subs) { 104 return { toString: () => stringFromTemplate(strings, subs) }; 105 } 106 107 if (typeof value === 'function') { 108 return lazyResult`function${value.name ? ` ${String(value.name)}` : ''}`; 109 } 110 if (typeof value !== 'object') { 111 // probably a symbol 112 return lazyResult`${value}`; 113 } 114 if (Array.isArray ? Array.isArray(value) : value instanceof Array) { 115 return lazyResult`[${value.map(value => format(value, seen))}]`(join); 116 } 117 if (value instanceof Date) { 118 return lazyResult`Date(${format(value.toISOString(), seen)})`; 119 } 120 if (value instanceof Error) { 121 return lazyResult`error ${value.name || 'Error'}(${format(value.message, seen)})`; 122 } 123 if (value instanceof RegExp) { 124 return lazyResult`${value}`; 125 } 126 if (typeof Map !== "undefined" && value instanceof Map) { 127 let contents = Array.from(value).map(pair => lazyString`${format(pair[0], seen)} => ${format(pair[1], seen)}`); 128 return lazyResult`Map {${contents}}`(join); 129 } 130 if (typeof Set !== "undefined" && value instanceof Set) { 131 let contents = Array.from(value).map(value => format(value, seen)); 132 return lazyResult`Set {${contents}}`(join); 133 } 134 135 let tag = Symbol.toStringTag && Symbol.toStringTag in value 136 ? value[Symbol.toStringTag] 137 : Object.getPrototypeOf(value) === null ? '[Object: null prototype]' : 'Object'; 138 let keys = Reflect.ownKeys(value).filter(key => getOwnPropertyDescriptor(value, key).enumerable); 139 let contents = keys.map(key => lazyString`${escapeKey(key)}: ${format(value[key], seen)}`); 140 return lazyResult`${tag ? `${tag} ` : ''}{${contents}}`(String, join); 141 }; 142 })(); 143 144 assert.deepEqual._compare = (function () { 145 var EQUAL = 1; 146 var NOT_EQUAL = -1; 147 var UNKNOWN = 0; 148 149 function deepEqual(a, b) { 150 return compareEquality(a, b) === EQUAL; 151 } 152 153 function compareEquality(a, b, cache) { 154 return compareIf(a, b, isOptional, compareOptionality) 155 || compareIf(a, b, isPrimitiveEquatable, comparePrimitiveEquality) 156 || compareIf(a, b, isObjectEquatable, compareObjectEquality, cache) 157 || NOT_EQUAL; 158 } 159 160 function compareIf(a, b, test, compare, cache) { 161 return !test(a) 162 ? !test(b) ? UNKNOWN : NOT_EQUAL 163 : !test(b) ? NOT_EQUAL : cacheComparison(a, b, compare, cache); 164 } 165 166 function tryCompareStrictEquality(a, b) { 167 return a === b ? EQUAL : UNKNOWN; 168 } 169 170 function tryCompareTypeOfEquality(a, b) { 171 return typeof a !== typeof b ? NOT_EQUAL : UNKNOWN; 172 } 173 174 function tryCompareToStringTagEquality(a, b) { 175 var aTag = Symbol.toStringTag in a ? a[Symbol.toStringTag] : undefined; 176 var bTag = Symbol.toStringTag in b ? b[Symbol.toStringTag] : undefined; 177 return aTag !== bTag ? NOT_EQUAL : UNKNOWN; 178 } 179 180 function isOptional(value) { 181 return value === undefined 182 || value === null; 183 } 184 185 function compareOptionality(a, b) { 186 return tryCompareStrictEquality(a, b) 187 || NOT_EQUAL; 188 } 189 190 function isPrimitiveEquatable(value) { 191 switch (typeof value) { 192 case 'string': 193 case 'number': 194 case 'bigint': 195 case 'boolean': 196 case 'symbol': 197 return true; 198 default: 199 return isBoxed(value); 200 } 201 } 202 203 function comparePrimitiveEquality(a, b) { 204 if (isBoxed(a)) a = a.valueOf(); 205 if (isBoxed(b)) b = b.valueOf(); 206 return tryCompareStrictEquality(a, b) 207 || tryCompareTypeOfEquality(a, b) 208 || compareIf(a, b, isNaNEquatable, compareNaNEquality) 209 || NOT_EQUAL; 210 } 211 212 function isNaNEquatable(value) { 213 return typeof value === 'number'; 214 } 215 216 function compareNaNEquality(a, b) { 217 return isNaN(a) && isNaN(b) ? EQUAL : NOT_EQUAL; 218 } 219 220 function isObjectEquatable(value) { 221 return typeof value === 'object' || typeof value === 'function'; 222 } 223 224 function compareObjectEquality(a, b, cache) { 225 if (!cache) cache = new Map(); 226 return getCache(cache, a, b) 227 || setCache(cache, a, b, EQUAL) // consider equal for now 228 || cacheComparison(a, b, tryCompareStrictEquality, cache) 229 || cacheComparison(a, b, tryCompareToStringTagEquality, cache) 230 || compareIf(a, b, isValueOfEquatable, compareValueOfEquality) 231 || compareIf(a, b, isToStringEquatable, compareToStringEquality) 232 || compareIf(a, b, isArrayLikeEquatable, compareArrayLikeEquality, cache) 233 || compareIf(a, b, isStructurallyEquatable, compareStructuralEquality, cache) 234 || compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) 235 || cacheComparison(a, b, fail, cache); 236 } 237 238 function isBoxed(value) { 239 return value instanceof String 240 || value instanceof Number 241 || value instanceof Boolean 242 || typeof Symbol === 'function' && value instanceof Symbol 243 || typeof BigInt === 'function' && value instanceof BigInt; 244 } 245 246 function isValueOfEquatable(value) { 247 return value instanceof Date; 248 } 249 250 function compareValueOfEquality(a, b) { 251 return compareIf(a.valueOf(), b.valueOf(), isPrimitiveEquatable, comparePrimitiveEquality) 252 || NOT_EQUAL; 253 } 254 255 function isToStringEquatable(value) { 256 return value instanceof RegExp; 257 } 258 259 function compareToStringEquality(a, b) { 260 return compareIf(a.toString(), b.toString(), isPrimitiveEquatable, comparePrimitiveEquality) 261 || NOT_EQUAL; 262 } 263 264 function isArrayLikeEquatable(value) { 265 return (Array.isArray ? Array.isArray(value) : value instanceof Array) 266 || (typeof Uint8Array === 'function' && value instanceof Uint8Array) 267 || (typeof Uint8ClampedArray === 'function' && value instanceof Uint8ClampedArray) 268 || (typeof Uint16Array === 'function' && value instanceof Uint16Array) 269 || (typeof Uint32Array === 'function' && value instanceof Uint32Array) 270 || (typeof Int8Array === 'function' && value instanceof Int8Array) 271 || (typeof Int16Array === 'function' && value instanceof Int16Array) 272 || (typeof Int32Array === 'function' && value instanceof Int32Array) 273 || (typeof Float32Array === 'function' && value instanceof Float32Array) 274 || (typeof Float64Array === 'function' && value instanceof Float64Array) 275 || (typeof BigUint64Array === 'function' && value instanceof BigUint64Array) 276 || (typeof BigInt64Array === 'function' && value instanceof BigInt64Array); 277 } 278 279 function compareArrayLikeEquality(a, b, cache) { 280 if (a.length !== b.length) return NOT_EQUAL; 281 for (var i = 0; i < a.length; i++) { 282 if (compareEquality(a[i], b[i], cache) === NOT_EQUAL) { 283 return NOT_EQUAL; 284 } 285 } 286 return EQUAL; 287 } 288 289 function isStructurallyEquatable(value) { 290 return !(typeof Promise === 'function' && value instanceof Promise // only comparable by reference 291 || typeof WeakMap === 'function' && value instanceof WeakMap // only comparable by reference 292 || typeof WeakSet === 'function' && value instanceof WeakSet // only comparable by reference 293 || typeof Map === 'function' && value instanceof Map // comparable via @@iterator 294 || typeof Set === 'function' && value instanceof Set); // comparable via @@iterator 295 } 296 297 function compareStructuralEquality(a, b, cache) { 298 var aKeys = []; 299 for (var key in a) aKeys.push(key); 300 301 var bKeys = []; 302 for (var key in b) bKeys.push(key); 303 304 if (aKeys.length !== bKeys.length) { 305 return NOT_EQUAL; 306 } 307 308 aKeys.sort(); 309 bKeys.sort(); 310 311 for (var i = 0; i < aKeys.length; i++) { 312 var aKey = aKeys[i]; 313 var bKey = bKeys[i]; 314 if (compareEquality(aKey, bKey, cache) === NOT_EQUAL) { 315 return NOT_EQUAL; 316 } 317 if (compareEquality(a[aKey], b[bKey], cache) === NOT_EQUAL) { 318 return NOT_EQUAL; 319 } 320 } 321 322 return compareIf(a, b, isIterableEquatable, compareIterableEquality, cache) 323 || EQUAL; 324 } 325 326 function isIterableEquatable(value) { 327 return typeof Symbol === 'function' 328 && typeof value[Symbol.iterator] === 'function'; 329 } 330 331 function compareIteratorEquality(a, b, cache) { 332 if (typeof Map === 'function' && a instanceof Map && b instanceof Map || 333 typeof Set === 'function' && a instanceof Set && b instanceof Set) { 334 if (a.size !== b.size) return NOT_EQUAL; // exit early if we detect a difference in size 335 } 336 337 var ar, br; 338 while (true) { 339 ar = a.next(); 340 br = b.next(); 341 if (ar.done) { 342 if (br.done) return EQUAL; 343 if (b.return) b.return(); 344 return NOT_EQUAL; 345 } 346 if (br.done) { 347 if (a.return) a.return(); 348 return NOT_EQUAL; 349 } 350 if (compareEquality(ar.value, br.value, cache) === NOT_EQUAL) { 351 if (a.return) a.return(); 352 if (b.return) b.return(); 353 return NOT_EQUAL; 354 } 355 } 356 } 357 358 function compareIterableEquality(a, b, cache) { 359 return compareIteratorEquality(a[Symbol.iterator](), b[Symbol.iterator](), cache); 360 } 361 362 function cacheComparison(a, b, compare, cache) { 363 var result = compare(a, b, cache); 364 if (cache && (result === EQUAL || result === NOT_EQUAL)) { 365 setCache(cache, a, b, /** @type {EQUAL | NOT_EQUAL} */(result)); 366 } 367 return result; 368 } 369 370 function fail() { 371 return NOT_EQUAL; 372 } 373 374 function setCache(cache, left, right, result) { 375 var otherCache; 376 377 otherCache = cache.get(left); 378 if (!otherCache) cache.set(left, otherCache = new Map()); 379 otherCache.set(right, result); 380 381 otherCache = cache.get(right); 382 if (!otherCache) cache.set(right, otherCache = new Map()); 383 otherCache.set(left, result); 384 } 385 386 function getCache(cache, left, right) { 387 var otherCache; 388 var result; 389 390 otherCache = cache.get(left); 391 result = otherCache && otherCache.get(right); 392 if (result) return result; 393 394 otherCache = cache.get(right); 395 result = otherCache && otherCache.get(left); 396 if (result) return result; 397 398 return UNKNOWN; 399 } 400 401 return deepEqual; 402 })();