@longlast/equals 0.5.4 → 0.5.5
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/dist/index.js +68 -15
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -67,40 +67,64 @@ export const equals = curry(_equals);
|
|
|
67
67
|
function _equals(a, b) {
|
|
68
68
|
// This is an optimized implementation. There is a simpler, equivalent one
|
|
69
69
|
// in pkg/equals/alt/reference.ts.
|
|
70
|
+
if (a == null) {
|
|
71
|
+
return a === b;
|
|
72
|
+
}
|
|
70
73
|
// TODO: (pre-1.0.0) decide if we should pass `equals` as the second
|
|
71
74
|
// argument to [$equals]. This would open the "protocol" up a bit more,
|
|
72
75
|
// since people could then define their own implementations of `equals`
|
|
73
76
|
// that work consistently through custom equality comparisons.
|
|
74
|
-
|
|
75
|
-
if (a != null && typeof a[$equals] === "function") {
|
|
77
|
+
if (typeof a[$equals] === "function") {
|
|
76
78
|
return Boolean(a[$equals](b));
|
|
77
79
|
}
|
|
78
80
|
if (Object.is(a, b)) {
|
|
79
81
|
return true;
|
|
80
82
|
}
|
|
81
|
-
if (a
|
|
83
|
+
if (typeof a === "function" && typeof b === "function") {
|
|
84
|
+
const aUnapplied = a[$unapplied];
|
|
85
|
+
const bUnapplied = b[$unapplied];
|
|
86
|
+
return (aUnapplied != null &&
|
|
87
|
+
aUnapplied === bUnapplied &&
|
|
88
|
+
_equals(getBoundArguments(a), getBoundArguments(b)));
|
|
89
|
+
}
|
|
90
|
+
// If `a` is a primitive at this point, return false, since we already know
|
|
91
|
+
// it is not identical to `b`.
|
|
92
|
+
if (typeof a !== "object") {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const aConstructorString = functionString(constructorOf(a));
|
|
96
|
+
const bConstructorString = functionString(constructorOf(b));
|
|
97
|
+
if (aConstructorString !== bConstructorString) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
if (dateConstructorString === aConstructorString) {
|
|
101
|
+
unsafeNarrow(a);
|
|
102
|
+
unsafeNarrow(b);
|
|
82
103
|
return Object.is(+a, +b);
|
|
83
104
|
}
|
|
84
|
-
if (
|
|
105
|
+
if (regexConstructorString === aConstructorString) {
|
|
85
106
|
return String(a) === String(b);
|
|
86
107
|
}
|
|
87
|
-
if (a instanceof Error && b instanceof Error)
|
|
88
|
-
|
|
108
|
+
if ((a instanceof Error && b instanceof Error) ||
|
|
109
|
+
nativeErrorConstructorStrings.includes(aConstructorString)) {
|
|
110
|
+
unsafeNarrow(a);
|
|
111
|
+
unsafeNarrow(b);
|
|
112
|
+
return a.message === b.message;
|
|
89
113
|
}
|
|
90
114
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
91
115
|
return a.length === b.length && a.every((_, i) => _equals(a[i], b[i]));
|
|
92
116
|
}
|
|
93
|
-
if (
|
|
117
|
+
if (setConstructorString === aConstructorString) {
|
|
118
|
+
unsafeNarrow(a);
|
|
119
|
+
unsafeNarrow(b);
|
|
94
120
|
return a.size === b.size && [...a].every((v) => b.has(v));
|
|
95
121
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
if (a && b && typeof a === "object" && protoOf(a) === protoOf(b)) {
|
|
122
|
+
// TODO: typed arrays
|
|
123
|
+
// TODO: Map
|
|
124
|
+
if (objectConstructorString === aConstructorString ||
|
|
125
|
+
protoOf(a) === protoOf(b)) {
|
|
126
|
+
unsafeNarrow(a);
|
|
127
|
+
unsafeNarrow(b);
|
|
104
128
|
const aKeys = Object.keys(a);
|
|
105
129
|
const bKeys = Object.keys(b);
|
|
106
130
|
if (aKeys.length !== bKeys.length) {
|
|
@@ -123,4 +147,33 @@ function getBoundArguments(f) {
|
|
|
123
147
|
// TODO: (pre-1.0.0) remove `f[$boundArguments]` fallback.
|
|
124
148
|
return f[$getBoundArguments]?.() ?? f[$boundArguments];
|
|
125
149
|
}
|
|
150
|
+
function functionString(f) {
|
|
151
|
+
if (typeof f !== "function") {
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
return Function.prototype.toString.call(f);
|
|
155
|
+
}
|
|
156
|
+
function constructorOf(value) {
|
|
157
|
+
if (value == null) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
return Object.getPrototypeOf(value)?.constructor;
|
|
161
|
+
}
|
|
162
|
+
function unsafeNarrow(value) {
|
|
163
|
+
value;
|
|
164
|
+
}
|
|
126
165
|
const protoOf = Object.getPrototypeOf;
|
|
166
|
+
const objectConstructorString = functionString(Object);
|
|
167
|
+
const dateConstructorString = functionString(Date);
|
|
168
|
+
const regexConstructorString = functionString(RegExp);
|
|
169
|
+
const setConstructorString = functionString(Set);
|
|
170
|
+
const nativeErrorConstructorStrings = [
|
|
171
|
+
functionString(Error),
|
|
172
|
+
// TODO: add DOMException? Be sure to check the `name` property.
|
|
173
|
+
functionString(EvalError),
|
|
174
|
+
functionString(RangeError),
|
|
175
|
+
functionString(ReferenceError),
|
|
176
|
+
functionString(SyntaxError),
|
|
177
|
+
functionString(TypeError),
|
|
178
|
+
functionString(URIError),
|
|
179
|
+
];
|