From 417eac93df01af75a3f7f1dcb7de5404d0b1dfef Mon Sep 17 00:00:00 2001 From: Tim Basel Date: Sun, 20 Oct 2024 15:56:41 +0200 Subject: [PATCH] fix class/prototype getters not getting wrapped + tests --- packages/solid/store/src/store.ts | 20 +++++++- packages/solid/store/test/store.spec.ts | 62 +++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/packages/solid/store/src/store.ts b/packages/solid/store/src/store.ts index 3402bae1a..6a7bea530 100644 --- a/packages/solid/store/src/store.ts +++ b/packages/solid/store/src/store.ts @@ -1,4 +1,4 @@ -import { getListener, batch, DEV, $PROXY, $TRACK, createSignal } from "solid-js"; +import { $PROXY, $TRACK, batch, createSignal, DEV, getListener } from "solid-js"; export const $RAW = Symbol("store-raw"), $NODE = Symbol("store-node"), @@ -49,11 +49,27 @@ function wrap(value: T): T { Object.defineProperty(value, $PROXY, { value: (p = new Proxy(value, proxyTraps)) }); if (!Array.isArray(value)) { const keys = Object.keys(value), - desc = Object.getOwnPropertyDescriptors(value); + desc = Object.getOwnPropertyDescriptors(value), + proto = Object.getPrototypeOf(value); + + const isClass = + proto !== null && + value !== null && + typeof value === "object" && + !Array.isArray(value) && + proto !== Object.prototype; + if (isClass) { + const descriptors = Object.getOwnPropertyDescriptors(proto); + keys.push(...Object.keys(descriptors)); + Object.assign(desc, descriptors); + } + for (let i = 0, l = keys.length; i < l; i++) { const prop = keys[i]; + if (isClass && prop === "constructor") continue; if (desc[prop].get) { Object.defineProperty(value, prop, { + configurable: true, enumerable: desc[prop].enumerable, get: desc[prop].get!.bind(p) }); diff --git a/packages/solid/store/test/store.spec.ts b/packages/solid/store/test/store.spec.ts index 6fc6e28c1..e29b5a665 100644 --- a/packages/solid/store/test/store.spec.ts +++ b/packages/solid/store/test/store.spec.ts @@ -722,6 +722,37 @@ describe("Nested Classes", () => { expect(sum).toBe(15); }); + test("wrapped nested class getter", () => { + class CustomThing { + a: number; + b: number; + constructor(value: number) { + this.a = value; + this.b = 10; + } + get sum(): number { + return this.a + this.b; + } + } + + const [inner] = createStore(new CustomThing(1)); + const [store, setStore] = createStore({ inner }); + + expect(store.inner.sum).toBe(11); + + let sum; + createRoot(() => { + createEffect(() => { + sum = store.inner.sum; + }); + }); + expect(sum).toBe(11); + setStore("inner", "a", 10); + expect(sum).toBe(20); + setStore("inner", "b", 5); + expect(sum).toBe(15); + }); + test("not wrapped nested class", () => { class CustomThing { a: number; @@ -748,6 +779,37 @@ describe("Nested Classes", () => { setStore("inner", "b", 5); expect(sum).toBe(11); }); + + test("not wrapped nested class getter", () => { + class CustomThing { + a: number; + b: number; + constructor(value: number) { + this.a = value; + this.b = 10; + } + get sum(): number { + return this.a + this.b; + } + } + + const [store, setStore] = createStore({ inner: new CustomThing(1) }); + + let sum; + createRoot(() => { + createEffect(() => { + sum = store.inner.sum; + }); + }); + expect(sum).toBe(11); + expect(store.inner.sum).toBe(11); + setStore("inner", "a", 10); + expect(sum).toBe(11); + expect(store.inner.sum).toBe(20); + setStore("inner", "b", 5); + expect(sum).toBe(11); + expect(store.inner.sum).toBe(15); + }); }); describe("In Operator", () => {