fast-equals 3.0.3 → 4.0.1-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +4 -0
- package/CHANGELOG.md +31 -0
- package/README.md +60 -44
- package/dist/fast-equals.cjs.js +353 -274
- package/dist/fast-equals.cjs.js.map +1 -1
- package/dist/fast-equals.esm.js +352 -274
- package/dist/fast-equals.esm.js.map +1 -1
- package/dist/fast-equals.js +353 -274
- package/dist/fast-equals.js.map +1 -1
- package/dist/fast-equals.min.js +1 -1
- package/dist/fast-equals.mjs +352 -274
- package/dist/fast-equals.mjs.map +1 -1
- package/index.d.ts +26 -25
- package/package.json +21 -19
- package/recipes/explicit-property-check.md +28 -0
- package/recipes/legacy-circular-equal-support.md +66 -0
- package/recipes/legacy-regexp-support.md +26 -0
- package/recipes/non-standard-properties.md +34 -0
- package/recipes/strict-property-descriptor-check.md +42 -0
- package/recipes/using-meta-in-comparison.md +23 -0
- package/src/arrays.ts +36 -0
- package/src/comparator.ts +146 -0
- package/src/dates.ts +12 -0
- package/src/index.ts +139 -0
- package/src/maps.ts +66 -0
- package/src/objects.ts +62 -0
- package/src/regexps.ts +11 -0
- package/src/sets.ts +61 -0
- package/src/utils.ts +128 -0
package/dist/fast-equals.cjs.js
CHANGED
|
@@ -2,139 +2,193 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var HAS_WEAK_MAP_SUPPORT = typeof WeakMap === 'function';
|
|
6
|
-
var keys = Object.keys;
|
|
7
5
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* @param a the value to compare against
|
|
11
|
-
* @param b the value to test
|
|
12
|
-
* @returns are the values equal by the SameValueZero principle
|
|
6
|
+
* Default equality comparator pass-through, used as the standard `isEqual` creator for
|
|
7
|
+
* use inside the built comparator.
|
|
13
8
|
*/
|
|
14
|
-
function
|
|
15
|
-
return
|
|
9
|
+
function createDefaultIsNestedEqual(comparator) {
|
|
10
|
+
return function isEqual(a, b, _indexOrKeyA, _indexOrKeyB, _parentA, _parentB, meta) {
|
|
11
|
+
return comparator(a, b, meta);
|
|
12
|
+
};
|
|
16
13
|
}
|
|
17
14
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* @returns is the value a plain object
|
|
15
|
+
* Wrap the provided `areItemsEqual` method to manage the circular cache, allowing
|
|
16
|
+
* for circular references to be safely included in the comparison without creating
|
|
17
|
+
* stack overflows.
|
|
22
18
|
*/
|
|
23
|
-
function
|
|
24
|
-
return
|
|
19
|
+
function createIsCircular(areItemsEqual) {
|
|
20
|
+
return function isCircular(a, b, isEqual, cache) {
|
|
21
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
|
|
22
|
+
return areItemsEqual(a, b, isEqual, cache);
|
|
23
|
+
}
|
|
24
|
+
var cachedA = cache.get(a);
|
|
25
|
+
var cachedB = cache.get(b);
|
|
26
|
+
if (cachedA && cachedB) {
|
|
27
|
+
return cachedA === b && cachedB === a;
|
|
28
|
+
}
|
|
29
|
+
cache.set(a, b);
|
|
30
|
+
cache.set(b, a);
|
|
31
|
+
var result = areItemsEqual(a, b, isEqual, cache);
|
|
32
|
+
cache.delete(a);
|
|
33
|
+
cache.delete(b);
|
|
34
|
+
return result;
|
|
35
|
+
};
|
|
25
36
|
}
|
|
26
37
|
/**
|
|
27
|
-
*
|
|
38
|
+
* Targeted shallow merge of two objects.
|
|
28
39
|
*
|
|
29
|
-
* @
|
|
30
|
-
*
|
|
40
|
+
* @NOTE
|
|
41
|
+
* This exists as a tinier compiled version of the `__assign` helper that
|
|
42
|
+
* `tsc` injects in case of `Object.assign` not being present.
|
|
31
43
|
*/
|
|
32
|
-
function
|
|
33
|
-
|
|
44
|
+
function merge(a, b) {
|
|
45
|
+
var merged = {};
|
|
46
|
+
for (var key in a) {
|
|
47
|
+
merged[key] = a[key];
|
|
48
|
+
}
|
|
49
|
+
for (var key in b) {
|
|
50
|
+
merged[key] = b[key];
|
|
51
|
+
}
|
|
52
|
+
return merged;
|
|
34
53
|
}
|
|
35
54
|
/**
|
|
36
|
-
*
|
|
55
|
+
* Whether the value is a plain object.
|
|
37
56
|
*
|
|
38
|
-
* @
|
|
39
|
-
*
|
|
57
|
+
* @NOTE
|
|
58
|
+
* This is a same-realm compariosn only.
|
|
40
59
|
*/
|
|
41
|
-
function
|
|
42
|
-
return
|
|
60
|
+
function isPlainObject(value) {
|
|
61
|
+
return value.constructor === Object || value.constructor == null;
|
|
43
62
|
}
|
|
44
63
|
/**
|
|
45
|
-
*
|
|
46
|
-
* object that mimics the necessary API aspects for cache purposes
|
|
47
|
-
*
|
|
48
|
-
* @returns the new cache object
|
|
64
|
+
* When the value is `Promise`-like, aka "then-able".
|
|
49
65
|
*/
|
|
50
|
-
function
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
delete: function (key) {
|
|
54
|
-
for (var index = 0; index < entries.length; ++index) {
|
|
55
|
-
if (entries[index][0] === key) {
|
|
56
|
-
entries.splice(index, 1);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
get: function (key) {
|
|
62
|
-
for (var index = 0; index < entries.length; ++index) {
|
|
63
|
-
if (entries[index][0] === key) {
|
|
64
|
-
return entries[index][1];
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
},
|
|
68
|
-
set: function (key, value) {
|
|
69
|
-
for (var index = 0; index < entries.length; ++index) {
|
|
70
|
-
if (entries[index][0] === key) {
|
|
71
|
-
entries[index][1] = value;
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
entries.push([key, value]);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
66
|
+
function isPromiseLike(value) {
|
|
67
|
+
return typeof value.then === 'function';
|
|
78
68
|
}
|
|
79
69
|
/**
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
* @returns the new cache object
|
|
70
|
+
* Whether the values passed are strictly equal or both NaN.
|
|
83
71
|
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
72
|
+
function sameValueZeroEqual(a, b) {
|
|
73
|
+
return a === b || (a !== a && b !== b);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var ARGUMENTS_TAG = '[object Arguments]';
|
|
77
|
+
var BOOLEAN_TAG = '[object Boolean]';
|
|
78
|
+
var DATE_TAG = '[object Date]';
|
|
79
|
+
var REG_EXP_TAG = '[object RegExp]';
|
|
80
|
+
var MAP_TAG = '[object Map]';
|
|
81
|
+
var NUMBER_TAG = '[object Number]';
|
|
82
|
+
var OBJECT_TAG = '[object Object]';
|
|
83
|
+
var SET_TAG = '[object Set]';
|
|
84
|
+
var STRING_TAG = '[object String]';
|
|
85
|
+
var toString = Object.prototype.toString;
|
|
86
|
+
function createComparator(_a) {
|
|
87
|
+
var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areMapsEqual = _a.areMapsEqual, areObjectsEqual = _a.areObjectsEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual, createIsNestedEqual = _a.createIsNestedEqual;
|
|
88
|
+
var isEqual = createIsNestedEqual(comparator);
|
|
89
|
+
/**
|
|
90
|
+
* compare the value of the two objects and return true if they are equivalent in values
|
|
91
|
+
*/
|
|
92
|
+
function comparator(a, b, meta) {
|
|
93
|
+
// If the items are strictly equal, no need to do a value comparison.
|
|
94
|
+
if (a === b) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
// If the items are not non-nullish objects, then the only possibility
|
|
98
|
+
// of them being equal but not strictly is if they are both `NaN`. Since
|
|
99
|
+
// `NaN` is uniquely not equal to itself, we can use self-comparison of
|
|
100
|
+
// both objects, which is faster than `isNaN()`.
|
|
101
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
|
|
102
|
+
return a !== a && b !== b;
|
|
103
|
+
}
|
|
104
|
+
// Checks are listed in order of commonality of use-case:
|
|
105
|
+
// 1. Common complex object types (plain object, array)
|
|
106
|
+
// 2. Common data values (date, regexp)
|
|
107
|
+
// 3. Less-common complex object types (map, set)
|
|
108
|
+
// 4. Less-common data values (promise, primitive wrappers)
|
|
109
|
+
// Inherently this is both subjective and assumptive, however
|
|
110
|
+
// when reviewing comparable libraries in the wild this order
|
|
111
|
+
// appears to be generally consistent.
|
|
112
|
+
// `isPlainObject` only checks against the object's own realm. Cross-realm
|
|
113
|
+
// comparisons are rare, and will be handled in the ultimate fallback, so
|
|
114
|
+
// we can avoid the `toString.call()` cost unless necessary.
|
|
115
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
116
|
+
return areObjectsEqual(a, b, isEqual, meta);
|
|
117
|
+
}
|
|
118
|
+
// `isArray()` works on subclasses and is cross-realm, so we can again avoid
|
|
119
|
+
// the `toString.call()` cost unless necessary by just checking if either
|
|
120
|
+
// and then both are arrays.
|
|
121
|
+
var aArray = Array.isArray(a);
|
|
122
|
+
var bArray = Array.isArray(b);
|
|
123
|
+
if (aArray || bArray) {
|
|
124
|
+
return aArray === bArray && areArraysEqual(a, b, isEqual, meta);
|
|
125
|
+
}
|
|
126
|
+
// Since this is a custom object, use the classic `toString.call()` to get its
|
|
127
|
+
// type. This is reasonably performant in modern environments like v8 and
|
|
128
|
+
// SpiderMonkey, and allows for cross-realm comparison when other checks like
|
|
129
|
+
// `instanceof` do not.
|
|
130
|
+
var aTag = toString.call(a);
|
|
131
|
+
if (aTag !== toString.call(b)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
if (aTag === DATE_TAG) {
|
|
135
|
+
// `getTime()` showed better results compared to alternatives like `valueOf()`
|
|
136
|
+
// or the unary `+` operator.
|
|
137
|
+
return areDatesEqual(a, b, isEqual, meta);
|
|
138
|
+
}
|
|
139
|
+
if (aTag === REG_EXP_TAG) {
|
|
140
|
+
return areRegExpsEqual(a, b, isEqual, meta);
|
|
141
|
+
}
|
|
142
|
+
if (aTag === MAP_TAG) {
|
|
143
|
+
return areMapsEqual(a, b, isEqual, meta);
|
|
144
|
+
}
|
|
145
|
+
if (aTag === SET_TAG) {
|
|
146
|
+
return areSetsEqual(a, b, isEqual, meta);
|
|
147
|
+
}
|
|
148
|
+
// If a simple object tag, then we can prioritize a simple object comparison because
|
|
149
|
+
// it is likely a custom class. If an arguments tag, it should be treated as a standard
|
|
150
|
+
// object.
|
|
151
|
+
if (aTag === OBJECT_TAG || aTag === ARGUMENTS_TAG) {
|
|
152
|
+
// The exception for value comparison is `Promise`-like contracts. These should be
|
|
153
|
+
// treated the same as standard `Promise` objects, which means strict equality.
|
|
154
|
+
return isPromiseLike(a) || isPromiseLike(b)
|
|
155
|
+
? a === b
|
|
156
|
+
: areObjectsEqual(a, b, isEqual, meta);
|
|
157
|
+
}
|
|
158
|
+
// As the penultimate fallback, check if the values passed are primitive wrappers. This
|
|
159
|
+
// is very rare in modern JS, which is why it is deprioritized compared to all other object
|
|
160
|
+
// types.
|
|
161
|
+
if (aTag === BOOLEAN_TAG || aTag === NUMBER_TAG || aTag === STRING_TAG) {
|
|
162
|
+
return sameValueZeroEqual(a.valueOf(), b.valueOf());
|
|
163
|
+
}
|
|
164
|
+
// If not matching any tags that require a specific type of comparison, then use strict
|
|
165
|
+
// equality. This is for a few reasons:
|
|
166
|
+
// - For types that cannot be introspected (`Promise`, `WeakMap`, etc.), this is the only
|
|
167
|
+
// comparison that can be made.
|
|
168
|
+
// - For types that can be introspected, but rarely have requirements to be compared
|
|
169
|
+
// (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common
|
|
170
|
+
// use-cases.
|
|
171
|
+
// - For types that can be introspected, but do not have an objective definition of what
|
|
172
|
+
// equality is (`Error`, etc.), the subjective decision was to be conservative.
|
|
173
|
+
// In all cases, these decisions should be reevaluated based on changes to the language and
|
|
174
|
+
// common development practices.
|
|
175
|
+
return a === b;
|
|
89
176
|
}
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
* create a custom isEqual handler specific to circular objects
|
|
94
|
-
*
|
|
95
|
-
* @param [isEqual] the isEqual comparator to use instead of isDeepEqual
|
|
96
|
-
* @returns the method to create the `isEqual` function
|
|
97
|
-
*/
|
|
98
|
-
function createCircularEqualCreator(isEqual) {
|
|
99
|
-
return function createCircularEqual(comparator) {
|
|
100
|
-
var _comparator = isEqual || comparator;
|
|
101
|
-
return function circularEqual(a, b, indexOrKeyA, indexOrKeyB, parentA, parentB, cache) {
|
|
102
|
-
if (cache === void 0) { cache = getNewCache(); }
|
|
103
|
-
var isCacheableA = !!a && typeof a === 'object';
|
|
104
|
-
var isCacheableB = !!b && typeof b === 'object';
|
|
105
|
-
if (isCacheableA !== isCacheableB) {
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
if (!isCacheableA && !isCacheableB) {
|
|
109
|
-
return _comparator(a, b, cache);
|
|
110
|
-
}
|
|
111
|
-
var cachedA = cache.get(a);
|
|
112
|
-
if (cachedA && cache.get(b)) {
|
|
113
|
-
return cachedA === b;
|
|
114
|
-
}
|
|
115
|
-
cache.set(a, b);
|
|
116
|
-
cache.set(b, a);
|
|
117
|
-
var result = _comparator(a, b, cache);
|
|
118
|
-
cache.delete(a);
|
|
119
|
-
cache.delete(b);
|
|
120
|
-
return result;
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
}
|
|
177
|
+
return comparator;
|
|
178
|
+
}
|
|
179
|
+
|
|
124
180
|
/**
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
* @param a the array to test
|
|
128
|
-
* @param b the array to test against
|
|
129
|
-
* @param isEqual the comparator to determine equality
|
|
130
|
-
* @param meta the meta object to pass through
|
|
131
|
-
* @returns are the arrays equal
|
|
181
|
+
* Whether the arrays are equal in value.
|
|
132
182
|
*/
|
|
133
183
|
function areArraysEqual(a, b, isEqual, meta) {
|
|
134
184
|
var index = a.length;
|
|
135
185
|
if (b.length !== index) {
|
|
136
186
|
return false;
|
|
137
187
|
}
|
|
188
|
+
// Decrementing `while` showed faster results than either incrementing or
|
|
189
|
+
// decrementing `for` loop and than an incrementing `while` loop. Declarative
|
|
190
|
+
// methods like `some` / `every` were not used to avoid incurring the garbage
|
|
191
|
+
// cost of anonymous callbacks.
|
|
138
192
|
while (index-- > 0) {
|
|
139
193
|
if (!isEqual(a[index], b[index], index, index, a, b, meta)) {
|
|
140
194
|
return false;
|
|
@@ -143,210 +197,235 @@ function areArraysEqual(a, b, isEqual, meta) {
|
|
|
143
197
|
return true;
|
|
144
198
|
}
|
|
145
199
|
/**
|
|
146
|
-
*
|
|
200
|
+
* Whether the arrays are equal in value, including circular references.
|
|
201
|
+
*/
|
|
202
|
+
var areArraysEqualCircular = createIsCircular(areArraysEqual);
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Whether the dates passed are equal in value.
|
|
147
206
|
*
|
|
148
|
-
* @
|
|
149
|
-
*
|
|
150
|
-
*
|
|
151
|
-
|
|
152
|
-
|
|
207
|
+
* @NOTE
|
|
208
|
+
* This is a standalone function instead of done inline in the comparator
|
|
209
|
+
* to allow for overrides.
|
|
210
|
+
*/
|
|
211
|
+
function areDatesEqual(a, b) {
|
|
212
|
+
return sameValueZeroEqual(a.valueOf(), b.valueOf());
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Whether the `Map`s are equal in value.
|
|
153
217
|
*/
|
|
154
218
|
function areMapsEqual(a, b, isEqual, meta) {
|
|
155
219
|
var isValueEqual = a.size === b.size;
|
|
156
|
-
if (isValueEqual
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
220
|
+
if (!isValueEqual) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
if (!a.size) {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
// The use of `forEach()` is to avoid the transpilation cost of `for...of` comparisons, and
|
|
227
|
+
// the inability to control the performance of the resulting code. It also avoids excessive
|
|
228
|
+
// iteration compared to doing comparisons of `keys()` and `values()`. As a result, though,
|
|
229
|
+
// we cannot short-circuit the iterations; bookkeeping must be done to short-circuit the
|
|
230
|
+
// equality checks themselves.
|
|
231
|
+
var matchedIndices = {};
|
|
232
|
+
var indexA = 0;
|
|
233
|
+
a.forEach(function (aValue, aKey) {
|
|
234
|
+
if (!isValueEqual) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
var hasMatch = false;
|
|
238
|
+
var matchIndexB = 0;
|
|
239
|
+
b.forEach(function (bValue, bKey) {
|
|
240
|
+
if (!hasMatch &&
|
|
241
|
+
!matchedIndices[matchIndexB] &&
|
|
242
|
+
(hasMatch =
|
|
243
|
+
isEqual(aKey, bKey, indexA, matchIndexB, a, b, meta) &&
|
|
244
|
+
isEqual(aValue, bValue, aKey, bKey, a, b, meta))) {
|
|
245
|
+
matchedIndices[matchIndexB] = true;
|
|
176
246
|
}
|
|
247
|
+
matchIndexB++;
|
|
177
248
|
});
|
|
178
|
-
|
|
249
|
+
indexA++;
|
|
250
|
+
isValueEqual = hasMatch;
|
|
251
|
+
});
|
|
179
252
|
return isValueEqual;
|
|
180
253
|
}
|
|
254
|
+
/**
|
|
255
|
+
* Whether the `Map`s are equal in value, including circular references.
|
|
256
|
+
*/
|
|
257
|
+
var areMapsEqualCircular = createIsCircular(areMapsEqual);
|
|
258
|
+
|
|
181
259
|
var OWNER = '_owner';
|
|
182
|
-
var hasOwnProperty =
|
|
260
|
+
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
183
261
|
/**
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
* @param a the object to test
|
|
187
|
-
* @param b the object to test against
|
|
188
|
-
* @param isEqual the comparator to determine equality
|
|
189
|
-
* @param meta the meta object to pass through
|
|
190
|
-
* @returns are the objects equal
|
|
262
|
+
* Whether the objects are equal in value.
|
|
191
263
|
*/
|
|
192
264
|
function areObjectsEqual(a, b, isEqual, meta) {
|
|
193
|
-
var keysA = keys(a);
|
|
265
|
+
var keysA = Object.keys(a);
|
|
194
266
|
var index = keysA.length;
|
|
195
|
-
if (keys(b).length !== index) {
|
|
267
|
+
if (Object.keys(b).length !== index) {
|
|
196
268
|
return false;
|
|
197
269
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
if (!hasOwnProperty(b, key) ||
|
|
211
|
-
!isEqual(a[key], b[key], key, key, a, b, meta)) {
|
|
270
|
+
var key;
|
|
271
|
+
// Decrementing `while` showed faster results than either incrementing or
|
|
272
|
+
// decrementing `for` loop and than an incrementing `while` loop. Declarative
|
|
273
|
+
// methods like `some` / `every` were not used to avoid incurring the garbage
|
|
274
|
+
// cost of anonymous callbacks.
|
|
275
|
+
while (index-- > 0) {
|
|
276
|
+
key = keysA[index];
|
|
277
|
+
if (key === OWNER) {
|
|
278
|
+
var reactElementA = !!a.$$typeof;
|
|
279
|
+
var reactElementB = !!b.$$typeof;
|
|
280
|
+
if ((reactElementA || reactElementB) && reactElementA !== reactElementB) {
|
|
212
281
|
return false;
|
|
213
282
|
}
|
|
214
283
|
}
|
|
284
|
+
if (!hasOwnProperty.call(b, key) ||
|
|
285
|
+
!isEqual(a[key], b[key], key, key, a, b, meta)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
215
288
|
}
|
|
216
289
|
return true;
|
|
217
290
|
}
|
|
218
291
|
/**
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* @param a the regExp to test
|
|
222
|
-
* @param b the regExp to test agains
|
|
223
|
-
* @returns are the regExps equal
|
|
292
|
+
* Whether the objects are equal in value, including circular references.
|
|
224
293
|
*/
|
|
225
|
-
var
|
|
226
|
-
|
|
227
|
-
return function areRegExpsEqual(a, b) {
|
|
228
|
-
return a.source === b.source && a.flags === b.flags;
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
return function areRegExpsEqualFallback(a, b) {
|
|
232
|
-
return (a.source === b.source &&
|
|
233
|
-
a.global === b.global &&
|
|
234
|
-
a.ignoreCase === b.ignoreCase &&
|
|
235
|
-
a.multiline === b.multiline &&
|
|
236
|
-
a.unicode === b.unicode &&
|
|
237
|
-
a.sticky === b.sticky &&
|
|
238
|
-
a.lastIndex === b.lastIndex);
|
|
239
|
-
};
|
|
240
|
-
})();
|
|
294
|
+
var areObjectsEqualCircular = createIsCircular(areObjectsEqual);
|
|
295
|
+
|
|
241
296
|
/**
|
|
242
|
-
*
|
|
297
|
+
* Whether the regexps passed are equal in value.
|
|
243
298
|
*
|
|
244
|
-
* @
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
|
|
299
|
+
* @NOTE
|
|
300
|
+
* This is a standalone function instead of done inline in the comparator
|
|
301
|
+
* to allow for overrides. An example of this would be supporting a
|
|
302
|
+
* pre-ES2015 environment where the `flags` property is not available.
|
|
303
|
+
*/
|
|
304
|
+
function areRegExpsEqual(a, b) {
|
|
305
|
+
return a.source === b.source && a.flags === b.flags;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Whether the `Set`s are equal in value.
|
|
249
310
|
*/
|
|
250
311
|
function areSetsEqual(a, b, isEqual, meta) {
|
|
251
312
|
var isValueEqual = a.size === b.size;
|
|
252
|
-
if (isValueEqual
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
313
|
+
if (!isValueEqual) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
if (!a.size) {
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
// The use of `forEach()` is to avoid the transpilation cost of `for...of` comparisons, and
|
|
320
|
+
// the inability to control the performance of the resulting code. It also avoids excessive
|
|
321
|
+
// iteration compared to doing comparisons of `keys()` and `values()`. As a result, though,
|
|
322
|
+
// we cannot short-circuit the iterations; bookkeeping must be done to short-circuit the
|
|
323
|
+
// equality checks themselves.
|
|
324
|
+
var matchedIndices = {};
|
|
325
|
+
a.forEach(function (aValue, aKey) {
|
|
326
|
+
if (!isValueEqual) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
var hasMatch = false;
|
|
330
|
+
var matchIndex = 0;
|
|
331
|
+
b.forEach(function (bValue, bKey) {
|
|
332
|
+
if (!hasMatch &&
|
|
333
|
+
!matchedIndices[matchIndex] &&
|
|
334
|
+
(hasMatch = isEqual(aValue, bValue, aKey, bKey, a, b, meta))) {
|
|
335
|
+
matchedIndices[matchIndex] = true;
|
|
268
336
|
}
|
|
337
|
+
matchIndex++;
|
|
269
338
|
});
|
|
270
|
-
|
|
339
|
+
isValueEqual = hasMatch;
|
|
340
|
+
});
|
|
271
341
|
return isValueEqual;
|
|
272
|
-
}
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Whether the `Set`s are equal in value, including circular references.
|
|
345
|
+
*/
|
|
346
|
+
var areSetsEqualCircular = createIsCircular(areSetsEqual);
|
|
273
347
|
|
|
274
|
-
var
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
348
|
+
var DEFAULT_CONFIG = Object.freeze({
|
|
349
|
+
areArraysEqual: areArraysEqual,
|
|
350
|
+
areDatesEqual: areDatesEqual,
|
|
351
|
+
areMapsEqual: areMapsEqual,
|
|
352
|
+
areObjectsEqual: areObjectsEqual,
|
|
353
|
+
areRegExpsEqual: areRegExpsEqual,
|
|
354
|
+
areSetsEqual: areSetsEqual,
|
|
355
|
+
createIsNestedEqual: createDefaultIsNestedEqual,
|
|
356
|
+
});
|
|
357
|
+
var DEFAULT_CIRCULAR_CONFIG = Object.freeze({
|
|
358
|
+
areArraysEqual: areArraysEqualCircular,
|
|
359
|
+
areDatesEqual: areDatesEqual,
|
|
360
|
+
areMapsEqual: areMapsEqualCircular,
|
|
361
|
+
areObjectsEqual: areObjectsEqualCircular,
|
|
362
|
+
areRegExpsEqual: areRegExpsEqual,
|
|
363
|
+
areSetsEqual: areSetsEqualCircular,
|
|
364
|
+
createIsNestedEqual: createDefaultIsNestedEqual,
|
|
365
|
+
});
|
|
366
|
+
var isDeepEqual = createComparator(DEFAULT_CONFIG);
|
|
367
|
+
/**
|
|
368
|
+
* Whether the items passed are deeply-equal in value.
|
|
369
|
+
*/
|
|
370
|
+
function deepEqual(a, b) {
|
|
371
|
+
return isDeepEqual(a, b, undefined);
|
|
372
|
+
}
|
|
373
|
+
var isShallowEqual = createComparator(merge(DEFAULT_CONFIG, { createIsNestedEqual: function () { return sameValueZeroEqual; } }));
|
|
374
|
+
/**
|
|
375
|
+
* Whether the items passed are shallowly-equal in value.
|
|
376
|
+
*/
|
|
377
|
+
function shallowEqual(a, b) {
|
|
378
|
+
return isShallowEqual(a, b, undefined);
|
|
379
|
+
}
|
|
380
|
+
var isCircularDeepEqual = createComparator(DEFAULT_CIRCULAR_CONFIG);
|
|
381
|
+
/**
|
|
382
|
+
* Whether the items passed are deeply-equal in value, including circular references.
|
|
383
|
+
*/
|
|
384
|
+
function circularDeepEqual(a, b) {
|
|
385
|
+
return isCircularDeepEqual(a, b, new WeakMap());
|
|
386
|
+
}
|
|
387
|
+
var isCircularShallowEqual = createComparator(merge(DEFAULT_CIRCULAR_CONFIG, {
|
|
388
|
+
createIsNestedEqual: function () { return sameValueZeroEqual; },
|
|
389
|
+
}));
|
|
390
|
+
/**
|
|
391
|
+
* Whether the items passed are shallowly-equal in value, including circular references.
|
|
392
|
+
*/
|
|
393
|
+
function circularShallowEqual(a, b) {
|
|
394
|
+
return isCircularShallowEqual(a, b, new WeakMap());
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Create a custom equality comparison method.
|
|
398
|
+
*
|
|
399
|
+
* This can be done to create very targeted comparisons in extreme hot-path scenarios
|
|
400
|
+
* where the standard methods are not performant enough, but can also be used to provide
|
|
401
|
+
* support for legacy environments that do not support expected features like
|
|
402
|
+
* `RegExp.prototype.flags` out of the box.
|
|
403
|
+
*/
|
|
404
|
+
function createCustomEqual(getComparatorOptions) {
|
|
405
|
+
return createComparator(merge(DEFAULT_CONFIG, getComparatorOptions(DEFAULT_CONFIG)));
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Create a custom equality comparison method that handles circular references. This is very
|
|
409
|
+
* similar to `createCustomEqual`, with the only difference being that `meta` expects to be
|
|
410
|
+
* populated with a `WeakMap`-like contract.
|
|
411
|
+
*
|
|
412
|
+
* This can be done to create very targeted comparisons in extreme hot-path scenarios
|
|
413
|
+
* where the standard methods are not performant enough, but can also be used to provide
|
|
414
|
+
* support for legacy environments that do not support expected features like
|
|
415
|
+
* `WeakMap` out of the box.
|
|
416
|
+
*/
|
|
417
|
+
function createCustomCircularEqual(getComparatorOptions) {
|
|
418
|
+
var comparator = createComparator(merge(DEFAULT_CIRCULAR_CONFIG, getComparatorOptions(DEFAULT_CIRCULAR_CONFIG)));
|
|
419
|
+
return (function (a, b, meta) {
|
|
420
|
+
if (meta === void 0) { meta = new WeakMap(); }
|
|
421
|
+
return comparator(a, b, meta);
|
|
422
|
+
});
|
|
340
423
|
}
|
|
341
424
|
|
|
342
|
-
var deepEqual = createComparator();
|
|
343
|
-
var shallowEqual = createComparator(function () { return sameValueZeroEqual; });
|
|
344
|
-
var circularDeepEqual = createComparator(createCircularEqualCreator());
|
|
345
|
-
var circularShallowEqual = createComparator(createCircularEqualCreator(sameValueZeroEqual));
|
|
346
|
-
|
|
347
425
|
exports.circularDeepEqual = circularDeepEqual;
|
|
348
426
|
exports.circularShallowEqual = circularShallowEqual;
|
|
349
|
-
exports.
|
|
427
|
+
exports.createCustomCircularEqual = createCustomCircularEqual;
|
|
428
|
+
exports.createCustomEqual = createCustomEqual;
|
|
350
429
|
exports.deepEqual = deepEqual;
|
|
351
430
|
exports.sameValueZeroEqual = sameValueZeroEqual;
|
|
352
431
|
exports.shallowEqual = shallowEqual;
|