fast-equals 5.0.0-beta.2 → 5.0.0-beta.3
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/README.md +1 -1
- package/dist/cjs/index.cjs +170 -185
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/types/comparator.d.ts +8 -1
- package/dist/cjs/types/equals.d.ts +0 -13
- package/dist/cjs/types/index.d.ts +11 -21
- package/dist/cjs/types/internalTypes.d.ts +8 -0
- package/dist/cjs/types/utils.d.ts +10 -0
- package/dist/esm/index.mjs +170 -185
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/types/comparator.d.ts +8 -1
- package/dist/esm/types/equals.d.ts +0 -13
- package/dist/esm/types/index.d.ts +11 -21
- package/dist/esm/types/internalTypes.d.ts +8 -0
- package/dist/esm/types/utils.d.ts +10 -0
- package/dist/min/index.js +1 -1
- package/dist/min/types/comparator.d.ts +8 -1
- package/dist/min/types/equals.d.ts +0 -13
- package/dist/min/types/index.d.ts +11 -21
- package/dist/min/types/internalTypes.d.ts +8 -0
- package/dist/min/types/utils.d.ts +10 -0
- package/dist/umd/index.js +170 -185
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/types/comparator.d.ts +8 -1
- package/dist/umd/types/equals.d.ts +0 -13
- package/dist/umd/types/index.d.ts +11 -21
- package/dist/umd/types/internalTypes.d.ts +8 -0
- package/dist/umd/types/utils.d.ts +10 -0
- package/index.d.ts +1 -21
- package/package.json +1 -1
- package/src/comparator.ts +63 -0
- package/src/equals.ts +0 -13
- package/src/index.ts +39 -126
- package/src/internalTypes.ts +11 -0
- package/src/utils.ts +10 -0
|
@@ -1,2 +1,9 @@
|
|
|
1
|
-
import type { ComparatorConfig, EqualityComparator } from './internalTypes';
|
|
1
|
+
import type { ComparatorConfig, CustomEqualCreatorOptions, EqualityComparator } from './internalTypes';
|
|
2
|
+
/**
|
|
3
|
+
* Create a comparator method based on the type-specific equality comparators passed.
|
|
4
|
+
*/
|
|
2
5
|
export declare function createComparator<Meta>({ areArraysEqual, areDatesEqual, areMapsEqual, areObjectsEqual, arePrimitiveWrappersEqual, areRegExpsEqual, areSetsEqual, }: ComparatorConfig<Meta>): EqualityComparator<Meta>;
|
|
6
|
+
/**
|
|
7
|
+
* Create the configuration object used for building comparators.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createComparatorConfig<Meta>({ circular, createCustomConfig, strict, }: CustomEqualCreatorOptions<Meta>): ComparatorConfig<Meta>;
|
|
@@ -5,10 +5,6 @@ import type { Dictionary, State } from './internalTypes';
|
|
|
5
5
|
export declare function areArraysEqual(a: any[], b: any[], state: State<any>): boolean;
|
|
6
6
|
/**
|
|
7
7
|
* Whether the dates passed are equal in value.
|
|
8
|
-
*
|
|
9
|
-
* @NOTE
|
|
10
|
-
* This is a standalone function instead of done inline in the comparator
|
|
11
|
-
* to allow for overrides.
|
|
12
8
|
*/
|
|
13
9
|
export declare function areDatesEqual(a: Date, b: Date): boolean;
|
|
14
10
|
/**
|
|
@@ -25,19 +21,10 @@ export declare function areObjectsEqual(a: Dictionary, b: Dictionary, state: Sta
|
|
|
25
21
|
export declare function areObjectsEqualStrict(a: Dictionary, b: Dictionary, state: State<any>): boolean;
|
|
26
22
|
/**
|
|
27
23
|
* Whether the primitive wrappers passed are equal in value.
|
|
28
|
-
*
|
|
29
|
-
* @NOTE
|
|
30
|
-
* This is a standalone function instead of done inline in the comparator
|
|
31
|
-
* to allow for overrides.
|
|
32
24
|
*/
|
|
33
25
|
export declare function arePrimitiveWrappersEqual(a: Date, b: Date): boolean;
|
|
34
26
|
/**
|
|
35
27
|
* Whether the regexps passed are equal in value.
|
|
36
|
-
*
|
|
37
|
-
* @NOTE
|
|
38
|
-
* This is a standalone function instead of done inline in the comparator
|
|
39
|
-
* to allow for overrides. An example of this would be supporting a
|
|
40
|
-
* pre-ES2015 environment where the `flags` property is not available.
|
|
41
28
|
*/
|
|
42
29
|
export declare function areRegExpsEqual(a: RegExp, b: RegExp): boolean;
|
|
43
30
|
/**
|
|
@@ -1,51 +1,41 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { CustomEqualCreatorOptions } from './internalTypes';
|
|
2
|
+
import { sameValueZeroEqual } from './utils';
|
|
3
3
|
export { sameValueZeroEqual };
|
|
4
4
|
export * from './internalTypes';
|
|
5
|
-
interface DefaultEqualCreatorOptions<Meta> {
|
|
6
|
-
comparator?: EqualityComparator<Meta>;
|
|
7
|
-
circular?: boolean;
|
|
8
|
-
strict?: boolean;
|
|
9
|
-
}
|
|
10
|
-
interface CustomEqualCreatorOptions<Meta> extends DefaultEqualCreatorOptions<Meta> {
|
|
11
|
-
createCustomConfig?: CreateCustomComparatorConfig<Meta>;
|
|
12
|
-
createInternalComparator?: typeof createInternalComparator;
|
|
13
|
-
createState?: CreateState<Meta>;
|
|
14
|
-
}
|
|
15
5
|
/**
|
|
16
6
|
* Whether the items passed are deeply-equal in value.
|
|
17
7
|
*/
|
|
18
|
-
export declare const deepEqual: <A, B>(a: A, b: B) => boolean;
|
|
8
|
+
export declare const deepEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
19
9
|
/**
|
|
20
10
|
* Whether the items passed are deeply-equal in value based on strict comparison.
|
|
21
11
|
*/
|
|
22
|
-
export declare const strictDeepEqual: <A, B>(a: A, b: B) => boolean;
|
|
12
|
+
export declare const strictDeepEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
23
13
|
/**
|
|
24
14
|
* Whether the items passed are deeply-equal in value, including circular references.
|
|
25
15
|
*/
|
|
26
|
-
export declare const circularDeepEqual: <A, B>(a: A, b: B) => boolean;
|
|
16
|
+
export declare const circularDeepEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
27
17
|
/**
|
|
28
18
|
* Whether the items passed are deeply-equal in value, including circular references,
|
|
29
19
|
* based on strict comparison.
|
|
30
20
|
*/
|
|
31
|
-
export declare const strictCircularDeepEqual: <A, B>(a: A, b: B) => boolean;
|
|
21
|
+
export declare const strictCircularDeepEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
32
22
|
/**
|
|
33
23
|
* Whether the items passed are shallowly-equal in value.
|
|
34
24
|
*/
|
|
35
|
-
export declare const shallowEqual: <A, B>(a: A, b: B) => boolean;
|
|
25
|
+
export declare const shallowEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
36
26
|
/**
|
|
37
27
|
* Whether the items passed are shallowly-equal in value based on strict comparison
|
|
38
28
|
*/
|
|
39
|
-
export declare const strictShallowEqual: <A, B>(a: A, b: B) => boolean;
|
|
29
|
+
export declare const strictShallowEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
40
30
|
/**
|
|
41
31
|
* Whether the items passed are shallowly-equal in value, including circular references.
|
|
42
32
|
*/
|
|
43
|
-
export declare const circularShallowEqual: <A, B>(a: A, b: B) => boolean;
|
|
33
|
+
export declare const circularShallowEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
44
34
|
/**
|
|
45
35
|
* Whether the items passed are shallowly-equal in value, including circular references,
|
|
46
36
|
* based on strict comparison.
|
|
47
37
|
*/
|
|
48
|
-
export declare const strictCircularShallowEqual: <A, B>(a: A, b: B) => boolean;
|
|
38
|
+
export declare const strictCircularShallowEqual: <A, B>(a: A, b: B, metaOverride?: unknown) => boolean;
|
|
49
39
|
/**
|
|
50
40
|
* Create a custom equality comparison method.
|
|
51
41
|
*
|
|
@@ -54,4 +44,4 @@ export declare const strictCircularShallowEqual: <A, B>(a: A, b: B) => boolean;
|
|
|
54
44
|
* support for legacy environments that do not support expected features like
|
|
55
45
|
* `RegExp.prototype.flags` out of the box.
|
|
56
46
|
*/
|
|
57
|
-
export declare function createCustomEqual<Meta>(options?: CustomEqualCreatorOptions<Meta>): <A, B>(a: A, b: B, metaOverride?: Meta) => boolean;
|
|
47
|
+
export declare function createCustomEqual<Meta>(options?: CustomEqualCreatorOptions<Meta>): <A, B>(a: A, b: B, metaOverride?: Meta | undefined) => boolean;
|
|
@@ -34,3 +34,11 @@ export type AnyEqualityComparator<Meta> = (a: any, b: any, state: State<Meta>) =
|
|
|
34
34
|
export type EqualityComparatorCreator<Meta> = (fn: EqualityComparator<Meta>) => InternalEqualityComparator<Meta>;
|
|
35
35
|
export type InternalEqualityComparator<Meta> = (a: any, b: any, indexOrKeyA: any, indexOrKeyB: any, parentA: any, parentB: any, state: State<Meta>) => boolean;
|
|
36
36
|
export type TypeEqualityComparator<Type, Meta = undefined> = (a: Type, b: Type, state: State<Meta>) => boolean;
|
|
37
|
+
export interface CustomEqualCreatorOptions<Meta> {
|
|
38
|
+
circular?: boolean;
|
|
39
|
+
comparator?: EqualityComparator<Meta>;
|
|
40
|
+
createCustomConfig?: CreateCustomComparatorConfig<Meta>;
|
|
41
|
+
createInternalComparator?: <Meta>(compare: EqualityComparator<Meta>) => InternalEqualityComparator<Meta>;
|
|
42
|
+
createState?: CreateState<Meta>;
|
|
43
|
+
strict?: boolean;
|
|
44
|
+
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { AnyEqualityComparator, Dictionary, EqualityComparator, InternalEqualityComparator, State, TypeEqualityComparator } from './internalTypes';
|
|
2
|
+
/**
|
|
3
|
+
* Combine two comparators into a single comparators.
|
|
4
|
+
*/
|
|
2
5
|
export declare function combineComparators<Meta>(comparatorA: AnyEqualityComparator<Meta>, comparatorB: AnyEqualityComparator<Meta>): <A, B>(a: A, b: B, state: State<Meta>) => boolean;
|
|
3
6
|
/**
|
|
4
7
|
* Default equality comparator pass-through, used as the standard `isEqual` creator for
|
|
@@ -11,7 +14,14 @@ export declare function createInternalComparator<Meta>(compare: EqualityComparat
|
|
|
11
14
|
* stack overflows.
|
|
12
15
|
*/
|
|
13
16
|
export declare function createIsCircular<AreItemsEqual extends TypeEqualityComparator<any, any>>(areItemsEqual: AreItemsEqual): AreItemsEqual;
|
|
17
|
+
/**
|
|
18
|
+
* Get the properties to strictly examine, which include both own properties that are
|
|
19
|
+
* not enumerable and symbol properties.
|
|
20
|
+
*/
|
|
14
21
|
export declare function getStrictProperties(object: Dictionary): Array<string | symbol>;
|
|
22
|
+
/**
|
|
23
|
+
* Whether the object contains the property passed as an own property.
|
|
24
|
+
*/
|
|
15
25
|
export declare const hasOwn: (o: object, v: PropertyKey) => boolean;
|
|
16
26
|
/**
|
|
17
27
|
* Whether the values passed are strictly equal or both NaN.
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,110 +1,8 @@
|
|
|
1
|
-
var ARGUMENTS_TAG = '[object Arguments]';
|
|
2
|
-
var BOOLEAN_TAG = '[object Boolean]';
|
|
3
|
-
var DATE_TAG = '[object Date]';
|
|
4
|
-
var MAP_TAG = '[object Map]';
|
|
5
|
-
var NUMBER_TAG = '[object Number]';
|
|
6
|
-
var OBJECT_TAG = '[object Object]';
|
|
7
|
-
var REG_EXP_TAG = '[object RegExp]';
|
|
8
|
-
var SET_TAG = '[object Set]';
|
|
9
|
-
var STRING_TAG = '[object String]';
|
|
10
|
-
var isArray = Array.isArray;
|
|
11
|
-
var getTag = Object.prototype.toString.call.bind(Object.prototype.toString);
|
|
12
|
-
function createComparator(_a) {
|
|
13
|
-
var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areMapsEqual = _a.areMapsEqual, areObjectsEqual = _a.areObjectsEqual, arePrimitiveWrappersEqual = _a.arePrimitiveWrappersEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual;
|
|
14
|
-
/**
|
|
15
|
-
* compare the value of the two objects and return true if they are equivalent in values
|
|
16
|
-
*/
|
|
17
|
-
return function comparator(a, b, state) {
|
|
18
|
-
// If the items are strictly equal, no need to do a value comparison.
|
|
19
|
-
if (a === b) {
|
|
20
|
-
return true;
|
|
21
|
-
}
|
|
22
|
-
// If the items are not non-nullish objects, then the only possibility
|
|
23
|
-
// of them being equal but not strictly is if they are both `NaN`. Since
|
|
24
|
-
// `NaN` is uniquely not equal to itself, we can use self-comparison of
|
|
25
|
-
// both objects, which is faster than `isNaN()`.
|
|
26
|
-
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
|
|
27
|
-
return a !== a && b !== b;
|
|
28
|
-
}
|
|
29
|
-
// Checks are listed in order of commonality of use-case:
|
|
30
|
-
// 1. Common complex object types (plain object, array)
|
|
31
|
-
// 2. Common data values (date, regexp)
|
|
32
|
-
// 3. Less-common complex object types (map, set)
|
|
33
|
-
// 4. Less-common data values (promise, primitive wrappers)
|
|
34
|
-
// Inherently this is both subjective and assumptive, however
|
|
35
|
-
// when reviewing comparable libraries in the wild this order
|
|
36
|
-
// appears to be generally consistent.
|
|
37
|
-
// `isPlainObject` only checks against the object's own realm. Cross-realm
|
|
38
|
-
// comparisons are rare, and will be handled in the ultimate fallback, so
|
|
39
|
-
// we can avoid the `toString.call()` cost unless necessary.
|
|
40
|
-
if (a.constructor === Object && b.constructor === Object) {
|
|
41
|
-
return areObjectsEqual(a, b, state);
|
|
42
|
-
}
|
|
43
|
-
// `isArray()` works on subclasses and is cross-realm, so we can again avoid
|
|
44
|
-
// the `toString.call()` cost unless necessary by just checking if either
|
|
45
|
-
// and then both are arrays.
|
|
46
|
-
var aArray = isArray(a);
|
|
47
|
-
var bArray = isArray(b);
|
|
48
|
-
if (aArray || bArray) {
|
|
49
|
-
return aArray === bArray && areArraysEqual(a, b, state);
|
|
50
|
-
}
|
|
51
|
-
// Since this is a custom object, use the classic `toString.call()` to get its
|
|
52
|
-
// type. This is reasonably performant in modern environments like v8 and
|
|
53
|
-
// SpiderMonkey, and allows for cross-realm comparison when other checks like
|
|
54
|
-
// `instanceof` do not.
|
|
55
|
-
var tag = getTag(a);
|
|
56
|
-
if (tag !== getTag(b)) {
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
59
|
-
if (tag === DATE_TAG) {
|
|
60
|
-
return areDatesEqual(a, b, state);
|
|
61
|
-
}
|
|
62
|
-
if (tag === REG_EXP_TAG) {
|
|
63
|
-
return areRegExpsEqual(a, b, state);
|
|
64
|
-
}
|
|
65
|
-
if (tag === MAP_TAG) {
|
|
66
|
-
return areMapsEqual(a, b, state);
|
|
67
|
-
}
|
|
68
|
-
if (tag === SET_TAG) {
|
|
69
|
-
return areSetsEqual(a, b, state);
|
|
70
|
-
}
|
|
71
|
-
// If a simple object tag, then we can prioritize a simple object comparison because
|
|
72
|
-
// it is likely a custom class.
|
|
73
|
-
if (tag === OBJECT_TAG) {
|
|
74
|
-
// The exception for value comparison is `Promise`-like contracts. These should be
|
|
75
|
-
// treated the same as standard `Promise` objects, which means strict equality, and if
|
|
76
|
-
// it reaches this point then that strict equality comparison has already failed.
|
|
77
|
-
return (typeof a.then !== 'function' &&
|
|
78
|
-
typeof b.then !== 'function' &&
|
|
79
|
-
areObjectsEqual(a, b, state));
|
|
80
|
-
}
|
|
81
|
-
// If an arguments tag, it should be treated as a standard object.
|
|
82
|
-
if (tag === ARGUMENTS_TAG) {
|
|
83
|
-
return areObjectsEqual(a, b, state);
|
|
84
|
-
}
|
|
85
|
-
// As the penultimate fallback, check if the values passed are primitive wrappers. This
|
|
86
|
-
// is very rare in modern JS, which is why it is deprioritized compared to all other object
|
|
87
|
-
// types.
|
|
88
|
-
if (tag === BOOLEAN_TAG || tag === NUMBER_TAG || tag === STRING_TAG) {
|
|
89
|
-
return arePrimitiveWrappersEqual(a, b, state);
|
|
90
|
-
}
|
|
91
|
-
// If not matching any tags that require a specific type of comparison, then we hard-code false because
|
|
92
|
-
// the only thing remaining is strict equality, which has already been compared. This is for a few reasons:
|
|
93
|
-
// - Certain types that cannot be introspected (e.g., `WeakMap`). For these types, this is the only
|
|
94
|
-
// comparison that can be made.
|
|
95
|
-
// - For types that can be introspected, but rarely have requirements to be compared
|
|
96
|
-
// (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common
|
|
97
|
-
// use-cases (may be included in a future release, if requested enough).
|
|
98
|
-
// - For types that can be introspected but do not have an objective definition of what
|
|
99
|
-
// equality is (`Error`, etc.), the subjective decision is to be conservative and strictly compare.
|
|
100
|
-
// In all cases, these decisions should be reevaluated based on changes to the language and
|
|
101
|
-
// common development practices.
|
|
102
|
-
return false;
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
1
|
var getOwnPropertyNames = Object.getOwnPropertyNames, getOwnPropertySymbols = Object.getOwnPropertySymbols;
|
|
107
2
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
3
|
+
/**
|
|
4
|
+
* Combine two comparators into a single comparators.
|
|
5
|
+
*/
|
|
108
6
|
function combineComparators(comparatorA, comparatorB) {
|
|
109
7
|
return function isEqual(a, b, state) {
|
|
110
8
|
return comparatorA(a, b, state) && comparatorB(a, b, state);
|
|
@@ -143,9 +41,16 @@ function createIsCircular(areItemsEqual) {
|
|
|
143
41
|
return result;
|
|
144
42
|
};
|
|
145
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Get the properties to strictly examine, which include both own properties that are
|
|
46
|
+
* not enumerable and symbol properties.
|
|
47
|
+
*/
|
|
146
48
|
function getStrictProperties(object) {
|
|
147
49
|
return getOwnPropertyNames(object).concat(getOwnPropertySymbols(object));
|
|
148
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Whether the object contains the property passed as an own property.
|
|
53
|
+
*/
|
|
149
54
|
var hasOwn = Object.hasOwn ||
|
|
150
55
|
(function (object, property) {
|
|
151
56
|
return hasOwnProperty.call(object, property);
|
|
@@ -176,10 +81,6 @@ function areArraysEqual(a, b, state) {
|
|
|
176
81
|
}
|
|
177
82
|
/**
|
|
178
83
|
* Whether the dates passed are equal in value.
|
|
179
|
-
*
|
|
180
|
-
* @NOTE
|
|
181
|
-
* This is a standalone function instead of done inline in the comparator
|
|
182
|
-
* to allow for overrides.
|
|
183
84
|
*/
|
|
184
85
|
function areDatesEqual(a, b) {
|
|
185
86
|
return sameValueZeroEqual(a.getTime(), b.getTime());
|
|
@@ -291,21 +192,12 @@ function areObjectsEqualStrict(a, b, state) {
|
|
|
291
192
|
}
|
|
292
193
|
/**
|
|
293
194
|
* Whether the primitive wrappers passed are equal in value.
|
|
294
|
-
*
|
|
295
|
-
* @NOTE
|
|
296
|
-
* This is a standalone function instead of done inline in the comparator
|
|
297
|
-
* to allow for overrides.
|
|
298
195
|
*/
|
|
299
196
|
function arePrimitiveWrappersEqual(a, b) {
|
|
300
197
|
return sameValueZeroEqual(a.valueOf(), b.valueOf());
|
|
301
198
|
}
|
|
302
199
|
/**
|
|
303
200
|
* Whether the regexps passed are equal in value.
|
|
304
|
-
*
|
|
305
|
-
* @NOTE
|
|
306
|
-
* This is a standalone function instead of done inline in the comparator
|
|
307
|
-
* to allow for overrides. An example of this would be supporting a
|
|
308
|
-
* pre-ES2015 environment where the `flags` property is not available.
|
|
309
201
|
*/
|
|
310
202
|
function areRegExpsEqual(a, b) {
|
|
311
203
|
return a.source === b.source && a.flags === b.flags;
|
|
@@ -342,94 +234,191 @@ function areSetsEqual(a, b, state) {
|
|
|
342
234
|
return isValueEqual;
|
|
343
235
|
}
|
|
344
236
|
|
|
237
|
+
var ARGUMENTS_TAG = '[object Arguments]';
|
|
238
|
+
var BOOLEAN_TAG = '[object Boolean]';
|
|
239
|
+
var DATE_TAG = '[object Date]';
|
|
240
|
+
var MAP_TAG = '[object Map]';
|
|
241
|
+
var NUMBER_TAG = '[object Number]';
|
|
242
|
+
var OBJECT_TAG = '[object Object]';
|
|
243
|
+
var REG_EXP_TAG = '[object RegExp]';
|
|
244
|
+
var SET_TAG = '[object Set]';
|
|
245
|
+
var STRING_TAG = '[object String]';
|
|
246
|
+
var isArray = Array.isArray;
|
|
247
|
+
var assign = Object.assign;
|
|
248
|
+
var getTag = Object.prototype.toString.call.bind(Object.prototype.toString);
|
|
249
|
+
/**
|
|
250
|
+
* Create a comparator method based on the type-specific equality comparators passed.
|
|
251
|
+
*/
|
|
252
|
+
function createComparator(_a) {
|
|
253
|
+
var areArraysEqual = _a.areArraysEqual, areDatesEqual = _a.areDatesEqual, areMapsEqual = _a.areMapsEqual, areObjectsEqual = _a.areObjectsEqual, arePrimitiveWrappersEqual = _a.arePrimitiveWrappersEqual, areRegExpsEqual = _a.areRegExpsEqual, areSetsEqual = _a.areSetsEqual;
|
|
254
|
+
/**
|
|
255
|
+
* compare the value of the two objects and return true if they are equivalent in values
|
|
256
|
+
*/
|
|
257
|
+
return function comparator(a, b, state) {
|
|
258
|
+
// If the items are strictly equal, no need to do a value comparison.
|
|
259
|
+
if (a === b) {
|
|
260
|
+
return true;
|
|
261
|
+
}
|
|
262
|
+
// If the items are not non-nullish objects, then the only possibility
|
|
263
|
+
// of them being equal but not strictly is if they are both `NaN`. Since
|
|
264
|
+
// `NaN` is uniquely not equal to itself, we can use self-comparison of
|
|
265
|
+
// both objects, which is faster than `isNaN()`.
|
|
266
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object') {
|
|
267
|
+
return a !== a && b !== b;
|
|
268
|
+
}
|
|
269
|
+
// Checks are listed in order of commonality of use-case:
|
|
270
|
+
// 1. Common complex object types (plain object, array)
|
|
271
|
+
// 2. Common data values (date, regexp)
|
|
272
|
+
// 3. Less-common complex object types (map, set)
|
|
273
|
+
// 4. Less-common data values (promise, primitive wrappers)
|
|
274
|
+
// Inherently this is both subjective and assumptive, however
|
|
275
|
+
// when reviewing comparable libraries in the wild this order
|
|
276
|
+
// appears to be generally consistent.
|
|
277
|
+
// `isPlainObject` only checks against the object's own realm. Cross-realm
|
|
278
|
+
// comparisons are rare, and will be handled in the ultimate fallback, so
|
|
279
|
+
// we can avoid the `toString.call()` cost unless necessary.
|
|
280
|
+
if (a.constructor === Object && b.constructor === Object) {
|
|
281
|
+
return areObjectsEqual(a, b, state);
|
|
282
|
+
}
|
|
283
|
+
// `isArray()` works on subclasses and is cross-realm, so we can again avoid
|
|
284
|
+
// the `toString.call()` cost unless necessary by just checking if either
|
|
285
|
+
// and then both are arrays.
|
|
286
|
+
var aArray = isArray(a);
|
|
287
|
+
var bArray = isArray(b);
|
|
288
|
+
if (aArray || bArray) {
|
|
289
|
+
return aArray === bArray && areArraysEqual(a, b, state);
|
|
290
|
+
}
|
|
291
|
+
// Since this is a custom object, use the classic `toString.call()` to get its
|
|
292
|
+
// type. This is reasonably performant in modern environments like v8 and
|
|
293
|
+
// SpiderMonkey, and allows for cross-realm comparison when other checks like
|
|
294
|
+
// `instanceof` do not.
|
|
295
|
+
var tag = getTag(a);
|
|
296
|
+
if (tag !== getTag(b)) {
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
if (tag === DATE_TAG) {
|
|
300
|
+
return areDatesEqual(a, b, state);
|
|
301
|
+
}
|
|
302
|
+
if (tag === REG_EXP_TAG) {
|
|
303
|
+
return areRegExpsEqual(a, b, state);
|
|
304
|
+
}
|
|
305
|
+
if (tag === MAP_TAG) {
|
|
306
|
+
return areMapsEqual(a, b, state);
|
|
307
|
+
}
|
|
308
|
+
if (tag === SET_TAG) {
|
|
309
|
+
return areSetsEqual(a, b, state);
|
|
310
|
+
}
|
|
311
|
+
// If a simple object tag, then we can prioritize a simple object comparison because
|
|
312
|
+
// it is likely a custom class.
|
|
313
|
+
if (tag === OBJECT_TAG) {
|
|
314
|
+
// The exception for value comparison is `Promise`-like contracts. These should be
|
|
315
|
+
// treated the same as standard `Promise` objects, which means strict equality, and if
|
|
316
|
+
// it reaches this point then that strict equality comparison has already failed.
|
|
317
|
+
return (typeof a.then !== 'function' &&
|
|
318
|
+
typeof b.then !== 'function' &&
|
|
319
|
+
areObjectsEqual(a, b, state));
|
|
320
|
+
}
|
|
321
|
+
// If an arguments tag, it should be treated as a standard object.
|
|
322
|
+
if (tag === ARGUMENTS_TAG) {
|
|
323
|
+
return areObjectsEqual(a, b, state);
|
|
324
|
+
}
|
|
325
|
+
// As the penultimate fallback, check if the values passed are primitive wrappers. This
|
|
326
|
+
// is very rare in modern JS, which is why it is deprioritized compared to all other object
|
|
327
|
+
// types.
|
|
328
|
+
if (tag === BOOLEAN_TAG || tag === NUMBER_TAG || tag === STRING_TAG) {
|
|
329
|
+
return arePrimitiveWrappersEqual(a, b, state);
|
|
330
|
+
}
|
|
331
|
+
// If not matching any tags that require a specific type of comparison, then we hard-code false because
|
|
332
|
+
// the only thing remaining is strict equality, which has already been compared. This is for a few reasons:
|
|
333
|
+
// - Certain types that cannot be introspected (e.g., `WeakMap`). For these types, this is the only
|
|
334
|
+
// comparison that can be made.
|
|
335
|
+
// - For types that can be introspected, but rarely have requirements to be compared
|
|
336
|
+
// (`ArrayBuffer`, `DataView`, etc.), the cost is avoided to prioritize the common
|
|
337
|
+
// use-cases (may be included in a future release, if requested enough).
|
|
338
|
+
// - For types that can be introspected but do not have an objective definition of what
|
|
339
|
+
// equality is (`Error`, etc.), the subjective decision is to be conservative and strictly compare.
|
|
340
|
+
// In all cases, these decisions should be reevaluated based on changes to the language and
|
|
341
|
+
// common development practices.
|
|
342
|
+
return false;
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Create the configuration object used for building comparators.
|
|
347
|
+
*/
|
|
345
348
|
function createComparatorConfig(_a) {
|
|
346
|
-
var circular = _a.circular, strict = _a.strict;
|
|
349
|
+
var circular = _a.circular, createCustomConfig = _a.createCustomConfig, strict = _a.strict;
|
|
347
350
|
var config = {
|
|
348
|
-
areArraysEqual:
|
|
351
|
+
areArraysEqual: strict
|
|
352
|
+
? areObjectsEqualStrict
|
|
353
|
+
: areArraysEqual,
|
|
349
354
|
areDatesEqual: areDatesEqual,
|
|
350
|
-
areMapsEqual:
|
|
351
|
-
|
|
355
|
+
areMapsEqual: strict
|
|
356
|
+
? combineComparators(areMapsEqual, areObjectsEqualStrict)
|
|
357
|
+
: areMapsEqual,
|
|
358
|
+
areObjectsEqual: strict
|
|
359
|
+
? areObjectsEqualStrict
|
|
360
|
+
: areObjectsEqual,
|
|
352
361
|
arePrimitiveWrappersEqual: arePrimitiveWrappersEqual,
|
|
353
362
|
areRegExpsEqual: areRegExpsEqual,
|
|
354
|
-
areSetsEqual:
|
|
363
|
+
areSetsEqual: strict
|
|
364
|
+
? combineComparators(areSetsEqual, areObjectsEqualStrict)
|
|
365
|
+
: areSetsEqual,
|
|
355
366
|
};
|
|
356
|
-
if (
|
|
357
|
-
config
|
|
358
|
-
config.areMapsEqual = combineComparators(areMapsEqual, areObjectsEqual);
|
|
359
|
-
config.areObjectsEqual = areObjectsEqualStrict;
|
|
360
|
-
config.areSetsEqual = combineComparators(areSetsEqual, areObjectsEqual);
|
|
367
|
+
if (createCustomConfig) {
|
|
368
|
+
config = assign({}, config, createCustomConfig(config));
|
|
361
369
|
}
|
|
362
370
|
if (circular) {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
371
|
+
var areArraysEqual$1 = createIsCircular(config.areArraysEqual);
|
|
372
|
+
var areMapsEqual$1 = createIsCircular(config.areMapsEqual);
|
|
373
|
+
var areObjectsEqual$1 = createIsCircular(config.areObjectsEqual);
|
|
374
|
+
var areSetsEqual$1 = createIsCircular(config.areSetsEqual);
|
|
375
|
+
config = assign({}, config, {
|
|
376
|
+
areArraysEqual: areArraysEqual$1,
|
|
377
|
+
areMapsEqual: areMapsEqual$1,
|
|
378
|
+
areObjectsEqual: areObjectsEqual$1,
|
|
379
|
+
areSetsEqual: areSetsEqual$1,
|
|
380
|
+
});
|
|
367
381
|
}
|
|
368
382
|
return config;
|
|
369
383
|
}
|
|
370
|
-
|
|
371
|
-
if (options === void 0) { options = {}; }
|
|
372
|
-
var config = createComparatorConfig(options);
|
|
373
|
-
var isEqual = createComparator(config);
|
|
374
|
-
var isEqualComparator = options.comparator || createInternalComparator(isEqual);
|
|
375
|
-
var strict = !!options.strict;
|
|
376
|
-
if (options.circular) {
|
|
377
|
-
return function equals(a, b) {
|
|
378
|
-
return isEqual(a, b, {
|
|
379
|
-
cache: new WeakMap(),
|
|
380
|
-
equals: isEqualComparator,
|
|
381
|
-
meta: undefined,
|
|
382
|
-
strict: strict,
|
|
383
|
-
});
|
|
384
|
-
};
|
|
385
|
-
}
|
|
386
|
-
var state = Object.freeze({
|
|
387
|
-
cache: undefined,
|
|
388
|
-
equals: isEqualComparator,
|
|
389
|
-
meta: undefined,
|
|
390
|
-
strict: strict,
|
|
391
|
-
});
|
|
392
|
-
return function equals(a, b) {
|
|
393
|
-
return isEqual(a, b, state);
|
|
394
|
-
};
|
|
395
|
-
}
|
|
384
|
+
|
|
396
385
|
/**
|
|
397
386
|
* Whether the items passed are deeply-equal in value.
|
|
398
387
|
*/
|
|
399
|
-
var deepEqual =
|
|
388
|
+
var deepEqual = createCustomEqual();
|
|
400
389
|
/**
|
|
401
390
|
* Whether the items passed are deeply-equal in value based on strict comparison.
|
|
402
391
|
*/
|
|
403
|
-
var strictDeepEqual =
|
|
392
|
+
var strictDeepEqual = createCustomEqual({ strict: true });
|
|
404
393
|
/**
|
|
405
394
|
* Whether the items passed are deeply-equal in value, including circular references.
|
|
406
395
|
*/
|
|
407
|
-
var circularDeepEqual =
|
|
396
|
+
var circularDeepEqual = createCustomEqual({ circular: true });
|
|
408
397
|
/**
|
|
409
398
|
* Whether the items passed are deeply-equal in value, including circular references,
|
|
410
399
|
* based on strict comparison.
|
|
411
400
|
*/
|
|
412
|
-
var strictCircularDeepEqual =
|
|
401
|
+
var strictCircularDeepEqual = createCustomEqual({
|
|
413
402
|
circular: true,
|
|
414
403
|
strict: true,
|
|
415
404
|
});
|
|
416
405
|
/**
|
|
417
406
|
* Whether the items passed are shallowly-equal in value.
|
|
418
407
|
*/
|
|
419
|
-
var shallowEqual =
|
|
408
|
+
var shallowEqual = createCustomEqual({
|
|
420
409
|
comparator: sameValueZeroEqual,
|
|
421
410
|
});
|
|
422
411
|
/**
|
|
423
412
|
* Whether the items passed are shallowly-equal in value based on strict comparison
|
|
424
413
|
*/
|
|
425
|
-
var strictShallowEqual =
|
|
414
|
+
var strictShallowEqual = createCustomEqual({
|
|
426
415
|
comparator: sameValueZeroEqual,
|
|
427
416
|
strict: true,
|
|
428
417
|
});
|
|
429
418
|
/**
|
|
430
419
|
* Whether the items passed are shallowly-equal in value, including circular references.
|
|
431
420
|
*/
|
|
432
|
-
var circularShallowEqual =
|
|
421
|
+
var circularShallowEqual = createCustomEqual({
|
|
433
422
|
comparator: sameValueZeroEqual,
|
|
434
423
|
circular: true,
|
|
435
424
|
});
|
|
@@ -437,7 +426,7 @@ var circularShallowEqual = createDefaultEqualCreator({
|
|
|
437
426
|
* Whether the items passed are shallowly-equal in value, including circular references,
|
|
438
427
|
* based on strict comparison.
|
|
439
428
|
*/
|
|
440
|
-
var strictCircularShallowEqual =
|
|
429
|
+
var strictCircularShallowEqual = createCustomEqual({
|
|
441
430
|
comparator: sameValueZeroEqual,
|
|
442
431
|
circular: true,
|
|
443
432
|
strict: true,
|
|
@@ -452,35 +441,31 @@ var strictCircularShallowEqual = createDefaultEqualCreator({
|
|
|
452
441
|
*/
|
|
453
442
|
function createCustomEqual(options) {
|
|
454
443
|
if (options === void 0) { options = {}; }
|
|
455
|
-
var comparator = options.comparator, createCustomInternalComparator = options.createInternalComparator,
|
|
456
|
-
var
|
|
457
|
-
var
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
? createCustomInternalComparator(isEqualCustom)
|
|
463
|
-
: createInternalComparator(isEqualCustom);
|
|
464
|
-
var strict = !!options.strict;
|
|
444
|
+
var circular = options.circular, comparator = options.comparator, createCustomInternalComparator = options.createInternalComparator, createState = options.createState, _a = options.strict, baseStrict = _a === void 0 ? false : _a;
|
|
445
|
+
var config = createComparatorConfig(options);
|
|
446
|
+
var isEqualCustom = createComparator(config);
|
|
447
|
+
var isEqualCustomComparator = comparator ||
|
|
448
|
+
(createCustomInternalComparator
|
|
449
|
+
? createCustomInternalComparator(isEqualCustom)
|
|
450
|
+
: createInternalComparator(isEqualCustom));
|
|
465
451
|
if (createState) {
|
|
466
452
|
return function isEqual(a, b, metaOverride) {
|
|
467
|
-
var
|
|
453
|
+
var _a = createState(isEqualCustom), _b = _a.cache, cache = _b === void 0 ? circular ? new WeakMap() : undefined : _b, _c = _a.equals, equals = _c === void 0 ? isEqualCustomComparator : _c, meta = _a.meta, _d = _a.strict, strict = _d === void 0 ? baseStrict : _d;
|
|
468
454
|
return isEqualCustom(a, b, {
|
|
469
|
-
cache:
|
|
470
|
-
equals:
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
strict: customState.strict !== undefined ? customState.strict : strict,
|
|
455
|
+
cache: cache,
|
|
456
|
+
equals: equals,
|
|
457
|
+
meta: metaOverride !== undefined ? metaOverride : meta,
|
|
458
|
+
strict: strict,
|
|
474
459
|
});
|
|
475
460
|
};
|
|
476
461
|
}
|
|
477
|
-
if (
|
|
462
|
+
if (circular) {
|
|
478
463
|
return function equals(a, b) {
|
|
479
464
|
return isEqualCustom(a, b, {
|
|
480
465
|
cache: new WeakMap(),
|
|
481
466
|
equals: isEqualCustomComparator,
|
|
482
467
|
meta: undefined,
|
|
483
|
-
strict:
|
|
468
|
+
strict: baseStrict,
|
|
484
469
|
});
|
|
485
470
|
};
|
|
486
471
|
}
|
|
@@ -488,7 +473,7 @@ function createCustomEqual(options) {
|
|
|
488
473
|
cache: undefined,
|
|
489
474
|
equals: isEqualCustomComparator,
|
|
490
475
|
meta: undefined,
|
|
491
|
-
strict:
|
|
476
|
+
strict: baseStrict,
|
|
492
477
|
});
|
|
493
478
|
return function equals(a, b) {
|
|
494
479
|
return isEqualCustom(a, b, state);
|