(a: Loose, b: Loose)
| 62 | } |
| 63 | |
| 64 | function filter(a: Loose, b: Loose): Loose { |
| 65 | const seen = new WeakMap<Loose | unknown[], Loose | unknown[]>(); |
| 66 | return filterObject(a, b); |
| 67 | |
| 68 | function filterObject(a: Loose, b: Loose): Loose { |
| 69 | // Prevent infinite loop with circular references with same filter |
| 70 | const memo = seen.get(a); |
| 71 | if (memo && (memo === b)) return a; |
| 72 | |
| 73 | try { |
| 74 | seen.set(a, b); |
| 75 | } catch (err) { |
| 76 | if (err instanceof TypeError) { |
| 77 | throw new TypeError( |
| 78 | `Cannot assertObjectMatch ${a === null ? null : `type ${typeof a}`}`, |
| 79 | ); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | // Filter keys and symbols which are present in both actual and expected |
| 84 | const filtered = {} as Loose; |
| 85 | const keysA = Reflect.ownKeys(a); |
| 86 | const keysB = Reflect.ownKeys(b); |
| 87 | const entries = keysA.filter((key) => keysB.includes(key)) |
| 88 | .map((key) => [key, a[key as string]]) as Array<[string, unknown]>; |
| 89 | |
| 90 | if (keysA.length && keysB.length && !entries.length) { |
| 91 | // If both objects are not empty but don't have the same keys or symbols, |
| 92 | // returns the entries in object a. |
| 93 | for (const key of keysA) defineProperty(filtered, key, a[key]); |
| 94 | return filtered; |
| 95 | } |
| 96 | |
| 97 | for (const [key, value] of entries) { |
| 98 | // On regexp references, keep value as it to avoid losing pattern and flags |
| 99 | if (value instanceof RegExp) { |
| 100 | defineProperty(filtered, key, value); |
| 101 | continue; |
| 102 | } |
| 103 | // On date references, keep value as it to avoid losing the timestamp |
| 104 | if (value instanceof Date) { |
| 105 | defineProperty(filtered, key, value); |
| 106 | continue; |
| 107 | } |
| 108 | |
| 109 | const subset = (b as Loose)[key]; |
| 110 | |
| 111 | // On array references, build a filtered array and filter nested objects inside |
| 112 | if (Array.isArray(value) && Array.isArray(subset)) { |
| 113 | defineProperty(filtered, key, filterArray(value, subset)); |
| 114 | continue; |
| 115 | } |
| 116 | |
| 117 | // On nested objects references, build a filtered object recursively |
| 118 | if (isObject(value) && isObject(subset)) { |
| 119 | // When both operands are maps, build a filtered map with common keys and filter nested objects inside |
| 120 | if ((value instanceof Map) && (subset instanceof Map)) { |
| 121 | defineProperty( |
no test coverage detected