diff --git a/packages/plugin-sdk/package.json b/packages/plugin-sdk/package.json index 5098326..36830b2 100644 --- a/packages/plugin-sdk/package.json +++ b/packages/plugin-sdk/package.json @@ -14,6 +14,9 @@ "test:watch": "vitest", "types": "yarn g:types" }, + "dependencies": { + "dequal": "^2.0.3" + }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, diff --git a/packages/plugin-sdk/src/react/hooks.ts b/packages/plugin-sdk/src/react/hooks.ts index adfd0b1..f3b9700 100644 --- a/packages/plugin-sdk/src/react/hooks.ts +++ b/packages/plugin-sdk/src/react/hooks.ts @@ -1,3 +1,4 @@ +import { dequal } from 'dequal/lite'; import * as React from 'react'; import { @@ -10,7 +11,6 @@ import { PluginStyle, UrlParameter, } from '../types'; -import { deepEqual } from '../utils/deepEqual'; import { PluginContext } from './Context'; @@ -34,7 +34,7 @@ export function useEditorPanelConfig( React.useEffect(() => { if (nextOptions == null) return; - if (!deepEqual(nextOptions, optionsRef.current)) { + if (!dequal(nextOptions, optionsRef.current)) { client.config.configureEditorPanel(nextOptions); optionsRef.current = nextOptions; } diff --git a/packages/plugin-sdk/src/utils/__tests__/deepEqual.test.ts b/packages/plugin-sdk/src/utils/__tests__/deepEqual.test.ts deleted file mode 100644 index a57783c..0000000 --- a/packages/plugin-sdk/src/utils/__tests__/deepEqual.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { deepEqual } from '../deepEqual'; - -describe('deepEqual', () => { - describe('primitive comparisons', () => { - it('returns true for strictly equal primitives', () => { - expect(deepEqual(1, 1)).toBe(true); - expect(deepEqual('a', 'a')).toBe(true); - expect(deepEqual(true, true)).toBe(true); - expect(deepEqual(false, false)).toBe(true); - expect(deepEqual(null, null)).toBe(true); - expect(deepEqual(undefined, undefined)).toBe(true); - }); - - it('returns true for the same reference', () => { - const sym = Symbol('x'); - expect(deepEqual(sym, sym)).toBe(true); - - const obj = { a: 1 }; - expect(deepEqual(obj, obj)).toBe(true); - }); - - it('returns falsy for non-equal primitives', () => { - expect(deepEqual(1, 2)).toBeFalsy(); - expect(deepEqual('a', 'b')).toBeFalsy(); - expect(deepEqual(true, false)).toBeFalsy(); - expect(deepEqual(null, undefined)).toBeFalsy(); - expect(deepEqual(0, '0')).toBeFalsy(); - }); - }); - - describe('object comparisons', () => { - it('returns true for two empty objects', () => { - expect(deepEqual({}, {})).toBe(true); - }); - - it('returns true for shallowly equal objects', () => { - expect(deepEqual({ a: 1, b: 'x' }, { a: 1, b: 'x' })).toBe(true); - }); - - it('returns false for objects with different key counts', () => { - expect(deepEqual({ a: 1 }, { a: 1, b: 2 })).toBe(false); - expect(deepEqual({ a: 1, b: 2 }, { a: 1 })).toBe(false); - }); - - it('returns false for objects with the same keys but different values', () => { - expect(deepEqual({ a: 1 }, { a: 2 })).toBe(false); - }); - - it('returns true for deeply nested equal objects', () => { - expect( - deepEqual( - { a: { b: { c: [1, 2, 3] } } }, - { a: { b: { c: [1, 2, 3] } } }, - ), - ).toBe(true); - }); - - it('returns false for deeply nested objects that differ', () => { - expect( - deepEqual( - { a: { b: { c: [1, 2, 3] } } }, - { a: { b: { c: [1, 2, 4] } } }, - ), - ).toBe(false); - }); - - it('compares arrays as objects', () => { - expect(deepEqual([1, 2, 3], [1, 2, 3])).toBe(true); - expect(deepEqual([1, 2], [1, 2, 3])).toBe(false); - expect(deepEqual([1, 2, 3], [3, 2, 1])).toBe(false); - }); - }); - - describe('mixed type comparisons', () => { - it('returns falsy when comparing an object against null', () => { - expect(deepEqual({}, null)).toBeFalsy(); - expect(deepEqual(null, {})).toBeFalsy(); - }); - - it('returns falsy when comparing an object against a primitive', () => { - expect(deepEqual({}, 1)).toBeFalsy(); - expect(deepEqual(1, {})).toBeFalsy(); - expect(deepEqual([], 'a')).toBeFalsy(); - }); - - it('returns falsy when comparing an object against undefined', () => { - expect(deepEqual({}, undefined)).toBeFalsy(); - expect(deepEqual(undefined, {})).toBeFalsy(); - }); - }); -}); diff --git a/packages/plugin-sdk/src/utils/deepEqual.ts b/packages/plugin-sdk/src/utils/deepEqual.ts deleted file mode 100644 index 06210f4..0000000 --- a/packages/plugin-sdk/src/utils/deepEqual.ts +++ /dev/null @@ -1,23 +0,0 @@ -function isObject(obj: any) { - if (typeof obj === 'object' && obj != null) { - return true; - } else { - return false; - } -} - -export function deepEqual(obj1: any, obj2: any) { - if (obj1 === obj2) { - return true; - } else if (isObject(obj1) && isObject(obj2)) { - if (Object.keys(obj1).length !== Object.keys(obj2).length) { - return false; - } - for (const prop in obj1) { - if (!deepEqual(obj1[prop], obj2[prop])) { - return false; - } - } - return true; - } -} diff --git a/packages/plugin-sdk/tsdown.config.ts b/packages/plugin-sdk/tsdown.config.ts index fdb5930..ceaf0ae 100644 --- a/packages/plugin-sdk/tsdown.config.ts +++ b/packages/plugin-sdk/tsdown.config.ts @@ -11,5 +11,17 @@ export default mergeConfig( define: { __VERSION__: JSON.stringify(packageJson.version), }, + format: { + umd: { + deps: { + alwaysBundle: id => id.startsWith('dequal'), + // TSDown's types are bad. `skipNodeModulesBundle` defaults to `false` + // but if you attempt to use the `deps` object, + // `skipNodeModulesBundle` is a required property so we have to set it + // or TS will throw a type error. + skipNodeModulesBundle: false, + }, + }, + }, }), ); diff --git a/yarn.lock b/yarn.lock index 6620614..62cd342 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1230,6 +1230,7 @@ __metadata: version: 0.0.0-use.local resolution: "@sigmacomputing/plugin@workspace:packages/plugin-sdk" dependencies: + dequal: "npm:^2.0.3" tsdown: "npm:^0.21.10" vitest: "npm:^4.1.5" peerDependencies: