360 lines
13 KiB
JavaScript
360 lines
13 KiB
JavaScript
|
(function (global, factory) {
|
||
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||
|
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["fast-equals"] = {}));
|
||
|
})(this, (function (exports) { 'use strict';
|
||
|
|
||
|
var HAS_WEAK_MAP_SUPPORT = typeof WeakMap === 'function';
|
||
|
var keys = Object.keys;
|
||
|
/**
|
||
|
* are the values passed strictly equal or both NaN
|
||
|
*
|
||
|
* @param a the value to compare against
|
||
|
* @param b the value to test
|
||
|
* @returns are the values equal by the SameValueZero principle
|
||
|
*/
|
||
|
function sameValueZeroEqual(a, b) {
|
||
|
return a === b || (a !== a && b !== b);
|
||
|
}
|
||
|
/**
|
||
|
* is the value a plain object
|
||
|
*
|
||
|
* @param value the value to test
|
||
|
* @returns is the value a plain object
|
||
|
*/
|
||
|
function isPlainObject(value) {
|
||
|
return value.constructor === Object || value.constructor == null;
|
||
|
}
|
||
|
/**
|
||
|
* is the value promise-like (meaning it is thenable)
|
||
|
*
|
||
|
* @param value the value to test
|
||
|
* @returns is the value promise-like
|
||
|
*/
|
||
|
function isPromiseLike(value) {
|
||
|
return !!value && typeof value.then === 'function';
|
||
|
}
|
||
|
/**
|
||
|
* is the value passed a react element
|
||
|
*
|
||
|
* @param value the value to test
|
||
|
* @returns is the value a react element
|
||
|
*/
|
||
|
function isReactElement(value) {
|
||
|
return !!(value && value.$$typeof);
|
||
|
}
|
||
|
/**
|
||
|
* in cases where WeakMap is not supported, creates a new custom
|
||
|
* object that mimics the necessary API aspects for cache purposes
|
||
|
*
|
||
|
* @returns the new cache object
|
||
|
*/
|
||
|
function getNewCacheFallback() {
|
||
|
var entries = [];
|
||
|
return {
|
||
|
delete: function (key) {
|
||
|
for (var index = 0; index < entries.length; ++index) {
|
||
|
if (entries[index][0] === key) {
|
||
|
entries.splice(index, 1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
get: function (key) {
|
||
|
for (var index = 0; index < entries.length; ++index) {
|
||
|
if (entries[index][0] === key) {
|
||
|
return entries[index][1];
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
set: function (key, value) {
|
||
|
for (var index = 0; index < entries.length; ++index) {
|
||
|
if (entries[index][0] === key) {
|
||
|
entries[index][1] = value;
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
entries.push([key, value]);
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* get a new cache object to prevent circular references
|
||
|
*
|
||
|
* @returns the new cache object
|
||
|
*/
|
||
|
var getNewCache = (function (canUseWeakMap) {
|
||
|
if (canUseWeakMap) {
|
||
|
return function _getNewCache() {
|
||
|
return new WeakMap();
|
||
|
};
|
||
|
}
|
||
|
return getNewCacheFallback;
|
||
|
})(HAS_WEAK_MAP_SUPPORT);
|
||
|
/**
|
||
|
* create a custom isEqual handler specific to circular objects
|
||
|
*
|
||
|
* @param [isEqual] the isEqual comparator to use instead of isDeepEqual
|
||
|
* @returns the method to create the `isEqual` function
|
||
|
*/
|
||
|
function createCircularEqualCreator(isEqual) {
|
||
|
return function createCircularEqual(comparator) {
|
||
|
var _comparator = isEqual || comparator;
|
||
|
return function circularEqual(a, b, indexOrKeyA, indexOrKeyB, parentA, parentB, cache) {
|
||
|
if (cache === void 0) { cache = getNewCache(); }
|
||
|
var isCacheableA = !!a && typeof a === 'object';
|
||
|
var isCacheableB = !!b && typeof b === 'object';
|
||
|
if (isCacheableA !== isCacheableB) {
|
||
|
return false;
|
||
|
}
|
||
|
if (!isCacheableA && !isCacheableB) {
|
||
|
return _comparator(a, b, cache);
|
||
|
}
|
||
|
var cachedA = cache.get(a);
|
||
|
if (cachedA && cache.get(b)) {
|
||
|
return cachedA === b;
|
||
|
}
|
||
|
cache.set(a, b);
|
||
|
cache.set(b, a);
|
||
|
var result = _comparator(a, b, cache);
|
||
|
cache.delete(a);
|
||
|
cache.delete(b);
|
||
|
return result;
|
||
|
};
|
||
|
};
|
||
|
}
|
||
|
/**
|
||
|
* are the arrays equal in value
|
||
|
*
|
||
|
* @param a the array to test
|
||
|
* @param b the array to test against
|
||
|
* @param isEqual the comparator to determine equality
|
||
|
* @param meta the meta object to pass through
|
||
|
* @returns are the arrays equal
|
||
|
*/
|
||
|
function areArraysEqual(a, b, isEqual, meta) {
|
||
|
var index = a.length;
|
||
|
if (b.length !== index) {
|
||
|
return false;
|
||
|
}
|
||
|
while (index-- > 0) {
|
||
|
if (!isEqual(a[index], b[index], index, index, a, b, meta)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
/**
|
||
|
* are the maps equal in value
|
||
|
*
|
||
|
* @param a the map to test
|
||
|
* @param b the map to test against
|
||
|
* @param isEqual the comparator to determine equality
|
||
|
* @param meta the meta map to pass through
|
||
|
* @returns are the maps equal
|
||
|
*/
|
||
|
function areMapsEqual(a, b, isEqual, meta) {
|
||
|
var isValueEqual = a.size === b.size;
|
||
|
if (isValueEqual && a.size) {
|
||
|
var matchedIndices_1 = {};
|
||
|
var indexA_1 = 0;
|
||
|
a.forEach(function (aValue, aKey) {
|
||
|
if (isValueEqual) {
|
||
|
var hasMatch_1 = false;
|
||
|
var matchIndexB_1 = 0;
|
||
|
b.forEach(function (bValue, bKey) {
|
||
|
if (!hasMatch_1 && !matchedIndices_1[matchIndexB_1]) {
|
||
|
hasMatch_1 =
|
||
|
isEqual(aKey, bKey, indexA_1, matchIndexB_1, a, b, meta) &&
|
||
|
isEqual(aValue, bValue, aKey, bKey, a, b, meta);
|
||
|
if (hasMatch_1) {
|
||
|
matchedIndices_1[matchIndexB_1] = true;
|
||
|
}
|
||
|
}
|
||
|
matchIndexB_1++;
|
||
|
});
|
||
|
indexA_1++;
|
||
|
isValueEqual = hasMatch_1;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
return isValueEqual;
|
||
|
}
|
||
|
var OWNER = '_owner';
|
||
|
var hasOwnProperty = Function.prototype.bind.call(Function.prototype.call, Object.prototype.hasOwnProperty);
|
||
|
/**
|
||
|
* are the objects equal in value
|
||
|
*
|
||
|
* @param a the object to test
|
||
|
* @param b the object to test against
|
||
|
* @param isEqual the comparator to determine equality
|
||
|
* @param meta the meta object to pass through
|
||
|
* @returns are the objects equal
|
||
|
*/
|
||
|
function areObjectsEqual(a, b, isEqual, meta) {
|
||
|
var keysA = keys(a);
|
||
|
var index = keysA.length;
|
||
|
if (keys(b).length !== index) {
|
||
|
return false;
|
||
|
}
|
||
|
if (index) {
|
||
|
var key = void 0;
|
||
|
while (index-- > 0) {
|
||
|
key = keysA[index];
|
||
|
if (key === OWNER) {
|
||
|
var reactElementA = isReactElement(a);
|
||
|
var reactElementB = isReactElement(b);
|
||
|
if ((reactElementA || reactElementB) &&
|
||
|
reactElementA !== reactElementB) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
if (!hasOwnProperty(b, key) ||
|
||
|
!isEqual(a[key], b[key], key, key, a, b, meta)) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
/**
|
||
|
* are the regExps equal in value
|
||
|
*
|
||
|
* @param a the regExp to test
|
||
|
* @param b the regExp to test agains
|
||
|
* @returns are the regExps equal
|
||
|
*/
|
||
|
var areRegExpsEqual = (function () {
|
||
|
if (/foo/g.flags === 'g') {
|
||
|
return function areRegExpsEqual(a, b) {
|
||
|
return a.source === b.source && a.flags === b.flags;
|
||
|
};
|
||
|
}
|
||
|
return function areRegExpsEqualFallback(a, b) {
|
||
|
return (a.source === b.source &&
|
||
|
a.global === b.global &&
|
||
|
a.ignoreCase === b.ignoreCase &&
|
||
|
a.multiline === b.multiline &&
|
||
|
a.unicode === b.unicode &&
|
||
|
a.sticky === b.sticky &&
|
||
|
a.lastIndex === b.lastIndex);
|
||
|
};
|
||
|
})();
|
||
|
/**
|
||
|
* are the sets equal in value
|
||
|
*
|
||
|
* @param a the set to test
|
||
|
* @param b the set to test against
|
||
|
* @param isEqual the comparator to determine equality
|
||
|
* @param meta the meta set to pass through
|
||
|
* @returns are the sets equal
|
||
|
*/
|
||
|
function areSetsEqual(a, b, isEqual, meta) {
|
||
|
var isValueEqual = a.size === b.size;
|
||
|
if (isValueEqual && a.size) {
|
||
|
var matchedIndices_2 = {};
|
||
|
a.forEach(function (aValue, aKey) {
|
||
|
if (isValueEqual) {
|
||
|
var hasMatch_2 = false;
|
||
|
var matchIndex_1 = 0;
|
||
|
b.forEach(function (bValue, bKey) {
|
||
|
if (!hasMatch_2 && !matchedIndices_2[matchIndex_1]) {
|
||
|
hasMatch_2 = isEqual(aValue, bValue, aKey, bKey, a, b, meta);
|
||
|
if (hasMatch_2) {
|
||
|
matchedIndices_2[matchIndex_1] = true;
|
||
|
}
|
||
|
}
|
||
|
matchIndex_1++;
|
||
|
});
|
||
|
isValueEqual = hasMatch_2;
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
return isValueEqual;
|
||
|
}
|
||
|
|
||
|
var HAS_MAP_SUPPORT = typeof Map === 'function';
|
||
|
var HAS_SET_SUPPORT = typeof Set === 'function';
|
||
|
var valueOf = Object.prototype.valueOf;
|
||
|
function createComparator(createIsEqual) {
|
||
|
var isEqual =
|
||
|
/* eslint-disable no-use-before-define */
|
||
|
typeof createIsEqual === 'function'
|
||
|
? createIsEqual(comparator)
|
||
|
: function (a, b, indexOrKeyA, indexOrKeyB, parentA, parentB, meta) { return comparator(a, b, meta); };
|
||
|
/* eslint-enable */
|
||
|
/**
|
||
|
* compare the value of the two objects and return true if they are equivalent in values
|
||
|
*
|
||
|
* @param a the value to test against
|
||
|
* @param b the value to test
|
||
|
* @param [meta] an optional meta object that is passed through to all equality test calls
|
||
|
* @returns are a and b equivalent in value
|
||
|
*/
|
||
|
function comparator(a, b, meta) {
|
||
|
if (a === b) {
|
||
|
return true;
|
||
|
}
|
||
|
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
||
|
if (isPlainObject(a) && isPlainObject(b)) {
|
||
|
return areObjectsEqual(a, b, isEqual, meta);
|
||
|
}
|
||
|
var aShape = Array.isArray(a);
|
||
|
var bShape = Array.isArray(b);
|
||
|
if (aShape || bShape) {
|
||
|
return aShape === bShape && areArraysEqual(a, b, isEqual, meta);
|
||
|
}
|
||
|
aShape = a instanceof Date;
|
||
|
bShape = b instanceof Date;
|
||
|
if (aShape || bShape) {
|
||
|
return (aShape === bShape && sameValueZeroEqual(a.getTime(), b.getTime()));
|
||
|
}
|
||
|
aShape = a instanceof RegExp;
|
||
|
bShape = b instanceof RegExp;
|
||
|
if (aShape || bShape) {
|
||
|
return aShape === bShape && areRegExpsEqual(a, b);
|
||
|
}
|
||
|
if (isPromiseLike(a) || isPromiseLike(b)) {
|
||
|
return a === b;
|
||
|
}
|
||
|
if (HAS_MAP_SUPPORT) {
|
||
|
aShape = a instanceof Map;
|
||
|
bShape = b instanceof Map;
|
||
|
if (aShape || bShape) {
|
||
|
return aShape === bShape && areMapsEqual(a, b, isEqual, meta);
|
||
|
}
|
||
|
}
|
||
|
if (HAS_SET_SUPPORT) {
|
||
|
aShape = a instanceof Set;
|
||
|
bShape = b instanceof Set;
|
||
|
if (aShape || bShape) {
|
||
|
return aShape === bShape && areSetsEqual(a, b, isEqual, meta);
|
||
|
}
|
||
|
}
|
||
|
if (a.valueOf !== valueOf || b.valueOf !== valueOf) {
|
||
|
return sameValueZeroEqual(a.valueOf(), b.valueOf());
|
||
|
}
|
||
|
return areObjectsEqual(a, b, isEqual, meta);
|
||
|
}
|
||
|
return a !== a && b !== b;
|
||
|
}
|
||
|
return comparator;
|
||
|
}
|
||
|
|
||
|
var deepEqual = createComparator();
|
||
|
var shallowEqual = createComparator(function () { return sameValueZeroEqual; });
|
||
|
var circularDeepEqual = createComparator(createCircularEqualCreator());
|
||
|
var circularShallowEqual = createComparator(createCircularEqualCreator(sameValueZeroEqual));
|
||
|
|
||
|
exports.circularDeepEqual = circularDeepEqual;
|
||
|
exports.circularShallowEqual = circularShallowEqual;
|
||
|
exports.createCustomEqual = createComparator;
|
||
|
exports.deepEqual = deepEqual;
|
||
|
exports.sameValueZeroEqual = sameValueZeroEqual;
|
||
|
exports.shallowEqual = shallowEqual;
|
||
|
|
||
|
Object.defineProperty(exports, '__esModule', { value: true });
|
||
|
|
||
|
}));
|
||
|
//# sourceMappingURL=fast-equals.js.map
|