Skip to content

Commit

Permalink
Do not stop refreshing if source contains weird object (#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
igorkamyshev committed Oct 16, 2023
1 parent 3ec5631 commit 54d3bf1
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 63 deletions.
5 changes: 5 additions & 0 deletions .changeset/fair-rabbits-sell.md
@@ -0,0 +1,5 @@
---
'@farfetched/core': patch
---

Do not stop refreshing if source contains weird object
20 changes: 18 additions & 2 deletions packages/core/src/libs/lohyphen/__tests__/is_equal.test.ts
@@ -1,9 +1,8 @@
// Source: https://github.com/smelukov/nano-equal

import { describe, test, expect } from 'vitest';

import { isEqual } from '../is_equal';

// Source: https://github.com/smelukov/nano-equal
describe('Functional', () => {
const objA = {
nan: NaN,
Expand Down Expand Up @@ -81,3 +80,20 @@ describe('Functional', () => {
expect(isEqual(recA3, recB3)).toBeFalsy();
});
});

describe('isEqual', () => {
test('do not throw on weird objects, issue #385', () => {
function createWeirdObject() {
const a = {};
// @ts-expect-error 🤷‍♂️
a.__proto__ = null;
return a;
}

const weirdObject1 = createWeirdObject();
const weirdObject2 = createWeirdObject();

expect(() => isEqual(weirdObject1, weirdObject2)).not.toThrow();
expect(isEqual(weirdObject1, weirdObject2)).toBe(false);
});
});
126 changes: 65 additions & 61 deletions packages/core/src/libs/lohyphen/is_equal.ts
@@ -1,91 +1,95 @@
// Source: https://github.com/smelukov/nano-equal

export function isEqual(a: any, b: any): boolean {
if (a === b) {
return true;
}

// is nan
if (a !== a && b !== b) {
// eslint-disable-line no-self-compare
return true;
}

const typeA = getType(a);
const typeB = getType(b);

if (typeA !== typeB) {
return false;
}

if (typeA === 'pure-object') {
try {
if (a === b) {
return true;
}

const keysA = Object.keys(a);
const keysBLength = Object.keys(b).length;
// is nan
if (a !== a && b !== b) {
// eslint-disable-line no-self-compare
return true;
}

if (keysA.length !== keysBLength) {
const typeA = getType(a);
const typeB = getType(b);

if (typeA !== typeB) {
return false;
}

for (let i = 0, l = keysA.length; i < l; i++) {
const key = keysA[i];

// eslint-disable-next-line no-prototype-builtins
if (!b.hasOwnProperty(keysA[i])) {
return false;
if (typeA === 'pure-object') {
if (a === b) {
return true;
}

const valA = a[key];
const valB = b[key];

// handle recursion
if (valA === a || valB === b || valA === b || valB === a) {
return valA === valB;
}
const keysA = Object.keys(a);
const keysBLength = Object.keys(b).length;

if (!isEqual(valA, valB)) {
if (keysA.length !== keysBLength) {
return false;
}
}

return true;
} else if (typeA === 'array') {
if (a.length === b.length) {
for (let j = 0; j < a.length; j++) {
const elA = a[j];
const elB = b[j];
for (let i = 0, l = keysA.length; i < l; i++) {
const key = keysA[i];

// eslint-disable-next-line no-prototype-builtins
if (!b.hasOwnProperty(keysA[i])) {
return false;
}

const valA = a[key];
const valB = b[key];

// handle recursion
if (elA === a || elB === b || elA === b || elB === a) {
return elA === elB;
if (valA === a || valB === b || valA === b || valB === a) {
return valA === valB;
}

if (!isEqual(elA, elB)) {
if (!isEqual(valA, valB)) {
return false;
}
}
} else {
return false;
}

return true;
} else if (typeA === 'object') {
if (
a.valueOf !== Object.prototype.valueOf() &&
b.valueOf !== Object.prototype.valueOf()
) {
return a.valueOf() === b.valueOf();
}
return true;
} else if (typeA === 'array') {
if (a.length === b.length) {
for (let j = 0; j < a.length; j++) {
const elA = a[j];
const elB = b[j];

// handle recursion
if (elA === a || elB === b || elA === b || elB === a) {
return elA === elB;
}

if (!isEqual(elA, elB)) {
return false;
}
}
} else {
return false;
}

if (
a.toString !== Object.prototype.toString() &&
b.toString !== Object.prototype.toString()
) {
return a.toString() === b.toString();
return true;
} else if (typeA === 'object') {
if (
a.valueOf !== Object.prototype.valueOf() &&
b.valueOf !== Object.prototype.valueOf()
) {
return a.valueOf() === b.valueOf();
}

if (
a.toString !== Object.prototype.toString() &&
b.toString !== Object.prototype.toString()
) {
return a.toString() === b.toString();
}
}
} catch (e) {
// We got extremely weird objects, let us skip it and consider them not equal
}

return false;
Expand Down

0 comments on commit 54d3bf1

Please sign in to comment.