|
'use strict'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var type = require('type-detect'); |
|
function FakeMap() { |
|
this._key = 'chai/deep-eql__' + Math.random() + Date.now(); |
|
} |
|
|
|
FakeMap.prototype = { |
|
get: function get(key) { |
|
return key[this._key]; |
|
}, |
|
set: function set(key, value) { |
|
if (Object.isExtensible(key)) { |
|
Object.defineProperty(key, this._key, { |
|
value: value, |
|
configurable: true, |
|
}); |
|
} |
|
}, |
|
}; |
|
|
|
var MemoizeMap = typeof WeakMap === 'function' ? WeakMap : FakeMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function memoizeCompare(leftHandOperand, rightHandOperand, memoizeMap) { |
|
|
|
if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { |
|
return null; |
|
} |
|
var leftHandMap = memoizeMap.get(leftHandOperand); |
|
if (leftHandMap) { |
|
var result = leftHandMap.get(rightHandOperand); |
|
if (typeof result === 'boolean') { |
|
return result; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function memoizeSet(leftHandOperand, rightHandOperand, memoizeMap, result) { |
|
|
|
if (!memoizeMap || isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { |
|
return; |
|
} |
|
var leftHandMap = memoizeMap.get(leftHandOperand); |
|
if (leftHandMap) { |
|
leftHandMap.set(rightHandOperand, result); |
|
} else { |
|
leftHandMap = new MemoizeMap(); |
|
leftHandMap.set(rightHandOperand, result); |
|
memoizeMap.set(leftHandOperand, leftHandMap); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
module.exports = deepEqual; |
|
module.exports.MemoizeMap = MemoizeMap; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function deepEqual(leftHandOperand, rightHandOperand, options) { |
|
|
|
if (options && options.comparator) { |
|
return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); |
|
} |
|
|
|
var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); |
|
if (simpleResult !== null) { |
|
return simpleResult; |
|
} |
|
|
|
|
|
return extensiveDeepEqual(leftHandOperand, rightHandOperand, options); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function simpleEqual(leftHandOperand, rightHandOperand) { |
|
|
|
if (leftHandOperand === rightHandOperand) { |
|
|
|
return leftHandOperand !== 0 || 1 / leftHandOperand === 1 / rightHandOperand; |
|
} |
|
|
|
|
|
if ( |
|
leftHandOperand !== leftHandOperand && |
|
rightHandOperand !== rightHandOperand |
|
) { |
|
return true; |
|
} |
|
|
|
|
|
|
|
if (isPrimitive(leftHandOperand) || isPrimitive(rightHandOperand)) { |
|
|
|
return false; |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function extensiveDeepEqual(leftHandOperand, rightHandOperand, options) { |
|
options = options || {}; |
|
options.memoize = options.memoize === false ? false : options.memoize || new MemoizeMap(); |
|
var comparator = options && options.comparator; |
|
|
|
|
|
var memoizeResultLeft = memoizeCompare(leftHandOperand, rightHandOperand, options.memoize); |
|
if (memoizeResultLeft !== null) { |
|
return memoizeResultLeft; |
|
} |
|
var memoizeResultRight = memoizeCompare(rightHandOperand, leftHandOperand, options.memoize); |
|
if (memoizeResultRight !== null) { |
|
return memoizeResultRight; |
|
} |
|
|
|
|
|
if (comparator) { |
|
var comparatorResult = comparator(leftHandOperand, rightHandOperand); |
|
|
|
if (comparatorResult === false || comparatorResult === true) { |
|
memoizeSet(leftHandOperand, rightHandOperand, options.memoize, comparatorResult); |
|
return comparatorResult; |
|
} |
|
|
|
|
|
var simpleResult = simpleEqual(leftHandOperand, rightHandOperand); |
|
if (simpleResult !== null) { |
|
|
|
return simpleResult; |
|
} |
|
} |
|
|
|
var leftHandType = type(leftHandOperand); |
|
if (leftHandType !== type(rightHandOperand)) { |
|
memoizeSet(leftHandOperand, rightHandOperand, options.memoize, false); |
|
return false; |
|
} |
|
|
|
|
|
memoizeSet(leftHandOperand, rightHandOperand, options.memoize, true); |
|
|
|
var result = extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options); |
|
memoizeSet(leftHandOperand, rightHandOperand, options.memoize, result); |
|
return result; |
|
} |
|
|
|
function extensiveDeepEqualByType(leftHandOperand, rightHandOperand, leftHandType, options) { |
|
switch (leftHandType) { |
|
case 'String': |
|
case 'Number': |
|
case 'Boolean': |
|
case 'Date': |
|
|
|
return deepEqual(leftHandOperand.valueOf(), rightHandOperand.valueOf()); |
|
case 'Promise': |
|
case 'Symbol': |
|
case 'function': |
|
case 'WeakMap': |
|
case 'WeakSet': |
|
return leftHandOperand === rightHandOperand; |
|
case 'Error': |
|
return keysEqual(leftHandOperand, rightHandOperand, [ 'name', 'message', 'code' ], options); |
|
case 'Arguments': |
|
case 'Int8Array': |
|
case 'Uint8Array': |
|
case 'Uint8ClampedArray': |
|
case 'Int16Array': |
|
case 'Uint16Array': |
|
case 'Int32Array': |
|
case 'Uint32Array': |
|
case 'Float32Array': |
|
case 'Float64Array': |
|
case 'Array': |
|
return iterableEqual(leftHandOperand, rightHandOperand, options); |
|
case 'RegExp': |
|
return regexpEqual(leftHandOperand, rightHandOperand); |
|
case 'Generator': |
|
return generatorEqual(leftHandOperand, rightHandOperand, options); |
|
case 'DataView': |
|
return iterableEqual(new Uint8Array(leftHandOperand.buffer), new Uint8Array(rightHandOperand.buffer), options); |
|
case 'ArrayBuffer': |
|
return iterableEqual(new Uint8Array(leftHandOperand), new Uint8Array(rightHandOperand), options); |
|
case 'Set': |
|
return entriesEqual(leftHandOperand, rightHandOperand, options); |
|
case 'Map': |
|
return entriesEqual(leftHandOperand, rightHandOperand, options); |
|
case 'Temporal.PlainDate': |
|
case 'Temporal.PlainTime': |
|
case 'Temporal.PlainDateTime': |
|
case 'Temporal.Instant': |
|
case 'Temporal.ZonedDateTime': |
|
case 'Temporal.PlainYearMonth': |
|
case 'Temporal.PlainMonthDay': |
|
return leftHandOperand.equals(rightHandOperand); |
|
case 'Temporal.Duration': |
|
return leftHandOperand.total('nanoseconds') === rightHandOperand.total('nanoseconds'); |
|
case 'Temporal.TimeZone': |
|
case 'Temporal.Calendar': |
|
return leftHandOperand.toString() === rightHandOperand.toString(); |
|
default: |
|
return objectEqual(leftHandOperand, rightHandOperand, options); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function regexpEqual(leftHandOperand, rightHandOperand) { |
|
return leftHandOperand.toString() === rightHandOperand.toString(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function entriesEqual(leftHandOperand, rightHandOperand, options) { |
|
|
|
if (leftHandOperand.size !== rightHandOperand.size) { |
|
return false; |
|
} |
|
if (leftHandOperand.size === 0) { |
|
return true; |
|
} |
|
var leftHandItems = []; |
|
var rightHandItems = []; |
|
leftHandOperand.forEach(function gatherEntries(key, value) { |
|
leftHandItems.push([ key, value ]); |
|
}); |
|
rightHandOperand.forEach(function gatherEntries(key, value) { |
|
rightHandItems.push([ key, value ]); |
|
}); |
|
return iterableEqual(leftHandItems.sort(), rightHandItems.sort(), options); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function iterableEqual(leftHandOperand, rightHandOperand, options) { |
|
var length = leftHandOperand.length; |
|
if (length !== rightHandOperand.length) { |
|
return false; |
|
} |
|
if (length === 0) { |
|
return true; |
|
} |
|
var index = -1; |
|
while (++index < length) { |
|
if (deepEqual(leftHandOperand[index], rightHandOperand[index], options) === false) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function generatorEqual(leftHandOperand, rightHandOperand, options) { |
|
return iterableEqual(getGeneratorEntries(leftHandOperand), getGeneratorEntries(rightHandOperand), options); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function hasIteratorFunction(target) { |
|
return typeof Symbol !== 'undefined' && |
|
typeof target === 'object' && |
|
typeof Symbol.iterator !== 'undefined' && |
|
typeof target[Symbol.iterator] === 'function'; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getIteratorEntries(target) { |
|
if (hasIteratorFunction(target)) { |
|
try { |
|
return getGeneratorEntries(target[Symbol.iterator]()); |
|
} catch (iteratorError) { |
|
return []; |
|
} |
|
} |
|
return []; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getGeneratorEntries(generator) { |
|
var generatorResult = generator.next(); |
|
var accumulator = [ generatorResult.value ]; |
|
while (generatorResult.done === false) { |
|
generatorResult = generator.next(); |
|
accumulator.push(generatorResult.value); |
|
} |
|
return accumulator; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getEnumerableKeys(target) { |
|
var keys = []; |
|
for (var key in target) { |
|
keys.push(key); |
|
} |
|
return keys; |
|
} |
|
|
|
function getEnumerableSymbols(target) { |
|
var keys = []; |
|
var allKeys = Object.getOwnPropertySymbols(target); |
|
for (var i = 0; i < allKeys.length; i += 1) { |
|
var key = allKeys[i]; |
|
if (Object.getOwnPropertyDescriptor(target, key).enumerable) { |
|
keys.push(key); |
|
} |
|
} |
|
return keys; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function keysEqual(leftHandOperand, rightHandOperand, keys, options) { |
|
var length = keys.length; |
|
if (length === 0) { |
|
return true; |
|
} |
|
for (var i = 0; i < length; i += 1) { |
|
if (deepEqual(leftHandOperand[keys[i]], rightHandOperand[keys[i]], options) === false) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function objectEqual(leftHandOperand, rightHandOperand, options) { |
|
var leftHandKeys = getEnumerableKeys(leftHandOperand); |
|
var rightHandKeys = getEnumerableKeys(rightHandOperand); |
|
var leftHandSymbols = getEnumerableSymbols(leftHandOperand); |
|
var rightHandSymbols = getEnumerableSymbols(rightHandOperand); |
|
leftHandKeys = leftHandKeys.concat(leftHandSymbols); |
|
rightHandKeys = rightHandKeys.concat(rightHandSymbols); |
|
|
|
if (leftHandKeys.length && leftHandKeys.length === rightHandKeys.length) { |
|
if (iterableEqual(mapSymbols(leftHandKeys).sort(), mapSymbols(rightHandKeys).sort()) === false) { |
|
return false; |
|
} |
|
return keysEqual(leftHandOperand, rightHandOperand, leftHandKeys, options); |
|
} |
|
|
|
var leftHandEntries = getIteratorEntries(leftHandOperand); |
|
var rightHandEntries = getIteratorEntries(rightHandOperand); |
|
if (leftHandEntries.length && leftHandEntries.length === rightHandEntries.length) { |
|
leftHandEntries.sort(); |
|
rightHandEntries.sort(); |
|
return iterableEqual(leftHandEntries, rightHandEntries, options); |
|
} |
|
|
|
if (leftHandKeys.length === 0 && |
|
leftHandEntries.length === 0 && |
|
rightHandKeys.length === 0 && |
|
rightHandEntries.length === 0) { |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function isPrimitive(value) { |
|
return value === null || typeof value !== 'object'; |
|
} |
|
|
|
function mapSymbols(arr) { |
|
return arr.map(function mapSymbol(entry) { |
|
if (typeof entry === 'symbol') { |
|
return entry.toString(); |
|
} |
|
|
|
return entry; |
|
}); |
|
} |
|
|