ccstate-solid 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 e7h4n
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN EFFECT OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,3 @@
1
+ {
2
+ "presets": [["@babel/preset-env"], ["@babel/preset-typescript"]]
3
+ }
Binary file
package/dist/index.cjs ADDED
@@ -0,0 +1,110 @@
1
+ 'use strict';
2
+
3
+ var solidJs = require('solid-js');
4
+ var ccstate = require('ccstate');
5
+
6
+ function _arrayLikeToArray(r, a) {
7
+ (null == a || a > r.length) && (a = r.length);
8
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
9
+ return n;
10
+ }
11
+ function _arrayWithHoles(r) {
12
+ if (Array.isArray(r)) return r;
13
+ }
14
+ function _iterableToArrayLimit(r, l) {
15
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
16
+ if (null != t) {
17
+ var e,
18
+ n,
19
+ i,
20
+ u,
21
+ a = [],
22
+ f = !0,
23
+ o = !1;
24
+ try {
25
+ if (i = (t = t.call(r)).next, 0 === l) {
26
+ if (Object(t) !== t) return;
27
+ f = !1;
28
+ } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
29
+ } catch (r) {
30
+ o = !0, n = r;
31
+ } finally {
32
+ try {
33
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
34
+ } finally {
35
+ if (o) throw n;
36
+ }
37
+ }
38
+ return a;
39
+ }
40
+ }
41
+ function _nonIterableRest() {
42
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
43
+ }
44
+ function _slicedToArray(r, e) {
45
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
46
+ }
47
+ function _unsupportedIterableToArray(r, a) {
48
+ if (r) {
49
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
50
+ var t = {}.toString.call(r).slice(8, -1);
51
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
52
+ }
53
+ }
54
+
55
+ var StoreContext = solidJs.createContext(null);
56
+ var StoreProvider = StoreContext.Provider;
57
+ function useStore() {
58
+ var store = solidJs.useContext(StoreContext);
59
+ if (!store) {
60
+ return ccstate.getDefaultStore();
61
+ }
62
+ return store;
63
+ }
64
+
65
+ function useGet(atom) {
66
+ var store = useStore();
67
+ var _createSignal = solidJs.createSignal(store.get(atom)),
68
+ _createSignal2 = _slicedToArray(_createSignal, 2),
69
+ value = _createSignal2[0],
70
+ setValue = _createSignal2[1];
71
+ var unsub = store.sub(atom, ccstate.command(function () {
72
+ setValue(function () {
73
+ return store.get(atom);
74
+ });
75
+ }));
76
+ solidJs.onCleanup(function () {
77
+ unsub();
78
+ });
79
+ return value;
80
+ }
81
+
82
+ function useSet(atom) {
83
+ var store = useStore();
84
+ if ('write' in atom) {
85
+ return function () {
86
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
87
+ args[_key] = arguments[_key];
88
+ }
89
+ var ret = store.set.apply(store, [atom].concat(args));
90
+ return ret;
91
+ };
92
+ }
93
+ return function (value) {
94
+ store.set(atom, value);
95
+ };
96
+ }
97
+
98
+ function useResource(atom) {
99
+ var _createResource = solidJs.createResource(useGet(atom), function (promise) {
100
+ return promise;
101
+ }),
102
+ _createResource2 = _slicedToArray(_createResource, 1),
103
+ data = _createResource2[0];
104
+ return data;
105
+ }
106
+
107
+ exports.StoreProvider = StoreProvider;
108
+ exports.useGet = useGet;
109
+ exports.useResource = useResource;
110
+ exports.useSet = useSet;
@@ -0,0 +1,14 @@
1
+ import * as solid_js from 'solid-js';
2
+ import { Resource } from 'solid-js';
3
+ import { State, Computed, Updater, Command, Store } from 'ccstate';
4
+
5
+ declare function useGet<T>(atom: State<T> | Computed<T>): solid_js.Accessor<T>;
6
+
7
+ declare function useSet<T>(atom: State<T>): (value: T | Updater<T>) => void;
8
+ declare function useSet<T, ARGS extends unknown[]>(atom: Command<T, ARGS>): (...args: ARGS) => T;
9
+
10
+ declare function useResource<T>(atom: State<Promise<T>> | Computed<Promise<T>>): Resource<T>;
11
+
12
+ declare const StoreProvider: solid_js.ContextProviderComponent<Store | null>;
13
+
14
+ export { StoreProvider, useGet, useResource, useSet };
@@ -0,0 +1,14 @@
1
+ import * as solid_js from 'solid-js';
2
+ import { Resource } from 'solid-js';
3
+ import { State, Computed, Updater, Command, Store } from 'ccstate';
4
+
5
+ declare function useGet<T>(atom: State<T> | Computed<T>): solid_js.Accessor<T>;
6
+
7
+ declare function useSet<T>(atom: State<T>): (value: T | Updater<T>) => void;
8
+ declare function useSet<T, ARGS extends unknown[]>(atom: Command<T, ARGS>): (...args: ARGS) => T;
9
+
10
+ declare function useResource<T>(atom: State<Promise<T>> | Computed<Promise<T>>): Resource<T>;
11
+
12
+ declare const StoreProvider: solid_js.ContextProviderComponent<Store | null>;
13
+
14
+ export { StoreProvider, useGet, useResource, useSet };
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ import { createContext, useContext, createSignal, onCleanup, createResource } from 'solid-js';
2
+ import { getDefaultStore, command } from 'ccstate';
3
+
4
+ function _arrayLikeToArray(r, a) {
5
+ (null == a || a > r.length) && (a = r.length);
6
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
7
+ return n;
8
+ }
9
+ function _arrayWithHoles(r) {
10
+ if (Array.isArray(r)) return r;
11
+ }
12
+ function _iterableToArrayLimit(r, l) {
13
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
14
+ if (null != t) {
15
+ var e,
16
+ n,
17
+ i,
18
+ u,
19
+ a = [],
20
+ f = !0,
21
+ o = !1;
22
+ try {
23
+ if (i = (t = t.call(r)).next, 0 === l) {
24
+ if (Object(t) !== t) return;
25
+ f = !1;
26
+ } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
27
+ } catch (r) {
28
+ o = !0, n = r;
29
+ } finally {
30
+ try {
31
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
32
+ } finally {
33
+ if (o) throw n;
34
+ }
35
+ }
36
+ return a;
37
+ }
38
+ }
39
+ function _nonIterableRest() {
40
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
41
+ }
42
+ function _slicedToArray(r, e) {
43
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
44
+ }
45
+ function _unsupportedIterableToArray(r, a) {
46
+ if (r) {
47
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
48
+ var t = {}.toString.call(r).slice(8, -1);
49
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
50
+ }
51
+ }
52
+
53
+ var StoreContext = createContext(null);
54
+ var StoreProvider = StoreContext.Provider;
55
+ function useStore() {
56
+ var store = useContext(StoreContext);
57
+ if (!store) {
58
+ return getDefaultStore();
59
+ }
60
+ return store;
61
+ }
62
+
63
+ function useGet(atom) {
64
+ var store = useStore();
65
+ var _createSignal = createSignal(store.get(atom)),
66
+ _createSignal2 = _slicedToArray(_createSignal, 2),
67
+ value = _createSignal2[0],
68
+ setValue = _createSignal2[1];
69
+ var unsub = store.sub(atom, command(function () {
70
+ setValue(function () {
71
+ return store.get(atom);
72
+ });
73
+ }));
74
+ onCleanup(function () {
75
+ unsub();
76
+ });
77
+ return value;
78
+ }
79
+
80
+ function useSet(atom) {
81
+ var store = useStore();
82
+ if ('write' in atom) {
83
+ return function () {
84
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
85
+ args[_key] = arguments[_key];
86
+ }
87
+ var ret = store.set.apply(store, [atom].concat(args));
88
+ return ret;
89
+ };
90
+ }
91
+ return function (value) {
92
+ store.set(atom, value);
93
+ };
94
+ }
95
+
96
+ function useResource(atom) {
97
+ var _createResource = createResource(useGet(atom), function (promise) {
98
+ return promise;
99
+ }),
100
+ _createResource2 = _slicedToArray(_createResource, 1),
101
+ data = _createResource2[0];
102
+ return data;
103
+ }
104
+
105
+ export { StoreProvider, useGet, useResource, useSet };
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "ccstate-solid",
3
+ "version": "3.0.0",
4
+ "description": "CCState Solid.js Hooks",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/e7h4n/ccstate.git"
8
+ },
9
+ "license": "MIT",
10
+ "type": "module",
11
+ "main": "./dist/index.cjs",
12
+ "module": "./dist/index.js",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/index.js",
16
+ "require": "./dist/index.cjs"
17
+ }
18
+ },
19
+ "peerDependencies": {
20
+ "solid-js": ">=1.6.0",
21
+ "ccstate": "^3.0.0"
22
+ },
23
+ "devDependencies": {
24
+ "@babel/preset-env": "^7.26.0",
25
+ "@babel/preset-typescript": "^7.26.0",
26
+ "@rollup/plugin-babel": "^6.0.4",
27
+ "@rollup/plugin-node-resolve": "^15.3.0",
28
+ "@solidjs/testing-library": "^0.8.10",
29
+ "@testing-library/jest-dom": "^6.6.3",
30
+ "@testing-library/user-event": "^14.5.2",
31
+ "happy-dom": "^15.11.7",
32
+ "jest-leak-detector": "^29.7.0",
33
+ "json": "^11.0.0",
34
+ "rollup": "^4.28.1",
35
+ "rollup-plugin-dts": "^6.1.1",
36
+ "shx": "^0.3.4",
37
+ "signal-timers": "^1.0.4",
38
+ "solid-js": "^1.9.3",
39
+ "vite-plugin-solid": "^2.11.0",
40
+ "vitest": "^2.1.8",
41
+ "ccstate": "^3.0.0"
42
+ },
43
+ "scripts": {
44
+ "build": "rollup -c",
45
+ "prebuild": "shx rm -rf dist"
46
+ }
47
+ }
@@ -0,0 +1,84 @@
1
+ // @ts-check
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { babel } from '@rollup/plugin-babel';
5
+ import { dts } from 'rollup-plugin-dts';
6
+ import { nodeResolve } from '@rollup/plugin-node-resolve';
7
+
8
+ const __dirname = path.dirname(new URL(import.meta.url).pathname);
9
+ const projectRootDir = path.resolve(__dirname);
10
+
11
+ /**
12
+ * @param {string} id
13
+ * @returns {boolean}
14
+ */
15
+ function external(id) {
16
+ return !id.startsWith('.') && !id.startsWith(projectRootDir);
17
+ }
18
+
19
+ /**
20
+ * @param {{input:string, targetCJS:string, targetES:string}} param0
21
+ * @returns {import('rollup').RollupOptions[]}
22
+ */
23
+ function generateTarget({ input, targetCJS, targetES }) {
24
+ return [
25
+ {
26
+ input,
27
+ onwarn: (warning) => {
28
+ throw new Error(warning?.message);
29
+ },
30
+ external,
31
+ plugins: [
32
+ nodeResolve({
33
+ extensions: ['.ts'],
34
+ }),
35
+ babel({
36
+ exclude: 'node_modules/**',
37
+ extensions: ['.ts'],
38
+ babelHelpers: 'bundled',
39
+ configFile: path.resolve(projectRootDir, './babel.config.json'),
40
+ }),
41
+ ],
42
+ output: [
43
+ {
44
+ file: targetCJS,
45
+ format: 'cjs',
46
+ },
47
+ {
48
+ file: targetES,
49
+ format: 'es',
50
+ },
51
+ ],
52
+ },
53
+ {
54
+ input,
55
+ onwarn: (warning) => {
56
+ throw new Error(warning?.message);
57
+ },
58
+ external,
59
+ plugins: [
60
+ dts({
61
+ respectExternal: true,
62
+ tsconfig: path.resolve(projectRootDir, './tsconfig.json'),
63
+ // https://github.com/Swatinem/rollup-plugin-dts/issues/143
64
+ compilerOptions: { preserveSymlinks: false },
65
+ }),
66
+ ],
67
+ output: [
68
+ {
69
+ file: targetCJS.replace(/\.cjs$/, '.d.cts'),
70
+ },
71
+ {
72
+ file: targetES.replace(/\.js$/, '.d.ts'),
73
+ },
74
+ ],
75
+ },
76
+ ];
77
+ }
78
+
79
+ /** @type { Array<import('rollup').RollupOptions> } */
80
+ export default generateTarget({
81
+ input: './src/index.ts',
82
+ targetCJS: './dist/index.cjs',
83
+ targetES: './dist/index.js',
84
+ });
@@ -0,0 +1,189 @@
1
+ import { render } from '@solidjs/testing-library';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { expect, it } from 'vitest';
4
+ import { computed, command, state, createDebugStore, getDefaultStore } from 'ccstate';
5
+ import { StoreProvider, useGet, useSet } from '..';
6
+ import { createSignal } from 'solid-js';
7
+ import '@testing-library/jest-dom/vitest';
8
+
9
+ it('using ccstate in solid', async () => {
10
+ const base$ = state(0);
11
+
12
+ function App() {
13
+ const ret = useGet(base$);
14
+ return <div>{ret()}</div>;
15
+ }
16
+
17
+ const screen = render(() => <App />);
18
+
19
+ expect(screen.getByText('0')).toBeInTheDocument();
20
+ getDefaultStore().set(base$, 1);
21
+ expect(await screen.findByText('1')).toBeInTheDocument();
22
+ });
23
+
24
+ it('computed should re-render', async () => {
25
+ const base$ = state(0);
26
+ const derived$ = computed((get) => get(base$) * 2);
27
+
28
+ function App() {
29
+ const ret = useGet(derived$);
30
+ return <div>{ret()}</div>;
31
+ }
32
+
33
+ const screen = render(() => <App />);
34
+
35
+ expect(screen.getByText('0')).toBeInTheDocument();
36
+ getDefaultStore().set(base$, 1);
37
+ expect(await screen.findByText('2')).toBeInTheDocument();
38
+ });
39
+
40
+ it('user click counter should increment', async () => {
41
+ const count$ = state(0);
42
+ const onClick$ = command(({ get, set }) => {
43
+ const ret = get(count$);
44
+ set(count$, ret + 1);
45
+ });
46
+
47
+ function App() {
48
+ const ret = useGet(count$);
49
+ const onClick = useSet(onClick$);
50
+
51
+ return <button onClick={onClick}>{ret()}</button>;
52
+ }
53
+
54
+ const screen = render(() => <App />);
55
+ const button = screen.getByText('0');
56
+ expect(button).toBeInTheDocument();
57
+
58
+ const user = userEvent.setup();
59
+ await user.click(button);
60
+ expect(screen.getByText('1')).toBeInTheDocument();
61
+ await user.click(button);
62
+ expect(screen.getByText('2')).toBeInTheDocument();
63
+ });
64
+
65
+ it('two atom changes should re-render once', async () => {
66
+ const state1$ = state(0);
67
+ const state2$ = state(0);
68
+
69
+ function App() {
70
+ const ret1 = useGet(state1$);
71
+ const ret2 = useGet(state2$);
72
+ return <div>{ret1() + ret2()}</div>;
73
+ }
74
+
75
+ const screen = render(() => <App />);
76
+ expect(screen.getByText('0')).toBeInTheDocument();
77
+
78
+ getDefaultStore().set(state1$, 1);
79
+ getDefaultStore().set(state2$, 2);
80
+ await Promise.resolve();
81
+ expect(screen.getByText('3')).toBeInTheDocument();
82
+ });
83
+
84
+ it('async callback will trigger rerender', async () => {
85
+ const count$ = state(0);
86
+ const onClick$ = command(({ get, set }) => {
87
+ return Promise.resolve().then(() => {
88
+ set(count$, get(count$) + 1);
89
+ });
90
+ });
91
+
92
+ function App() {
93
+ const val = useGet(count$);
94
+ const onClick = useSet(onClick$);
95
+ return (
96
+ <button
97
+ onClick={() => {
98
+ void onClick();
99
+ }}
100
+ >
101
+ {val()}
102
+ </button>
103
+ );
104
+ }
105
+
106
+ const screen = render(() => <App />);
107
+ const button = screen.getByText('0');
108
+ expect(button).toBeInTheDocument();
109
+
110
+ const user = userEvent.setup();
111
+ await user.click(button);
112
+ expect(screen.getByText('1')).toBeInTheDocument();
113
+ });
114
+
115
+ it('floating promise trigger rerender', async () => {
116
+ const count$ = state(0);
117
+ const onClick$ = command(({ get, set }) => {
118
+ void Promise.resolve().then(() => {
119
+ set(count$, get(count$) + 1);
120
+ });
121
+ });
122
+
123
+ function App() {
124
+ const val = useGet(count$);
125
+ const onClick = useSet(onClick$);
126
+ return <button onClick={onClick}>{val()}</button>;
127
+ }
128
+
129
+ const screen = render(() => <App />);
130
+ const button = screen.getByText('0');
131
+ expect(button).toBeInTheDocument();
132
+
133
+ const user = userEvent.setup();
134
+ await user.click(button);
135
+ expect(await screen.findByText('1')).toBeInTheDocument();
136
+ });
137
+
138
+ it('should use default store if no provider', () => {
139
+ const count$ = state(0);
140
+ getDefaultStore().set(count$, 10);
141
+
142
+ function App() {
143
+ const count = useGet(count$);
144
+ return <div>{count()}</div>;
145
+ }
146
+
147
+ const screen = render(() => <App />);
148
+ expect(screen.getByText('10')).toBeInTheDocument();
149
+ });
150
+
151
+ it('will unmount when component cleanup', async () => {
152
+ const base$ = state(0);
153
+
154
+ function App() {
155
+ const ret = useGet(base$);
156
+ return <div>{ret()}</div>;
157
+ }
158
+
159
+ function Container() {
160
+ const [show, setShow] = createSignal(true);
161
+
162
+ return (
163
+ <div>
164
+ {show() ? <App /> : <div>unmounted</div>}
165
+ <button
166
+ onClick={() => {
167
+ setShow(false);
168
+ }}
169
+ >
170
+ hide
171
+ </button>
172
+ </div>
173
+ );
174
+ }
175
+
176
+ const store = createDebugStore();
177
+ const screen = render(() => (
178
+ <StoreProvider value={store}>
179
+ <Container />
180
+ </StoreProvider>
181
+ ));
182
+
183
+ const user = userEvent.setup();
184
+ expect(store.getSubscribeGraph()).toHaveLength(1);
185
+ const button = screen.getByText('hide');
186
+ await user.click(button);
187
+ expect(await screen.findByText('unmounted')).toBeInTheDocument();
188
+ expect(store.getSubscribeGraph()).toHaveLength(0);
189
+ });