Skip to content

Commit

Permalink
Replace broken isEqual implementation with our own.
Browse files Browse the repository at this point in the history
  • Loading branch information
zewish committed Dec 3, 2022
1 parent e8bdce9 commit b498c44
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 29 deletions.
15 changes: 1 addition & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "redux-remember",
"version": "3.1.2",
"version": "3.1.3",
"description": "Saves and loads your redux state from a key-value store of your choice",
"main": "lib/index.js",
"module": "es/index.js",
Expand Down Expand Up @@ -71,9 +71,6 @@
"react",
"react-native"
],
"dependencies": {
"@zerodep/is.equal": "^0.2.0"
},
"devDependencies": {
"@babel/cli": "^7.19.3",
"@babel/core": "^7.20.5",
Expand Down
15 changes: 10 additions & 5 deletions src/__tests__/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,21 @@ describe('init.ts', () => {
mockThrottle = jest.fn((fn: any) => fn);

jest.mock(
'../rehydrate',
'../rehydrate.js',
() => mockRehydrate
);

jest.mock(
'../persist',
'../persist.js',
() => mockPersist
);

jest.mock(
'../utils',
'../utils.js',
() => ({
pick: mockPick,
throttle: mockThrottle
throttle: mockThrottle,
isEqual: () => false
})
);

Expand Down Expand Up @@ -131,7 +132,11 @@ describe('init.ts', () => {

it('does not call store.dispatch()', async () => {
jest.resetModules();
jest.mock('@zerodep/is.equal', () => ({ isEqual: () => true }));
jest.mock('../utils.js', () => ({
pick: mockPick,
throttle: mockThrottle,
isEqual: () => true
}));

init = (await import('../init.js')).default as any;
await init(...args);
Expand Down
18 changes: 14 additions & 4 deletions src/__tests__/persist.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ describe('persist.ts', () => {

mockIsEqual = jest.fn((a, b) => a === b);

jest.mock('@zerodep/is.equal', () => ({
jest.mock('../utils.js', () => ({
...jest.requireActual('../utils.js'),
isEqual: mockIsEqual
}));

Expand Down Expand Up @@ -112,7 +113,10 @@ describe('persist.ts', () => {
});

it('does not call driver.setItem()', async () => {
jest.mock('@zerodep/is.equal', () => ({ isEqual: () => true }));
jest.mock('../utils.js', () => ({
...jest.requireActual('../utils.js'),
isEqual: () => true
}));
jest.resetModules();

mod = await import('../persist.js');
Expand Down Expand Up @@ -221,7 +225,10 @@ describe('persist.ts', () => {
});

it('does not call driver.setItem()', async () => {
jest.mock('@zerodep/is.equal', () => ({ isEqual: () => true }));
jest.mock('../utils.js', () => ({
...jest.requireActual('../utils.js'),
isEqual: () => true
}));
jest.resetModules();

mod = await import('../persist.js');
Expand Down Expand Up @@ -290,7 +297,10 @@ describe('persist.ts', () => {
});

it('calls console.warn()', async () => {
jest.mock('@zerodep/is.equal', () => ({ isEqual: () => false }));
jest.mock('../utils.js', () => ({
...jest.requireActual('../utils.js'),
isEqual: () => false
}));
jest.resetModules();

mod = await import('../persist.js');
Expand Down
2 changes: 1 addition & 1 deletion src/init.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Store } from 'redux';
import { isEqual } from '@zerodep/is.equal';
import { isEqual } from './utils.js';
import { ExtendedOptions } from './types.js';
import { rehydrate } from './rehydrate.js';
import { persist } from './persist.js';
Expand Down
2 changes: 1 addition & 1 deletion src/persist.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isEqual } from '@zerodep/is.equal';
import { isEqual } from './utils.js';
import { ExtendedOptions } from './types.js';

type SaveAllOptions = Pick<
Expand Down
139 changes: 139 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
type TimeoutHandle = ReturnType<typeof setTimeout>;

const getObjectType = (o: object): string => Object.prototype.toString.call(o).match(
/\[object (.*)\]/
)?.[1] || '';

const isTypedArray = (o: object): boolean => (
/^\[object (?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array\]$/
.test(Object.prototype.toString.call(o))
);

export const pick = <T extends Record<string, any>, K extends keyof T>(
src: T | null | undefined,
keys: K[]
Expand Down Expand Up @@ -50,3 +59,133 @@ export const throttle = <T extends (...args: any) => void>(
}, timeLeft);
}) as T;
};

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

if (typeof a !== 'object' || typeof b !== 'object'
|| a === null || a === undefined || b === null || b === undefined
) {
return a !== a && b !== b;
}

if (a.constructor !== b.constructor) {
return false;
}

if (a.constructor === RegExp) {
return a.source === b.source && a.flags === b.flags;
}

if (Array.isArray(a)) {
if (a.length != b.length) {
return false;
}

for (let i = a.length; i-- !== 0;) {
if (!isEqual(a[i], b[i])) {
return false;
}
}

return true;
}

if (isTypedArray(a) && isTypedArray(b)) {
if (a.byteLength !== b.byteLength) {
return false;
}

for (let i = a.byteLength; i-- !== 0;) {
if (!isEqual(a[i], b[i])) {
return false;
}
}

return true;
}

const aType = getObjectType(a);
const bType = getObjectType(b);

if (aType === 'DataView' && bType === 'DataView') {
if (a.byteLength != b.byteLength || a.byteOffset != b.byteOffset) {
return false;
}

return isEqual(a.buffer, b.buffer);
}

if (aType === 'ArrayBuffer' && bType === 'ArrayBuffer') {
if (a.byteLength != b.byteLength) {
return false;
}

return isEqual(new Uint8Array(a), new Uint8Array(b));
}

if (aType === 'Map' && bType === 'Map') {
if (a.size !== b.size) {
return false;
}

for (let [key] of a.entries()) {
if (!b.has(key)) {
return false;
}
}

for (let [key, value] of a.entries()) {
if (!isEqual(value, b.get(key))) {
return false;
}
}

return true;
}

if (aType === 'Set' && bType === 'Set') {
if (a.size !== b.size) {
return false;
}

for (let [key] of a.entries()) {
if (!b.has(key)) {
return false;
}
}

return true;
}

if (aType === 'Date' && bType === 'Date') {
return +a === +b;
}

const aKeys = Object.keys(a);
if (aKeys.length !== Object.keys(b).length) {
return false;
}

for (let i = aKeys.length; i-- !== 0;) {
if (!Object.prototype.hasOwnProperty.call(b, aKeys[i])) {
return false;
}
}

for (let i = aKeys.length; i-- !== 0;) {
const key = aKeys[i];

if (!isEqual(a[key], b[key])) {
return false;
}
}

if (aType !== 'Object' && bType !== 'Object') {
return a === b;
}

return true;
};

0 comments on commit b498c44

Please sign in to comment.