From ca79d6962b14d57cb95d2b914826067995971fd5 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:20:20 +0200 Subject: [PATCH 01/25] Use @preact/signal as peer dependencies --- packages/deepsignal/package.json | 8 ++++---- pnpm-lock.yaml | 15 +++++++++------ 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index 133a635..c340f91 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -44,17 +44,17 @@ "scripts": { "prepublishOnly": "cp ../../README.md . && cd ../.. && pnpm build" }, - "dependencies": { + "peerDependencies": { "@preact/signals-core": "^1.3.1", "@preact/signals": "^1.1.4", "@preact/signals-react": "^1.3.3" }, - "peerDependencies": { - "preact": "10.x" - }, "devDependencies": { "preact": "10.9.0", "preact-render-to-string": "^5.2.4", + "@preact/signals-core": "^1.3.1", + "@preact/signals": "^1.1.4", + "@preact/signals-react": "^1.3.3", "@types/react": "^18.0.18", "@types/react-dom": "^18.0.6", "react": "^18.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cb808d4..73f8e4c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -138,7 +138,7 @@ importers: version: 4.7.4 packages/deepsignal: - dependencies: + devDependencies: '@preact/signals': specifier: ^1.1.4 version: 1.1.4(preact@10.9.0) @@ -148,7 +148,6 @@ importers: '@preact/signals-react': specifier: ^1.3.3 version: 1.3.3(react@18.2.0) - devDependencies: '@types/react': specifier: ^18.0.18 version: 18.0.27 @@ -1900,7 +1899,7 @@ packages: /@preact/signals-core@1.3.1: resolution: {integrity: sha512-DL+3kDssZ3UOMz9HufwSYE/gK0+TnT1jzegfF5rstgyPrnyfjz4BHAoxmzQA6Mkp4UlKe8qjsgl3v5a/obzNig==} - dev: false + dev: true /@preact/signals-react@1.3.3(react@18.2.0): resolution: {integrity: sha512-Tbv+oWPcrWowAJp1U1eWFiFUJihulOAnL8g/hJ3fUsP0IcsKsj8U0OcIDrwemIPQe7+J/3FuudkZzted0MD/bA==} @@ -1910,7 +1909,7 @@ packages: '@preact/signals-core': 1.3.1 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) - dev: false + dev: true /@preact/signals@1.1.4(preact@10.9.0): resolution: {integrity: sha512-4s0U8yHfy6OzBjTx8lgG0JZGFq1y+ye7xLmaaI8SXyJ5p+jAtMKW40Mg3GDLISM5WyKnqbfrQ4SOmbq1VTBtTw==} @@ -1919,7 +1918,7 @@ packages: dependencies: '@preact/signals-core': 1.3.1 preact: 10.9.0 - dev: false + dev: true /@rollup/plugin-alias@3.1.9(rollup@2.77.2): resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==} @@ -4577,6 +4576,7 @@ packages: /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + dev: true /js-yaml@3.14.1: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} @@ -4926,6 +4926,7 @@ packages: hasBin: true dependencies: js-tokens: 4.0.0 + dev: true /loupe@2.3.4: resolution: {integrity: sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==} @@ -5930,6 +5931,7 @@ packages: /preact@10.9.0: resolution: {integrity: sha512-jO6/OvCRL+OT8gst/+Q2ir7dMybZAX8ioP02Zmzh3BkQMHLyqZSujvxbUriXvHi8qmhcHKC2Gwbog6Kt+YTh+Q==} + dev: true /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} @@ -6045,6 +6047,7 @@ packages: engines: {node: '>=0.10.0'} dependencies: loose-envify: 1.4.0 + dev: true /read-pkg-up@7.0.1: resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} @@ -6995,7 +6998,7 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 dependencies: react: 18.2.0 - dev: false + dev: true /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} From 3e42bc9dc67e0c5b1a575e87e30893387f9ebe7b Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:23:19 +0200 Subject: [PATCH 02/25] Update Readme --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 80e58c8..6d819a9 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,13 @@ The most important feature is that **it just works**. You don't need to do anyth ## Installation +### With Preact + ```sh -npm install deepsignal +npm install deepsignal @preact/signals ``` -If you are using `deepsignal` with Preact (`@preact/signals`), you should use the `deepsignal` import. You don't need to install or import `@preact/signals` anywhere in your code if you don't need it. +If you are using `deepsignal` with Preact (`@preact/signals`), you should use the `deepsignal` import. You also need to install `@preact/signals`. ```js import { deepSignal } from "deepsignal"; @@ -73,7 +75,11 @@ const state = deepSignal({}); ### With React -If you are using the library with React, you should use the `deepsignal/react` import. You don't need to install or import `@preact/signals-react` anywhere in your code if you don't need it. +```sh +npm install deepsignal @preact/signals-react +``` + +If you are using the library with React, you should use the `deepsignal/react` import. You also need to install `@preact/signals-react`. ```js import { deepSignal } from "deepsignal/react"; @@ -83,7 +89,11 @@ const state = deepSignal({}); ### Without Preact/React -If you are using the library just with `@preact/signals-core`, you should use the `deepsignal/core` import. +```sh +npm install deepsignal @preact/signals-core +``` + +If you are using the library just with `@preact/signals-core`, you should use the `deepsignal/core` import. You also need to install `@preact/signals-core`. ```js import { deepSignal } from "deepsignal/core"; From 6284cd6db785a4ec48a6e2987fd6ea745cc36bdd Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:24:20 +0200 Subject: [PATCH 03/25] Add changeset --- .changeset/rude-llamas-develop.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/rude-llamas-develop.md diff --git a/.changeset/rude-llamas-develop.md b/.changeset/rude-llamas-develop.md new file mode 100644 index 0000000..6ebb116 --- /dev/null +++ b/.changeset/rude-llamas-develop.md @@ -0,0 +1,5 @@ +--- +"deepsignal": patch +--- + +Use `@preact/signals` dependencies as peer dependencies. From be8dc1d27fed8c05b737b6d67c81db5c6ee9671b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Aug 2023 13:29:57 +0000 Subject: [PATCH 04/25] Version Packages --- .changeset/rude-llamas-develop.md | 5 ----- packages/deepsignal/CHANGELOG.md | 6 ++++++ packages/deepsignal/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/rude-llamas-develop.md diff --git a/.changeset/rude-llamas-develop.md b/.changeset/rude-llamas-develop.md deleted file mode 100644 index 6ebb116..0000000 --- a/.changeset/rude-llamas-develop.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"deepsignal": patch ---- - -Use `@preact/signals` dependencies as peer dependencies. diff --git a/packages/deepsignal/CHANGELOG.md b/packages/deepsignal/CHANGELOG.md index f014422..6950ec8 100644 --- a/packages/deepsignal/CHANGELOG.md +++ b/packages/deepsignal/CHANGELOG.md @@ -1,5 +1,11 @@ # deepsignal +## 1.3.5 + +### Patch Changes + +- [#40](https://github.com/luisherranz/deepsignal/pull/40) [`6284cd6`](https://github.com/luisherranz/deepsignal/commit/6284cd6db785a4ec48a6e2987fd6ea745cc36bdd) Thanks [@luisherranz](https://github.com/luisherranz)! - Use `@preact/signals` dependencies as peer dependencies. + ## 1.3.4 ### Patch Changes diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index c340f91..050993b 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -1,6 +1,6 @@ { "name": "deepsignal", - "version": "1.3.4", + "version": "1.3.5", "license": "MIT", "description": "", "keywords": [], From b1c7cd996e0b86d5dc70ed7efdf3537183b76eb5 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:45:56 +0200 Subject: [PATCH 05/25] Add Preact as peer dependency back --- packages/deepsignal/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index 050993b..ced93fa 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -47,7 +47,8 @@ "peerDependencies": { "@preact/signals-core": "^1.3.1", "@preact/signals": "^1.1.4", - "@preact/signals-react": "^1.3.3" + "@preact/signals-react": "^1.3.3", + "preact": "^10.16.0" }, "devDependencies": { "preact": "10.9.0", From 79db35bebe4002c5d4e4ad77156b9ba609e14633 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:46:35 +0200 Subject: [PATCH 06/25] Changeset --- .changeset/smart-clocks-dance.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/smart-clocks-dance.md diff --git a/.changeset/smart-clocks-dance.md b/.changeset/smart-clocks-dance.md new file mode 100644 index 0000000..4a17e13 --- /dev/null +++ b/.changeset/smart-clocks-dance.md @@ -0,0 +1,5 @@ +--- +"deepsignal": patch +--- + +Add `preact` as peer dependency back. From ca320f831d265397ae0b6fbc092cbd0062101c34 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:50:18 +0200 Subject: [PATCH 07/25] Make peer dependencies optional --- packages/deepsignal/package.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index ced93fa..4576d03 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -50,6 +50,20 @@ "@preact/signals-react": "^1.3.3", "preact": "^10.16.0" }, + "peerDependenciesMeta": { + "@preact/signals-core": { + "optional": true + }, + "@preact/signals": { + "optional": true + }, + "@preact/signals-react": { + "optional": true + }, + "preact": { + "optional": true + } + }, "devDependencies": { "preact": "10.9.0", "preact-render-to-string": "^5.2.4", From a0531a6e7d3e522de91151bd33d0ecc0b253c928 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 4 Aug 2023 15:53:17 +0200 Subject: [PATCH 08/25] Update changelog --- .changeset/smart-clocks-dance.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/smart-clocks-dance.md b/.changeset/smart-clocks-dance.md index 4a17e13..003fefa 100644 --- a/.changeset/smart-clocks-dance.md +++ b/.changeset/smart-clocks-dance.md @@ -2,4 +2,4 @@ "deepsignal": patch --- -Add `preact` as peer dependency back. +Add `preact` as peer dependency back and mark them as optional. From e1ff0f679b805d51e937f6b423fe633b6970e3c1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 4 Aug 2023 13:56:29 +0000 Subject: [PATCH 09/25] Version Packages --- .changeset/smart-clocks-dance.md | 5 ----- packages/deepsignal/CHANGELOG.md | 6 ++++++ packages/deepsignal/package.json | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) delete mode 100644 .changeset/smart-clocks-dance.md diff --git a/.changeset/smart-clocks-dance.md b/.changeset/smart-clocks-dance.md deleted file mode 100644 index 003fefa..0000000 --- a/.changeset/smart-clocks-dance.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"deepsignal": patch ---- - -Add `preact` as peer dependency back and mark them as optional. diff --git a/packages/deepsignal/CHANGELOG.md b/packages/deepsignal/CHANGELOG.md index 6950ec8..ec3884e 100644 --- a/packages/deepsignal/CHANGELOG.md +++ b/packages/deepsignal/CHANGELOG.md @@ -1,5 +1,11 @@ # deepsignal +## 1.3.6 + +### Patch Changes + +- [#42](https://github.com/luisherranz/deepsignal/pull/42) [`79db35b`](https://github.com/luisherranz/deepsignal/commit/79db35bebe4002c5d4e4ad77156b9ba609e14633) Thanks [@luisherranz](https://github.com/luisherranz)! - Add `preact` as peer dependency back and mark them as optional. + ## 1.3.5 ### Patch Changes diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index 4576d03..0b2854c 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -1,6 +1,6 @@ { "name": "deepsignal", - "version": "1.3.5", + "version": "1.3.6", "license": "MIT", "description": "", "keywords": [], From bf1bae2c74bce112ee994a4faa23865614b4c90b Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 22 Sep 2023 09:30:39 +0200 Subject: [PATCH 10/25] Improve TypeScript docs --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6d819a9..4b4f341 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,8 @@ Use [Preact signals](https://github.com/preactjs/signals) with the interface of - [Passing the value of a signal directly to JSX](#passing-the-value-of-a-signal-directly-to-jsx) - [Passing a signal to a child component](#passing-a-signal-to-a-child-component) - [TypeScript](#typescript) + - [`DeepSignal`](#deepsignal-1) + - [`RevertDeepSignal`](#revertdeepsignal) - [License](#license) ## Features @@ -436,11 +438,25 @@ console.log(array.$![0].value); // 1 Note that here the position of the non-null assertion operator changes because `array.$` is an object in itself. -### DeepSignal and RevertDeepSignal types +DeepSignal exports two types, one to convert from a plain object/array to a `deepSignal` instance, and other to revert from a `deepSignal` instance back to the plain object/array. -DeepSignal exports two types, one to convert from a raw state/store to a `deepSignal` instance, and other to revert from a `deepSignal` instance back to the raw store. +### DeepSignal -These types are handy when manual casting is needed, like when you try to use `Object.values()`: +You can use the `DeepSignal` type if you want to declare your type instead of inferring it. + +```ts +import type { DeepSignal } from "deepsignal"; + +type Store = DeepSignal<{ + counter: boolean; +}>; + +const store = deepSignal({ counter: 0 }); +``` + +### RevertDeepSignal + +You can use the `RevertDeepSignal` type if you want to recover the type of the plain object/array using the type of the `deepSignal` instance, like for example when you need to use `Object.values()`. ```ts import type { RevertDeepSignal } from "deepsignal"; From b7024d1ac796be6bc4fd1cd770d2b2655ed99189 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 22 Sep 2023 09:50:44 +0200 Subject: [PATCH 11/25] Add test to make sure Object.assing works --- packages/deepsignal/core/test/index.test.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/deepsignal/core/test/index.test.tsx b/packages/deepsignal/core/test/index.test.tsx index a39f17d..f7ab99f 100644 --- a/packages/deepsignal/core/test/index.test.tsx +++ b/packages/deepsignal/core/test/index.test.tsx @@ -296,6 +296,16 @@ describe("deepsignal/core", () => { expect(store.a.nested.id).to.equal(4); expect(store.b.nested.id).to.equal(4); }); + + it.only("should be able to reset values with Object.assign", () => { + const initialNested = { ...nested }; + const initialState = { ...state, nested: initialNested }; + store.a = 2; + store.nested.b = 3; + Object.assign(store, initialState); + expect(store.a).to.equal(1); + expect(store.nested.b).to.equal(2); + }); }); describe("delete", () => { From 7b93d4e172bd04822a68d5de3fd64cb71ea1ce6f Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 22 Sep 2023 10:17:18 +0200 Subject: [PATCH 12/25] Add docs for resetting the store --- README.md | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 4b4f341..7cfd0f2 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,8 @@ Use [Preact signals](https://github.com/preactjs/signals) with the interface of - [`peek(state, "prop")`](#peekstate-prop) - [`state.$prop = signal(value)`](#stateprop--signalvalue) - [`useDeepSignal`](#usedeepsignal) + - [Common Patterns](#common-patterns) + - [Resetting the store](#resetting-the-store) - [When do you need access to signals?](#when-do-you-need-access-to-signals) - [Passing the value of a signal directly to JSX](#passing-the-value-of-a-signal-directly-to-jsx) - [Passing a signal to a child component](#passing-a-signal-to-a-child-component) @@ -47,7 +49,7 @@ Use [Preact signals](https://github.com/preactjs/signals) with the interface of ## Features -- **Transparent**: `deepsignal` wraps the objects with proxies that intercept all property accesses, but does not modify the object. This means that you can still use the object as you normally would, and it will behave exactly as expected. Mutating the object updates the value of the underlying signals. +- **Transparent**: `deepsignal` wraps the object with a proxy that intercepts all property accesses, but does not modify how you interact with the object. This means that you can still use the object as you normally would, and it will behave exactly as you would expect, except that mutating the object also updates the value of the underlying signals. - **Tiny (less than 1kB)**: `deepsignal` is designed to be lightweight and has a minimal footprint, making it easy to include in your projects. It's just a small wrapper around `@preact/signals-core`. - **Full array support**: `deepsignal` fully supports arrays, including nested arrays. - **Deep**: `deepsignal` converts nested objects and arrays to deep signal objects/arrays, allowing you to create fully reactive data structures. @@ -308,7 +310,7 @@ _For primitive values, you can get away with using `store.$prop.peek()` instead ### `state.$prop = signal(value)` -You can modify the underlying signal of an object's property doing an assignment to the `$`-prefixed name. +You can modify the underlying signal of an object's property by doing an assignment to the `$`-prefixed name. ```js const state = deepSignal({ counter: 0 }); @@ -350,6 +352,28 @@ function Counter() { } ``` +## Common Patterns + +### Resetting the store + +If you need to reset your store to some initial values, don't overwrite the reference. Instead, replace each value using something like `Object.assign`. + +```js +const initialState = { counter: 0 }; + +const store = deepSignal({ + ...initialState, + inc: () => { + store.counter++; + }, + reset: () => { + Object.assign(store, initialState); + }, +}); +``` + +Take into account that the object that you pass to `deepSignal` during the creation is also mutated when you mutate the deep signal. Therefore, if you need to keep a set of initial values, you need to store them in a different object or clone it before assigning it to the deepsignal. + ## When do you need access to signals? You will only need access to the underlying signals for performance optimizations. @@ -448,7 +472,7 @@ You can use the `DeepSignal` type if you want to declare your type instead of in import type { DeepSignal } from "deepsignal"; type Store = DeepSignal<{ - counter: boolean; + counter: boolean; }>; const store = deepSignal({ counter: 0 }); @@ -456,7 +480,7 @@ const store = deepSignal({ counter: 0 }); ### RevertDeepSignal -You can use the `RevertDeepSignal` type if you want to recover the type of the plain object/array using the type of the `deepSignal` instance, like for example when you need to use `Object.values()`. +You can use the `RevertDeepSignal` type if you want to recover the type of the plain object/array using the type of the `deepSignal` instance. For example, when you need to use `Object.values()`. ```ts import type { RevertDeepSignal } from "deepsignal"; From 24d7a97f692e5b8c5a4097f17527ac1ba61f08fe Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 22 Sep 2023 10:22:47 +0200 Subject: [PATCH 13/25] Remove only from test --- packages/deepsignal/core/test/index.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/deepsignal/core/test/index.test.tsx b/packages/deepsignal/core/test/index.test.tsx index f7ab99f..e714e76 100644 --- a/packages/deepsignal/core/test/index.test.tsx +++ b/packages/deepsignal/core/test/index.test.tsx @@ -297,7 +297,7 @@ describe("deepsignal/core", () => { expect(store.b.nested.id).to.equal(4); }); - it.only("should be able to reset values with Object.assign", () => { + it("should be able to reset values with Object.assign", () => { const initialNested = { ...nested }; const initialState = { ...state, nested: initialNested }; store.a = 2; From c7cb13803157a4fbe7813f685d03e9eede80c846 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Fri, 22 Sep 2023 10:26:50 +0200 Subject: [PATCH 14/25] Test also computations --- packages/deepsignal/core/test/index.test.tsx | 24 ++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/deepsignal/core/test/index.test.tsx b/packages/deepsignal/core/test/index.test.tsx index e714e76..339a433 100644 --- a/packages/deepsignal/core/test/index.test.tsx +++ b/packages/deepsignal/core/test/index.test.tsx @@ -764,6 +764,30 @@ describe("deepsignal/core", () => { expect(spy1).callCount(4); expect(spy2).callCount(4); }); + + it("should be able to reset values with Object.assign and still react to changes", () => { + const initialNested = { ...nested }; + const initialState = { ...state, nested: initialNested }; + let a, b; + + effect(() => { + a = store.a; + }); + effect(() => { + b = store.nested.b; + }); + + store.a = 2; + store.nested.b = 3; + + expect(a).to.equal(2); + expect(b).to.equal(3); + + Object.assign(store, initialState); + + expect(a).to.equal(1); + expect(b).to.equal(2); + }); }); describe("peek", () => { From b40b578adb30cc8e469cf07f6e385e68eb0d4608 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 18:30:44 +0100 Subject: [PATCH 15/25] Update package.json files --- packages/deepsignal/package.json | 8 ++++---- packages/deepsignal/react/package.json | 4 ++-- pnpm-lock.yaml | 20 ++++++++++---------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index 0b2854c..1de3f79 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -45,9 +45,9 @@ "prepublishOnly": "cp ../../README.md . && cd ../.. && pnpm build" }, "peerDependencies": { - "@preact/signals-core": "^1.3.1", + "@preact/signals-core": "^1.5.1", "@preact/signals": "^1.1.4", - "@preact/signals-react": "^1.3.3", + "@preact/signals-react": "^1.3.8 || ^2.0.0", "preact": "^10.16.0" }, "peerDependenciesMeta": { @@ -67,9 +67,9 @@ "devDependencies": { "preact": "10.9.0", "preact-render-to-string": "^5.2.4", - "@preact/signals-core": "^1.3.1", + "@preact/signals-core": "^1.5.1", "@preact/signals": "^1.1.4", - "@preact/signals-react": "^1.3.3", + "@preact/signals-react": "^2.0.0", "@types/react": "^18.0.18", "@types/react-dom": "^18.0.6", "react": "^18.2.0", diff --git a/packages/deepsignal/react/package.json b/packages/deepsignal/react/package.json index 8212290..fb59152 100644 --- a/packages/deepsignal/react/package.json +++ b/packages/deepsignal/react/package.json @@ -9,8 +9,8 @@ "source": "src/index.ts", "license": "MIT", "dependencies": { - "@preact/signals-core": "^1.3.1", - "@preact/signals-react": "^1.3.3" + "@preact/signals-core": "^1.5.1", + "@preact/signals-react": "^2.0.0" }, "peerDependencies": { "react": "17.x || 18.x" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 73f8e4c..d6d78aa 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -143,11 +143,11 @@ importers: specifier: ^1.1.4 version: 1.1.4(preact@10.9.0) '@preact/signals-core': - specifier: ^1.3.1 - version: 1.3.1 + specifier: ^1.5.1 + version: 1.5.1 '@preact/signals-react': - specifier: ^1.3.3 - version: 1.3.3(react@18.2.0) + specifier: ^2.0.0 + version: 2.0.0(react@18.2.0) '@types/react': specifier: ^18.0.18 version: 18.0.27 @@ -1897,16 +1897,16 @@ packages: fastq: 1.13.0 dev: true - /@preact/signals-core@1.3.1: - resolution: {integrity: sha512-DL+3kDssZ3UOMz9HufwSYE/gK0+TnT1jzegfF5rstgyPrnyfjz4BHAoxmzQA6Mkp4UlKe8qjsgl3v5a/obzNig==} + /@preact/signals-core@1.5.1: + resolution: {integrity: sha512-dE6f+WCX5ZUDwXzUIWNMhhglmuLpqJhuy3X3xHrhZYI0Hm2LyQwOu0l9mdPiWrVNsE+Q7txOnJPgtIqHCYoBVA==} dev: true - /@preact/signals-react@1.3.3(react@18.2.0): - resolution: {integrity: sha512-Tbv+oWPcrWowAJp1U1eWFiFUJihulOAnL8g/hJ3fUsP0IcsKsj8U0OcIDrwemIPQe7+J/3FuudkZzted0MD/bA==} + /@preact/signals-react@2.0.0(react@18.2.0): + resolution: {integrity: sha512-tMVi2SXFXlojaiPNWa8dlYaidR/XvEgMSp+iymKJgMssBM/QVtUQrodKZek1BJju+dkVHiyeuQHmkuLOI9oyNw==} peerDependencies: react: ^16.14.0 || 17.x || 18.x dependencies: - '@preact/signals-core': 1.3.1 + '@preact/signals-core': 1.5.1 react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) dev: true @@ -1916,7 +1916,7 @@ packages: peerDependencies: preact: 10.x dependencies: - '@preact/signals-core': 1.3.1 + '@preact/signals-core': 1.5.1 preact: 10.9.0 dev: true From 6120429ad2e58f17707c03f6145b61b4dbe5ca43 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 18:30:49 +0100 Subject: [PATCH 16/25] Update test --- packages/deepsignal/react/test/index.test.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/deepsignal/react/test/index.test.tsx b/packages/deepsignal/react/test/index.test.tsx index e8f91e9..6663628 100644 --- a/packages/deepsignal/react/test/index.test.tsx +++ b/packages/deepsignal/react/test/index.test.tsx @@ -6,6 +6,7 @@ import { createElement } from "react"; import { createRoot, Root } from "react-dom/client"; import { act } from "react-dom/test-utils"; import { useDeepSignal, type DeepSignal } from "deepsignal/react"; +import "@preact/signals/auto"; describe("deepsignal/react", () => { let scratch: HTMLDivElement; From 4140b8c2ea6dabf8f4e8d78c460fba8dab15afe5 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 18:30:53 +0100 Subject: [PATCH 17/25] Update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 7cfd0f2..15c1281 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,8 @@ import { deepSignal } from "deepsignal/react"; const state = deepSignal({}); ``` +Starting from `@preact/signals-react@2.0.0`, you should also follow the [React integration guide of `@preact/signals-react`](https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration) to choose one of the integration methods. + ### Without Preact/React ```sh From 9b0ebbba3707f4170596671e97975c15e1b7650c Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 18:30:58 +0100 Subject: [PATCH 18/25] Add changelog --- .changeset/strong-adults-march.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/strong-adults-march.md diff --git a/.changeset/strong-adults-march.md b/.changeset/strong-adults-march.md new file mode 100644 index 0000000..beeff16 --- /dev/null +++ b/.changeset/strong-adults-march.md @@ -0,0 +1,5 @@ +--- +"deepsignal": minor +--- + +Add a peer dependency for @preact/signals-react 2.0.0 From 21b7bf213a36f137c07b0d262b7bab6633f033e9 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 19:21:57 +0100 Subject: [PATCH 19/25] Force useDeepSignal to track the component --- .changeset/strong-adults-march.md | 2 +- README.md | 3 ++- packages/deepsignal/react/src/index.ts | 2 ++ packages/deepsignal/react/test/index.test.tsx | 22 +++++++++++-------- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.changeset/strong-adults-march.md b/.changeset/strong-adults-march.md index beeff16..29b6c17 100644 --- a/.changeset/strong-adults-march.md +++ b/.changeset/strong-adults-march.md @@ -2,4 +2,4 @@ "deepsignal": minor --- -Add a peer dependency for @preact/signals-react 2.0.0 +Add support for @preact/signals-react 2.0.0 diff --git a/README.md b/README.md index 15c1281..374c48f 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,8 @@ import { deepSignal } from "deepsignal/react"; const state = deepSignal({}); ``` -Starting from `@preact/signals-react@2.0.0`, you should also follow the [React integration guide of `@preact/signals-react`](https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration) to choose one of the integration methods. +- If you want to use `deepSignal` outside of the components, please follow the [React integration guide of `@preact/signals-react`](https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration) to choose one of the integration methods. +- If rely exclusively on `useDeepSignal`, no integration is required. ### Without Preact/React diff --git a/packages/deepsignal/react/src/index.ts b/packages/deepsignal/react/src/index.ts index 5bf6ef5..8b3993a 100644 --- a/packages/deepsignal/react/src/index.ts +++ b/packages/deepsignal/react/src/index.ts @@ -1,8 +1,10 @@ import "@preact/signals-react"; import { useMemo } from "react"; import { deepSignal, type DeepSignal } from "../../core/src"; +import { useSignals } from "@preact/signals-react/runtime"; export const useDeepSignal = (obj: T): DeepSignal => { + useSignals(); return useMemo(() => deepSignal(obj), []); }; diff --git a/packages/deepsignal/react/test/index.test.tsx b/packages/deepsignal/react/test/index.test.tsx index 6663628..52463d3 100644 --- a/packages/deepsignal/react/test/index.test.tsx +++ b/packages/deepsignal/react/test/index.test.tsx @@ -6,24 +6,28 @@ import { createElement } from "react"; import { createRoot, Root } from "react-dom/client"; import { act } from "react-dom/test-utils"; import { useDeepSignal, type DeepSignal } from "deepsignal/react"; -import "@preact/signals/auto"; describe("deepsignal/react", () => { let scratch: HTMLDivElement; let root: Root; - function render(element: Parameters[0]) { - act(() => root.render(element)); - } + let render: Root["render"]; - const window = globalThis as any; + beforeEach(async () => { + scratch = document.createElement("div"); + document.body.appendChild(scratch); - beforeEach(() => { - scratch = window.document.createElement("div"); - root = createRoot(scratch); + const realRoot = createRoot(scratch); + root = { + render: element => act(() => realRoot.render(element)), + unmount: () => act(() => realRoot.unmount()), + }; + + render = root.render.bind(root); }); afterEach(() => { act(() => root.unmount()); + scratch.remove(); }); describe("useDeepSignal", () => { @@ -38,7 +42,7 @@ describe("deepsignal/react", () => { } // @ts-ignore - render(); + await render(); expect(scratch.textContent).to.equal("test"); expect(spy).to.be.calledOnce; From 0c740e76ffc7f8b22dfee18a21537d1d452d4527 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 19:23:11 +0100 Subject: [PATCH 20/25] Improve readme mesage --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 374c48f..dfafb55 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,7 @@ const state = deepSignal({}); ``` - If you want to use `deepSignal` outside of the components, please follow the [React integration guide of `@preact/signals-react`](https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration) to choose one of the integration methods. -- If rely exclusively on `useDeepSignal`, no integration is required. +- For `useDeepSignal`, no integration is required. ### Without Preact/React From af938aa62fb1276aedf755d60c176fcc00c68646 Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 21:06:32 +0100 Subject: [PATCH 21/25] Add Lit example --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index dfafb55..b3039f3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ Use [Preact signals](https://github.com/preactjs/signals) with the interface of - [Preact & TypeScript](https://stackblitz.com/edit/vitejs-vite-hktyyf?file=src%2Fmain.tsx) - [React](https://stackblitz.com/edit/vitejs-vite-zoh464?file=src%2Fmain.jsx) - [React & TypeScript](https://stackblitz.com/edit/vitejs-vite-r2stgq?file=src%2Fmain.tsx) + - [Lit](https://stackblitz.com/edit/lit-and-deepsignal?file=src%2Fmy-element.js) - Or on Codesandbox - [Preact](https://codesandbox.io/s/deepsignal-demo-hv1i1p) - [Preact & TypeScript](https://codesandbox.io/s/deepsignal-demo-typescript-os7ox0?file=/src/index.tsx) @@ -74,7 +75,11 @@ If you are using `deepsignal` with Preact (`@preact/signals`), you should use th ```js import { deepSignal } from "deepsignal"; -const state = deepSignal({}); +const state = deepSignal({ + count: 0, +}); + +const Count = () =>
{state.$count}
; ``` ### With React @@ -83,18 +88,47 @@ const state = deepSignal({}); npm install deepsignal @preact/signals-react ``` -If you are using the library with React, you should use the `deepsignal/react` import. You also need to install `@preact/signals-react`. +If you are using the library with React (`@preact/signals-react`), you should use the `deepsignal/react` import. You also need to install `@preact/signals-react`. ```js import { deepSignal } from "deepsignal/react"; -const state = deepSignal({}); +const state = deepSignal({ + count: 0, +}); + +const Count = () =>
{state.$count}
; ``` - If you want to use `deepSignal` outside of the components, please follow the [React integration guide of `@preact/signals-react`](https://github.com/preactjs/signals/blob/main/packages/react/README.md#react-integration) to choose one of the integration methods. - For `useDeepSignal`, no integration is required. -### Without Preact/React +### With Lit + +Lit now [supports Preact Signals](https://lit.dev/blog/2023-10-10-lit-3.0/#preact-signals-integration), so you can also use `deepsignal` in Lit. + +```sh +npm install deepsignal @lit-labs/preact-signals +``` + +If you are using the library just with Lit, you should use the `deepsignal/core` import. You also need to install `@lit-labs/preact-signals` and use its `SignalWatcher` function. + +```js +import { SignalWatcher } from "@lit-labs/preact-signals"; +import { deepSignal } from "deepsignal/core"; + +const state = deepSignal({ + count: 0, +}); + +class Count extends SignalWatcher(LitElement) { + render() { + return html`
${state.$count}
`; + } +} +``` + +### Without Preact/React/Lit ```sh npm install deepsignal @preact/signals-core @@ -103,9 +137,16 @@ npm install deepsignal @preact/signals-core If you are using the library just with `@preact/signals-core`, you should use the `deepsignal/core` import. You also need to install `@preact/signals-core`. ```js +import { effect } from "@preact/signals-core"; import { deepSignal } from "deepsignal/core"; -const state = deepSignal({}); +const state = deepSignal({ + count: 0, +}); + +effect(() => { + console.log(`Count: ${state.count}`); +}); ``` This is because the `deepsignal` import includes a dependency on `@preact/signals`, while the `deepsignal/core` import does not. This allows you to use deep signals with either `@preact/signals` or `@preact/signals-core`, depending on your needs. **Do not use both.** From 66f39f88d84bd56024ca6de3af3af431e2d9b48e Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 21:41:00 +0100 Subject: [PATCH 22/25] Add failing test --- packages/deepsignal/core/test/index.test.tsx | 40 ++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/deepsignal/core/test/index.test.tsx b/packages/deepsignal/core/test/index.test.tsx index 339a433..f1b775d 100644 --- a/packages/deepsignal/core/test/index.test.tsx +++ b/packages/deepsignal/core/test/index.test.tsx @@ -189,6 +189,21 @@ describe("deepsignal/core", () => { expect(store.nested.b).to.equal(3); }); + it("should support setting values with setters", () => { + const store = deepSignal({ + counter: 1, + get double() { + return store.counter * 2; + }, + set double(val) { + store.counter = val / 2; + }, + }); + expect(store.counter).to.equal(1); + store.double = 4; + expect(store.counter).to.equal(2); + }); + it("should update array length", () => { expect(store.array.length).to.equal(2); store.array.push(4); @@ -394,6 +409,31 @@ describe("deepsignal/core", () => { }); describe("computations", () => { + it("should subscribe to values mutated with setters", () => { + const store = deepSignal({ + counter: 1, + get double() { + return store.counter * 2; + }, + set double(val) { + store.counter = val / 2; + }, + }); + let counter = 0; + let double = 0; + + effect(() => { + counter = store.counter; + double = store.double; + }); + + expect(counter).to.equal(1); + expect(double).to.equal(2); + store.double = 4; + expect(counter).to.equal(2); + expect(double).to.equal(4); + }); + it("should subscribe to changes when an item is removed from the array", () => { const store = deepSignal([0, 0, 0]); let sum = 0; From 0e1f65e06f343ad85e0c8cedabdd663b1ed02f8b Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 21:41:10 +0100 Subject: [PATCH 23/25] Fix the test --- packages/deepsignal/core/src/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/deepsignal/core/src/index.ts b/packages/deepsignal/core/src/index.ts index ae9e7bb..f67b69a 100644 --- a/packages/deepsignal/core/src/index.ts +++ b/packages/deepsignal/core/src/index.ts @@ -6,6 +6,7 @@ const arrayToArrayOfSignals = new WeakMap(); const proxies = new WeakSet(); const objToIterable = new WeakMap(); const rg = /^\$/; +const descriptor = Object.getOwnPropertyDescriptor; let peeking = false; export const deepSignal = (obj: T): DeepSignal => { @@ -58,7 +59,7 @@ const get = const key = returnSignal ? fullKey.replace(rg, "") : fullKey; if ( !signals.has(key) && - typeof Object.getOwnPropertyDescriptor(target, key)?.get === "function" + typeof descriptor(target, key)?.get === "function" ) { signals.set( key, @@ -83,6 +84,8 @@ const get = const objectHandlers = { get: get(false), set(target: object, fullKey: string, val: any, receiver: object): boolean { + if (typeof descriptor(target, fullKey)?.set === "function") + return Reflect.set(target, fullKey, val, receiver); if (!proxyToSignals.has(receiver)) proxyToSignals.set(receiver, new Map()); const signals = proxyToSignals.get(receiver); if (fullKey[0] === "$") { From beee51e38c56ff94ccb6b3b14583a34f629a006a Mon Sep 17 00:00:00 2001 From: Luis Herranz Date: Thu, 11 Jan 2024 21:41:47 +0100 Subject: [PATCH 24/25] Add changeset --- .changeset/hot-ties-play.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hot-ties-play.md diff --git a/.changeset/hot-ties-play.md b/.changeset/hot-ties-play.md new file mode 100644 index 0000000..0bb9b65 --- /dev/null +++ b/.changeset/hot-ties-play.md @@ -0,0 +1,5 @@ +--- +"deepsignal": minor +--- + +Add support for setters. From bb412500c571faef9cf37734630f48241e443434 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 11 Jan 2024 20:48:19 +0000 Subject: [PATCH 25/25] Version Packages --- .changeset/hot-ties-play.md | 5 ----- .changeset/strong-adults-march.md | 5 ----- packages/deepsignal/CHANGELOG.md | 8 ++++++++ packages/deepsignal/package.json | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) delete mode 100644 .changeset/hot-ties-play.md delete mode 100644 .changeset/strong-adults-march.md diff --git a/.changeset/hot-ties-play.md b/.changeset/hot-ties-play.md deleted file mode 100644 index 0bb9b65..0000000 --- a/.changeset/hot-ties-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"deepsignal": minor ---- - -Add support for setters. diff --git a/.changeset/strong-adults-march.md b/.changeset/strong-adults-march.md deleted file mode 100644 index 29b6c17..0000000 --- a/.changeset/strong-adults-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"deepsignal": minor ---- - -Add support for @preact/signals-react 2.0.0 diff --git a/packages/deepsignal/CHANGELOG.md b/packages/deepsignal/CHANGELOG.md index ec3884e..34b41d8 100644 --- a/packages/deepsignal/CHANGELOG.md +++ b/packages/deepsignal/CHANGELOG.md @@ -1,5 +1,13 @@ # deepsignal +## 1.4.0 + +### Minor Changes + +- [#62](https://github.com/luisherranz/deepsignal/pull/62) [`beee51e`](https://github.com/luisherranz/deepsignal/commit/beee51e38c56ff94ccb6b3b14583a34f629a006a) Thanks [@luisherranz](https://github.com/luisherranz)! - Add support for setters. + +* [#59](https://github.com/luisherranz/deepsignal/pull/59) [`9b0ebbb`](https://github.com/luisherranz/deepsignal/commit/9b0ebbba3707f4170596671e97975c15e1b7650c) Thanks [@luisherranz](https://github.com/luisherranz)! - Add support for @preact/signals-react 2.0.0 + ## 1.3.6 ### Patch Changes diff --git a/packages/deepsignal/package.json b/packages/deepsignal/package.json index 1de3f79..7d40fd1 100644 --- a/packages/deepsignal/package.json +++ b/packages/deepsignal/package.json @@ -1,6 +1,6 @@ { "name": "deepsignal", - "version": "1.3.6", + "version": "1.4.0", "license": "MIT", "description": "", "keywords": [],