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