123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- 'use strict';
- // Load modules
- // Declare internals
- const internals = {
- arrayType: Symbol('array'),
- bufferType: Symbol('buffer'),
- dateType: Symbol('date'),
- errorType: Symbol('error'),
- genericType: Symbol('generic'),
- mapType: Symbol('map'),
- regexType: Symbol('regex'),
- setType: Symbol('set'),
- weakMapType: Symbol('weak-map'),
- weakSetType: Symbol('weak-set'),
- mismatched: Symbol('mismatched')
- };
- internals.typeMap = {
- '[object Array]': internals.arrayType,
- '[object Date]': internals.dateType,
- '[object Error]': internals.errorType,
- '[object Map]': internals.mapType,
- '[object RegExp]': internals.regexType,
- '[object Set]': internals.setType,
- '[object WeakMap]': internals.weakMapType,
- '[object WeakSet]': internals.weakSetType
- };
- internals.SeenEntry = class {
- constructor(obj, ref) {
- this.obj = obj;
- this.ref = ref;
- }
- isSame(obj, ref) {
- return this.obj === obj && this.ref === ref;
- }
- };
- internals.getInternalType = function (obj) {
- const { typeMap, bufferType, genericType } = internals;
- if (obj instanceof Buffer) {
- return bufferType;
- }
- const objName = Object.prototype.toString.call(obj);
- return typeMap[objName] || genericType;
- };
- internals.getSharedType = function (obj, ref, checkPrototype) {
- if (checkPrototype) {
- if (Object.getPrototypeOf(obj) !== Object.getPrototypeOf(ref)) {
- return internals.mismatched;
- }
- return internals.getInternalType(obj);
- }
- const type = internals.getInternalType(obj);
- if (type !== internals.getInternalType(ref)) {
- return internals.mismatched;
- }
- return type;
- };
- internals.valueOf = function (obj) {
- const objValueOf = obj.valueOf;
- if (objValueOf === undefined) {
- return obj;
- }
- try {
- return objValueOf.call(obj);
- }
- catch (err) {
- return err;
- }
- };
- internals.hasOwnEnumerableProperty = function (obj, key) {
- return Object.prototype.propertyIsEnumerable.call(obj, key);
- };
- internals.isSetSimpleEqual = function (obj, ref) {
- for (const entry of obj) {
- if (!ref.has(entry)) {
- return false;
- }
- }
- return true;
- };
- internals.isDeepEqualObj = function (instanceType, obj, ref, options, seen) {
- const { isDeepEqual, valueOf, hasOwnEnumerableProperty } = internals;
- const { keys, getOwnPropertySymbols } = Object;
- if (instanceType === internals.arrayType) {
- if (options.part) {
- // Check if any index match any other index
- for (let i = 0; i < obj.length; ++i) {
- const objValue = obj[i];
- for (let j = 0; j < ref.length; ++j) {
- if (isDeepEqual(objValue, ref[j], options, seen)) {
- return true;
- }
- }
- }
- }
- else {
- if (obj.length !== ref.length) {
- return false;
- }
- for (let i = 0; i < obj.length; ++i) {
- if (!isDeepEqual(obj[i], ref[i], options, seen)) {
- return false;
- }
- }
- return true;
- }
- }
- else if (instanceType === internals.setType) {
- if (obj.size !== ref.size) {
- return false;
- }
- if (!internals.isSetSimpleEqual(obj, ref)) {
- // Check for deep equality
- const ref2 = new Set(ref);
- for (const objEntry of obj) {
- if (ref2.delete(objEntry)) {
- continue;
- }
- let found = false;
- for (const refEntry of ref2) {
- if (isDeepEqual(objEntry, refEntry, options, seen)) {
- ref2.delete(refEntry);
- found = true;
- break;
- }
- }
- if (!found) {
- return false;
- }
- }
- }
- }
- else if (instanceType === internals.mapType) {
- if (obj.size !== ref.size) {
- return false;
- }
- for (const [key, value] of obj) {
- if (value === undefined && !ref.has(key)) {
- return false;
- }
- if (!isDeepEqual(value, ref.get(key), options, seen)) {
- return false;
- }
- }
- }
- else if (instanceType === internals.errorType) {
- // Always check name and message
- if (obj.name !== ref.name || obj.message !== ref.message) {
- return false;
- }
- }
- // Check .valueOf()
- const valueOfObj = valueOf(obj);
- const valueOfRef = valueOf(ref);
- if (!(obj === valueOfObj && ref === valueOfRef) &&
- !isDeepEqual(valueOfObj, valueOfRef, options, seen)) {
- return false;
- }
- // Check properties
- const objKeys = keys(obj);
- if (!options.part && objKeys.length !== keys(ref).length) {
- return false;
- }
- for (let i = 0; i < objKeys.length; ++i) {
- const key = objKeys[i];
- if (!hasOwnEnumerableProperty(ref, key)) {
- return false;
- }
- if (!isDeepEqual(obj[key], ref[key], options, seen)) {
- return false;
- }
- }
- // Check symbols
- if (options.symbols) {
- const objSymbols = getOwnPropertySymbols(obj);
- const refSymbols = new Set(getOwnPropertySymbols(ref));
- for (let i = 0; i < objSymbols.length; ++i) {
- const key = objSymbols[i];
- if (hasOwnEnumerableProperty(obj, key)) {
- if (!hasOwnEnumerableProperty(ref, key)) {
- return false;
- }
- if (!isDeepEqual(obj[key], ref[key], options, seen)) {
- return false;
- }
- }
- else if (hasOwnEnumerableProperty(ref, key)) {
- return false;
- }
- refSymbols.delete(key);
- }
- for (const key of refSymbols) {
- if (hasOwnEnumerableProperty(ref, key)) {
- return false;
- }
- }
- }
- return true;
- };
- internals.isDeepEqual = function (obj, ref, options, seen) {
- if (obj === ref) { // Copied from Deep-eql, copyright(c) 2013 Jake Luer, jake@alogicalparadox.com, MIT Licensed, https://github.com/chaijs/deep-eql
- return obj !== 0 || 1 / obj === 1 / ref;
- }
- const type = typeof obj;
- if (type !== typeof ref) {
- return false;
- }
- if (type !== 'object' ||
- obj === null ||
- ref === null) {
- return obj !== obj && ref !== ref; // NaN
- }
- const instanceType = internals.getSharedType(obj, ref, !!options.prototype);
- switch (instanceType) {
- case internals.bufferType:
- return Buffer.prototype.equals.call(obj, ref);
- case internals.regexType:
- return obj.toString() === ref.toString();
- case internals.mismatched:
- return false;
- }
- for (let i = seen.length - 1; i >= 0; --i) {
- if (seen[i].isSame(obj, ref)) {
- return true; // If previous comparison failed, it would have stopped execution
- }
- }
- seen.push(new internals.SeenEntry(obj, ref));
- try {
- return !!internals.isDeepEqualObj(instanceType, obj, ref, options, seen);
- }
- finally {
- seen.pop();
- }
- };
- module.exports = function (obj, ref, options) {
- options = options || { prototype: true };
- return !!internals.isDeepEqual(obj, ref, options, []);
- };
|