-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
Copy pathlike-selector.js
42 lines (35 loc) · 1.3 KB
/
like-selector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
const isPrimitive = value => value === null || typeof value !== 'object';
export function isLikeSelector(selector) {
// Require selector to be an array or plain object.
if (
isPrimitive(selector)
|| (!Array.isArray(selector) && Reflect.getPrototypeOf(selector) !== Object.prototype)
) {
return false;
}
// Also require at least one enumerable property.
const descriptors = Object.getOwnPropertyDescriptors(selector);
return Reflect.ownKeys(descriptors).some(key => descriptors[key].enumerable === true);
}
export const CIRCULAR_SELECTOR = new Error('Encountered a circular selector');
export function selectComparable(actual, selector, circular = [selector]) {
if (isPrimitive(actual)) {
return actual;
}
const comparable = Array.isArray(selector) ? [] : {};
const enumerableKeys = Reflect.ownKeys(selector).filter(key => Reflect.getOwnPropertyDescriptor(selector, key).enumerable);
for (const key of enumerableKeys) {
const subselector = Reflect.get(selector, key);
if (isLikeSelector(subselector)) {
if (circular.includes(subselector)) {
throw CIRCULAR_SELECTOR;
}
circular.push(subselector);
comparable[key] = selectComparable(Reflect.get(actual, key), subselector, circular);
circular.pop();
} else {
comparable[key] = Reflect.get(actual, key);
}
}
return comparable;
}