UNPKG

10.5 kBJavaScriptView Raw
1function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
2
3function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
4
5function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
6
7function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
8
9import isDate from 'is-date-object';
10import isArguments from 'is-arguments';
11import isPrimitive from 'is-primitive';
12import isObject from 'is-object';
13import isBuffer from 'is-buffer';
14import isString from 'is-string';
15import isError from 'is-error-x';
16import isMap from 'is-map-x';
17import isSet from 'is-set-x';
18import isNil from 'is-nil-x';
19import isRegExp from 'is-regexp-x';
20import indexOf from 'index-of-x';
21import slice from 'array-slice-x';
22import some from 'array-some-x';
23import filter from 'array-filter-x';
24import sort from 'stable';
25import $keys from 'object-keys-x';
26import $getPrototypeOf from 'get-prototype-of-x';
27import hasBoxedString from 'has-boxed-string-x';
28import toBoolean from 'to-boolean-x'; // Check failure of by-index access of string characters (IE < 9)
29// and failure of `0 in boxedString` (Rhino)
30
31var hasBoxedStringBug = hasBoxedString === false; // Used to detect unsigned integer values.
32
33var reIsUint = /^(?:0|[1-9]\d*)$/;
34/* eslint-disable-next-line compat/compat */
35
36var hasMapEnumerables = typeof Map === 'function' ? $keys(new Map()) : [];
37/* eslint-disable-next-line compat/compat */
38
39var hasSetEnumerables = typeof Set === 'function' ? $keys(new Set()) : [];
40var hasErrorEnumerables;
41
42try {
43 // noinspection ExceptionCaughtLocallyJS
44 throw new Error('a');
45} catch (e) {
46 hasErrorEnumerables = $keys(e);
47}
48
49var indexNotFound = -1;
50var maxSafeIndex = 4294967295; // (2^32)-1
51
52/**
53 * Checks if `value` is a valid string index. Specifically for boxed string
54 * bug fix and not general purpose.
55 *
56 * @private
57 * @param {*} value - The value to check.
58 * @returns {boolean} Returns `true` if `value` is valid index, else `false`.
59 */
60
61var isIndex = function isIndex(value) {
62 var num = indexNotFound;
63
64 if (reIsUint.test(value)) {
65 num = Number(value);
66 }
67
68 return num > indexNotFound && num % 1 === 0 && num < maxSafeIndex;
69}; // eslint-disable jsdoc/require-param
70// noinspection JSCommentMatchesSignature
71
72/**
73 * Get an object's key avoiding boxed string bug. Specifically for boxed
74 * string bug fix and not general purpose.
75 *
76 * @private
77 * @param {Array|string|object} object - The object to get the `value` from.
78 * @param {string|number} key - The `key` reference to the `value`.
79 * @param {boolean} isStr - Is the object a string.
80 * @param {boolean} isIdx - Is the `key` a character index.
81 * @returns {*} Returns the `value` referenced by the `key`.
82 */
83// eslint-enable jsdoc/require-param
84
85
86var getItem = function getItem(args) {
87 var _args = _slicedToArray(args, 4),
88 object = _args[0],
89 key = _args[1],
90 isStr = _args[2],
91 isIdx = _args[3];
92
93 return isStr && isIdx ? object.charAt(key) : object[key];
94};
95/**
96 * Filter `keys` of unwanted Error enumerables. Specifically for Error has
97 * unwanted enumerables fix and not general purpose.
98 *
99 * @private
100 * @param {Array} keys - The Error object's keys.
101 * @param {Array} unwanted - The unwanted keys.
102 * @returns {Array} Returns the filtered keys.
103 */
104
105
106var filterUnwanted = function filterUnwanted(keys, unwanted) {
107 return unwanted.length ? filter(keys, function predicate(key) {
108 return indexOf(unwanted, key) === indexNotFound;
109 }) : keys;
110}; // eslint-disable jsdoc/require-param
111// noinspection JSCommentMatchesSignature
112
113/**
114 * Tests for deep equality. Primitive values are compared with the equal
115 * comparison operator ( == ). This only considers enumerable properties.
116 * It does not test object prototypes, attached symbols, or non-enumerable
117 * properties. This can lead to some potentially surprising results. If
118 * `strict` is `true` then Primitive values are compared with the strict
119 * equal comparison operator ( === ).
120 *
121 * @private
122 * @param {*} actual - First comparison object.
123 * @param {*} expected - Second comparison object.
124 * @param {boolean} [strict] - Comparison mode. If set to `true` use `===`.
125 * @param {object} previousStack - The circular stack.
126 * @returns {boolean} `true` if `actual` and `expected` are deemed equal,
127 * otherwise `false`.
128 */
129// eslint-enable jsdoc/require-param
130
131
132var baseDeepEqual = function baseDeepEqual(args) {
133 var _args2 = _slicedToArray(args, 4),
134 actual = _args2[0],
135 expected = _args2[1],
136 strict = _args2[2],
137 previousStack = _args2[3]; // 7.1. All identical values are equivalent, as determined by ===.
138
139
140 if (actual === expected) {
141 return true;
142 }
143
144 if (isBuffer(actual) && isBuffer(expected)) {
145 return actual.length === expected.length && some(actual, function predicate(item, index) {
146 return item !== expected[index];
147 }) === false;
148 } // 7.2. If the expected value is a Date object, the actual value is
149 // equivalent if it is also a Date object that refers to the same time.
150
151
152 if (isDate(actual) && isDate(expected)) {
153 return actual.getTime() === expected.getTime();
154 } // 7.3 If the expected value is a RegExp object, the actual value is
155 // equivalent if it is also a RegExp object with the same `source` and
156 // properties (`global`, `multiline`, `lastIndex`, `ignoreCase` & `sticky`).
157
158
159 if (isRegExp(actual) && isRegExp(expected)) {
160 return actual.toString() === expected.toString() && actual.lastIndex === expected.lastIndex;
161 } // 7.4. Other pairs that do not both pass typeof value == 'object',
162 // equivalence is determined by == or strict ===.
163
164
165 if (isObject(actual) === false && isObject(expected) === false) {
166 if (strict) {
167 return actual === expected;
168 } // noinspection EqualityComparisonWithCoercionJS
169
170
171 return actual == expected;
172 /* eslint-disable-line eqeqeq */
173 } // 7.5 For all other Object pairs, including Array objects, equivalence is
174 // determined by having the same number of owned properties (as verified
175 // with Object.prototype.hasOwnProperty.call), the same set of keys
176 // (although not necessarily the same order), equivalent values for every
177 // corresponding key, and an identical 'prototype' property. Note: this
178 // accounts for both named and indexed properties on Arrays.
179
180
181 if (isNil(actual) || isNil(expected)) {
182 return false;
183 }
184 /* jshint eqnull:false */
185 // This only considers enumerable properties. It does not test object
186 // prototypes, attached symbols, or non-enumerable properties. This can
187 // lead to some potentially surprising results.
188
189
190 if (strict && $getPrototypeOf(actual) !== $getPrototypeOf(expected)) {
191 return false;
192 } // if one is actual primitive, the other must be same
193
194
195 if (isPrimitive(actual) || isPrimitive(expected)) {
196 return actual === expected;
197 }
198
199 var ka = isArguments(actual);
200 var kb = isArguments(expected);
201 var aNotB = ka && kb === false;
202 var bNotA = ka === false && kb;
203
204 if (aNotB || bNotA) {
205 return false;
206 }
207
208 if (ka) {
209 if (ka.length !== kb.length) {
210 return false;
211 }
212
213 return baseDeepEqual([slice(actual), slice(expected), strict, null]);
214 }
215
216 ka = $keys(actual);
217 kb = $keys(expected); // having the same number of owned properties (keys incorporates hasOwnProperty)
218
219 if (ka.length !== kb.length) {
220 return false;
221 }
222
223 if (isObject(actual)) {
224 if (isError(actual)) {
225 ka = filterUnwanted(ka, hasErrorEnumerables);
226 } else if (isMap(actual)) {
227 ka = filterUnwanted(ka, hasMapEnumerables);
228 } else if (isSet(actual)) {
229 ka = filterUnwanted(ka, hasSetEnumerables);
230 }
231 }
232
233 if (isObject(expected)) {
234 if (isError(expected)) {
235 kb = filterUnwanted(kb, hasErrorEnumerables);
236 } else if (isMap(expected)) {
237 kb = filterUnwanted(kb, hasMapEnumerables);
238 } else if (isSet(expected)) {
239 kb = filterUnwanted(kb, hasSetEnumerables);
240 }
241 } // the same set of keys (although not necessarily the same order),
242
243
244 sort.inplace(ka);
245 sort.inplace(kb);
246 var aIsString;
247 var bIsString;
248
249 if (hasBoxedStringBug) {
250 aIsString = isString(actual);
251 bIsString = isString(expected);
252 } // ~~~cheap key test
253 // equivalent values for every corresponding key, and
254 // ~~~possibly expensive deep test
255
256
257 return some(ka, function predicate(key, index) {
258 if (key !== kb[index]) {
259 return true;
260 }
261
262 var isIdx = (aIsString || bIsString) && isIndex(key);
263 var stack = previousStack || [actual];
264 var item = getItem([actual, key, aIsString, isIdx]);
265 var isPrim = isPrimitive(item);
266
267 if (isPrim === false) {
268 if (indexOf(stack, item) !== indexNotFound) {
269 throw new RangeError('Circular object');
270 }
271
272 stack.push(item);
273 }
274
275 var result = baseDeepEqual([item, getItem([expected, key, bIsString, isIdx]), strict, stack]) === false;
276
277 if (isPrim === false) {
278 stack.pop();
279 }
280
281 return result;
282 }) === false;
283}; // eslint-enable jsdoc/require-param
284// noinspection JSCommentMatchesSignature
285
286/**
287 * Tests for deep equality. Primitive values are compared with the equal
288 * comparison operator ( == ). This only considers enumerable properties.
289 * It does not test object prototypes, attached symbols, or non-enumerable
290 * properties. This can lead to some potentially surprising results. If
291 * `strict` is `true` then Primitive values are compared with the strict
292 * equal comparison operator ( === ).
293 *
294 * @param {*} actual - First comparison object.
295 * @param {*} expected - Second comparison object.
296 * @param {boolean} [strict] - Comparison mode. If set to `true` use `===`.
297 * @returns {boolean} `true` if `actual` and `expected` are deemed equal,
298 * otherwise `false`.
299 * @see https://nodejs.org/api/assert.html
300 */
301// eslint-disable jsdoc/require-param
302
303
304var deepEqual = function deepEqual(actual, expected) {
305 /* eslint-disable-next-line prefer-rest-params */
306 return baseDeepEqual([actual, expected, toBoolean(arguments[2])]);
307};
308
309export default deepEqual;
310
311//# sourceMappingURL=deep-equal-x.esm.js.map
\No newline at end of file