@solid-primitives/deep 0.2.10 → 0.3.1
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/README.md +0 -1
- package/dist/index.d.ts +3 -80
- package/dist/index.js +3 -143
- package/dist/store-updates.d.ts +33 -0
- package/dist/store-updates.js +109 -0
- package/dist/track-deep.d.ts +25 -0
- package/dist/track-deep.js +44 -0
- package/dist/track-store.d.ts +21 -0
- package/dist/track-store.js +75 -0
- package/package.json +3 -7
- package/dist/index.cjs +0 -147
- package/dist/index.d.cts +0 -80
package/README.md
CHANGED
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
# @solid-primitives/deep
|
|
6
6
|
|
|
7
|
-
[](https://turborepo.org/)
|
|
8
7
|
[](https://bundlephobia.com/package/@solid-primitives/deep)
|
|
9
8
|
[](https://www.npmjs.com/package/@solid-primitives/deep)
|
|
10
9
|
[](https://github.com/solidjs-community/solid-primitives#contribution-process)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,80 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Tracks all properties of a {@link store} by iterating over them recursively.
|
|
5
|
-
*
|
|
6
|
-
* @param store reactive store dependency
|
|
7
|
-
* @returns same {@link store} that was passed in
|
|
8
|
-
*
|
|
9
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackDeep
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* createEffect(on(
|
|
14
|
-
* () => trackDeep(store),
|
|
15
|
-
* () => {
|
|
16
|
-
* // this effect will run when any property of store changes
|
|
17
|
-
* }
|
|
18
|
-
* ));
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
declare function trackDeep<T extends Store<object>>(store: T): T;
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated Renamed to {@link trackDeep}
|
|
24
|
-
*/
|
|
25
|
-
declare const deepTrack: typeof trackDeep;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Tracks all nested changes to passed {@link store}.
|
|
29
|
-
*
|
|
30
|
-
* @param store a reactive store proxy
|
|
31
|
-
* @returns same {@link store} that was passed in
|
|
32
|
-
*
|
|
33
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackStore
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* createEffect(on(
|
|
38
|
-
* () => trackStore(store),
|
|
39
|
-
* () => {
|
|
40
|
-
* // this effect will run when any property of store changes
|
|
41
|
-
* }
|
|
42
|
-
* ));
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
declare function trackStore<T extends object>(store: Store<T>): T;
|
|
46
|
-
|
|
47
|
-
type Static<T = unknown> = {
|
|
48
|
-
[K in number | string]: T;
|
|
49
|
-
} | T[];
|
|
50
|
-
type AllNestedObjects<T> = T extends Static<infer U> ? AllNestedObjects<U> | T : never;
|
|
51
|
-
type NestedUpdate<T> = {
|
|
52
|
-
path: (string | number)[];
|
|
53
|
-
value: AllNestedObjects<T>;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Creates a function for tracking and capturing updates to a {@link store}.
|
|
57
|
-
*
|
|
58
|
-
* Each execution of the returned function will return an array of updates to the store since the last execution.
|
|
59
|
-
*
|
|
60
|
-
* @param store - The store to track.
|
|
61
|
-
* @returns A function that returns an array of updates to the store since the last execution.
|
|
62
|
-
*
|
|
63
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#captureStoreUpdates
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```ts
|
|
67
|
-
* const [state, setState] = createStore({ todos: [] });
|
|
68
|
-
*
|
|
69
|
-
* const getDelta = captureStoreUpdates(state);
|
|
70
|
-
*
|
|
71
|
-
* getDelta(); // [{ path: [], value: { todos: [] } }]
|
|
72
|
-
*
|
|
73
|
-
* setState("todos", ["foo"]);
|
|
74
|
-
*
|
|
75
|
-
* getDelta(); // [{ path: ["todos"], value: ["foo"] }]
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
declare function captureStoreUpdates<T extends Static>(store: T): () => NestedUpdate<T>[];
|
|
79
|
-
|
|
80
|
-
export { type AllNestedObjects, type NestedUpdate, captureStoreUpdates, deepTrack, trackDeep, trackStore };
|
|
1
|
+
export * from "./track-deep.js";
|
|
2
|
+
export * from "./track-store.js";
|
|
3
|
+
export * from "./store-updates.js";
|
package/dist/index.js
CHANGED
|
@@ -1,143 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { isServer, isDev } from 'solid-js/web';
|
|
5
|
-
|
|
6
|
-
// src/track-deep.ts
|
|
7
|
-
function trackDeep(store) {
|
|
8
|
-
traverse(store, /* @__PURE__ */ new Set());
|
|
9
|
-
return store;
|
|
10
|
-
}
|
|
11
|
-
function traverse(value, seen) {
|
|
12
|
-
let isArray;
|
|
13
|
-
let proto;
|
|
14
|
-
if (value != null && typeof value === "object" && !seen.has(value) && ((isArray = Array.isArray(value)) || value[$PROXY] || !(proto = Object.getPrototypeOf(value)) || proto === Object.prototype)) {
|
|
15
|
-
seen.add(value);
|
|
16
|
-
for (const child of isArray ? value : Object.values(value)) traverse(child, seen);
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
var deepTrack = trackDeep;
|
|
20
|
-
var EQUALS_FALSE = { equals: false };
|
|
21
|
-
var TrackStoreCache = /* @__PURE__ */ new WeakMap();
|
|
22
|
-
var TrackVersion = 0;
|
|
23
|
-
function getTrackStoreNode(node) {
|
|
24
|
-
let track = TrackStoreCache.get(node);
|
|
25
|
-
if (!track) {
|
|
26
|
-
createRoot(() => {
|
|
27
|
-
const unwrapped = unwrap(node);
|
|
28
|
-
let is_reading = false;
|
|
29
|
-
let is_stale = true;
|
|
30
|
-
let version = 0;
|
|
31
|
-
const [signal, trigger] = createSignal(void 0, EQUALS_FALSE);
|
|
32
|
-
const memo = createMemo(
|
|
33
|
-
() => {
|
|
34
|
-
if (is_reading) {
|
|
35
|
-
node[$TRACK];
|
|
36
|
-
for (const [key, child] of Object.entries(unwrapped)) {
|
|
37
|
-
let childNode;
|
|
38
|
-
if (child != null && typeof child === "object" && ((childNode = child[$PROXY]) || $TRACK in (childNode = untrack(() => node[key])))) {
|
|
39
|
-
getTrackStoreNode(childNode)?.();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
} else {
|
|
43
|
-
signal();
|
|
44
|
-
is_stale = true;
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
void 0,
|
|
48
|
-
EQUALS_FALSE
|
|
49
|
-
);
|
|
50
|
-
track = () => {
|
|
51
|
-
is_reading = true;
|
|
52
|
-
if (is_stale) {
|
|
53
|
-
trigger();
|
|
54
|
-
is_stale = false;
|
|
55
|
-
}
|
|
56
|
-
const already_tracked = version === TrackVersion;
|
|
57
|
-
version = TrackVersion;
|
|
58
|
-
already_tracked || memo();
|
|
59
|
-
is_reading = false;
|
|
60
|
-
};
|
|
61
|
-
TrackStoreCache.set(node, track);
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
return track;
|
|
65
|
-
}
|
|
66
|
-
function trackStore(store) {
|
|
67
|
-
TrackVersion++;
|
|
68
|
-
$TRACK in store && getTrackStoreNode(store)?.();
|
|
69
|
-
return store;
|
|
70
|
-
}
|
|
71
|
-
function* entries(obj) {
|
|
72
|
-
if (Array.isArray(obj)) {
|
|
73
|
-
for (let i = 0; i < obj.length; i++) {
|
|
74
|
-
yield [i, obj[i]];
|
|
75
|
-
}
|
|
76
|
-
} else {
|
|
77
|
-
yield* Object.entries(obj)[Symbol.iterator]();
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
var StoreNodeChildrenCache = /* @__PURE__ */ new WeakMap();
|
|
81
|
-
function getStoreNodechildren(node) {
|
|
82
|
-
let signal = StoreNodeChildrenCache.get(node);
|
|
83
|
-
if (!signal) {
|
|
84
|
-
const unwrapped = unwrap(node), isArray = Array.isArray(unwrapped);
|
|
85
|
-
signal = createRoot(
|
|
86
|
-
() => createLazyMemo(() => {
|
|
87
|
-
node[$TRACK];
|
|
88
|
-
const children = isArray ? [] : {};
|
|
89
|
-
for (const [key, child] of entries(unwrapped)) {
|
|
90
|
-
let childNode;
|
|
91
|
-
if (child != null && typeof child === "object" && ((childNode = child[$PROXY]) || (childNode = untrack(() => node[key])) && $TRACK in childNode)) {
|
|
92
|
-
children[key] = childNode;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return children;
|
|
96
|
-
})
|
|
97
|
-
);
|
|
98
|
-
StoreNodeChildrenCache.set(node, signal);
|
|
99
|
-
}
|
|
100
|
-
return signal();
|
|
101
|
-
}
|
|
102
|
-
var CurrentUpdates;
|
|
103
|
-
var SeenNodes;
|
|
104
|
-
function newCacheNode(children) {
|
|
105
|
-
const record = { ...children };
|
|
106
|
-
for (const [key, node] of entries(children)) {
|
|
107
|
-
if (SeenNodes.has(node)) continue;
|
|
108
|
-
SeenNodes.add(node);
|
|
109
|
-
record[key] = newCacheNode(getStoreNodechildren(node));
|
|
110
|
-
}
|
|
111
|
-
return { children, record };
|
|
112
|
-
}
|
|
113
|
-
function compareStoreWithCache(node, parent, key, path) {
|
|
114
|
-
if (SeenNodes.has(node)) return;
|
|
115
|
-
SeenNodes.add(node);
|
|
116
|
-
const cacheNode = parent[key], children = getStoreNodechildren(node);
|
|
117
|
-
if (cacheNode && children === cacheNode.children) {
|
|
118
|
-
for (const [key2, child] of entries(children)) {
|
|
119
|
-
compareStoreWithCache(child, cacheNode.record, key2, [...path, key2]);
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
parent[key] = newCacheNode(children);
|
|
123
|
-
CurrentUpdates.push({ path, value: node });
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function captureStoreUpdates(store) {
|
|
127
|
-
if (isServer || !($TRACK in store)) {
|
|
128
|
-
if (isDev) {
|
|
129
|
-
console.warn("createStoreDelta expects a store, got", store);
|
|
130
|
-
}
|
|
131
|
-
let init = true;
|
|
132
|
-
return () => init ? (init = false, [{ path: [], value: store }]) : [];
|
|
133
|
-
}
|
|
134
|
-
const cache = {};
|
|
135
|
-
return () => {
|
|
136
|
-
CurrentUpdates = [];
|
|
137
|
-
SeenNodes = /* @__PURE__ */ new WeakSet();
|
|
138
|
-
compareStoreWithCache(store, cache, "root", []);
|
|
139
|
-
return CurrentUpdates;
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
export { captureStoreUpdates, deepTrack, trackDeep, trackStore };
|
|
1
|
+
export * from "./track-deep.js";
|
|
2
|
+
export * from "./track-store.js";
|
|
3
|
+
export * from "./store-updates.js";
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
type Static<T = unknown> = {
|
|
2
|
+
[K in number | string]: T;
|
|
3
|
+
} | T[];
|
|
4
|
+
export type AllNestedObjects<T> = T extends Static<infer U> ? AllNestedObjects<U> | T : never;
|
|
5
|
+
export type NestedUpdate<T> = {
|
|
6
|
+
path: (string | number)[];
|
|
7
|
+
value: AllNestedObjects<T>;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Creates a function for tracking and capturing updates to a {@link store}.
|
|
11
|
+
*
|
|
12
|
+
* Each execution of the returned function will return an array of updates to the store since the last execution.
|
|
13
|
+
*
|
|
14
|
+
* @param store - The store to track.
|
|
15
|
+
* @returns A function that returns an array of updates to the store since the last execution.
|
|
16
|
+
*
|
|
17
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#captureStoreUpdates
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* const [state, setState] = createStore({ todos: [] });
|
|
22
|
+
*
|
|
23
|
+
* const getDelta = captureStoreUpdates(state);
|
|
24
|
+
*
|
|
25
|
+
* getDelta(); // [{ path: [], value: { todos: [] } }]
|
|
26
|
+
*
|
|
27
|
+
* setState("todos", ["foo"]);
|
|
28
|
+
*
|
|
29
|
+
* getDelta(); // [{ path: ["todos"], value: ["foo"] }]
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export declare function captureStoreUpdates<T extends Static>(store: T): () => NestedUpdate<T>[];
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { createLazyMemo } from "@solid-primitives/memo";
|
|
2
|
+
import { $PROXY, $TRACK, createRoot, untrack } from "solid-js";
|
|
3
|
+
import { unwrap } from "solid-js/store";
|
|
4
|
+
import { isDev, isServer } from "solid-js/web";
|
|
5
|
+
function* entries(obj) {
|
|
6
|
+
if (Array.isArray(obj)) {
|
|
7
|
+
for (let i = 0; i < obj.length; i++) {
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
yield [i, obj[i]];
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
// @ts-ignore
|
|
14
|
+
yield* Object.entries(obj)[Symbol.iterator]();
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const StoreNodeChildrenCache = new WeakMap();
|
|
18
|
+
function getStoreNodechildren(node) {
|
|
19
|
+
let signal = StoreNodeChildrenCache.get(node);
|
|
20
|
+
if (!signal) {
|
|
21
|
+
const unwrapped = unwrap(node), isArray = Array.isArray(unwrapped);
|
|
22
|
+
signal = createRoot(() => createLazyMemo(() => {
|
|
23
|
+
node[$TRACK];
|
|
24
|
+
const children = isArray ? [] : {};
|
|
25
|
+
for (const [key, child] of entries(unwrapped)) {
|
|
26
|
+
let childNode;
|
|
27
|
+
if (child != null &&
|
|
28
|
+
typeof child === "object" &&
|
|
29
|
+
((childNode = child[$PROXY]) ||
|
|
30
|
+
((childNode = untrack(() => node[key])) && $TRACK in childNode))) {
|
|
31
|
+
children[key] = childNode;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return children;
|
|
35
|
+
}));
|
|
36
|
+
StoreNodeChildrenCache.set(node, signal);
|
|
37
|
+
}
|
|
38
|
+
return signal();
|
|
39
|
+
}
|
|
40
|
+
let CurrentUpdates;
|
|
41
|
+
let SeenNodes;
|
|
42
|
+
function newCacheNode(children) {
|
|
43
|
+
const record = { ...children };
|
|
44
|
+
for (const [key, node] of entries(children)) {
|
|
45
|
+
if (SeenNodes.has(node))
|
|
46
|
+
continue;
|
|
47
|
+
SeenNodes.add(node);
|
|
48
|
+
record[key] = newCacheNode(getStoreNodechildren(node));
|
|
49
|
+
}
|
|
50
|
+
return { children, record };
|
|
51
|
+
}
|
|
52
|
+
function compareStoreWithCache(node, parent, key, path) {
|
|
53
|
+
if (SeenNodes.has(node))
|
|
54
|
+
return;
|
|
55
|
+
SeenNodes.add(node);
|
|
56
|
+
const cacheNode = parent[key], children = getStoreNodechildren(node);
|
|
57
|
+
if (cacheNode && children === cacheNode.children) {
|
|
58
|
+
for (const [key, child] of entries(children)) {
|
|
59
|
+
compareStoreWithCache(child, cacheNode.record, key, [...path, key]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
parent[key] = newCacheNode(children);
|
|
64
|
+
CurrentUpdates.push({ path, value: node });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a function for tracking and capturing updates to a {@link store}.
|
|
69
|
+
*
|
|
70
|
+
* Each execution of the returned function will return an array of updates to the store since the last execution.
|
|
71
|
+
*
|
|
72
|
+
* @param store - The store to track.
|
|
73
|
+
* @returns A function that returns an array of updates to the store since the last execution.
|
|
74
|
+
*
|
|
75
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#captureStoreUpdates
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const [state, setState] = createStore({ todos: [] });
|
|
80
|
+
*
|
|
81
|
+
* const getDelta = captureStoreUpdates(state);
|
|
82
|
+
*
|
|
83
|
+
* getDelta(); // [{ path: [], value: { todos: [] } }]
|
|
84
|
+
*
|
|
85
|
+
* setState("todos", ["foo"]);
|
|
86
|
+
*
|
|
87
|
+
* getDelta(); // [{ path: ["todos"], value: ["foo"] }]
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export function captureStoreUpdates(store) {
|
|
91
|
+
// on the server you cannot check if the passed object is a store
|
|
92
|
+
// so we just return the whole store always
|
|
93
|
+
if (isServer || !($TRACK in store)) {
|
|
94
|
+
if (isDev) {
|
|
95
|
+
// eslint-disable-next-line no-console
|
|
96
|
+
console.warn("createStoreDelta expects a store, got", store);
|
|
97
|
+
}
|
|
98
|
+
let init = true;
|
|
99
|
+
return () => (init ? ((init = false), [{ path: [], value: store }]) : []);
|
|
100
|
+
}
|
|
101
|
+
const cache = {};
|
|
102
|
+
return () => {
|
|
103
|
+
// set globals before each cycle
|
|
104
|
+
CurrentUpdates = [];
|
|
105
|
+
SeenNodes = new WeakSet();
|
|
106
|
+
compareStoreWithCache(store, cache, "root", []);
|
|
107
|
+
return CurrentUpdates;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Store } from "solid-js/store";
|
|
2
|
+
/**
|
|
3
|
+
* Tracks all properties of a {@link store} by iterating over them recursively.
|
|
4
|
+
*
|
|
5
|
+
* @param store reactive store dependency
|
|
6
|
+
* @returns same {@link store} that was passed in
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackDeep
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* createEffect(on(
|
|
13
|
+
* () => trackDeep(store),
|
|
14
|
+
* () => {
|
|
15
|
+
* // this effect will run when any property of store changes
|
|
16
|
+
* }
|
|
17
|
+
* ));
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function trackDeep<T extends Store<object>>(store: T): T;
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Renamed to {@link trackDeep}
|
|
23
|
+
*/
|
|
24
|
+
declare const deepTrack: typeof trackDeep;
|
|
25
|
+
export { trackDeep, deepTrack };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { $PROXY } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* Tracks all properties of a {@link store} by iterating over them recursively.
|
|
4
|
+
*
|
|
5
|
+
* @param store reactive store dependency
|
|
6
|
+
* @returns same {@link store} that was passed in
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackDeep
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* createEffect(on(
|
|
13
|
+
* () => trackDeep(store),
|
|
14
|
+
* () => {
|
|
15
|
+
* // this effect will run when any property of store changes
|
|
16
|
+
* }
|
|
17
|
+
* ));
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function trackDeep(store) {
|
|
21
|
+
traverse(store, new Set());
|
|
22
|
+
return store;
|
|
23
|
+
}
|
|
24
|
+
function traverse(value, seen) {
|
|
25
|
+
let isArray;
|
|
26
|
+
let proto;
|
|
27
|
+
// check the same conditions as in `isWrappable` from `/packages/solid/store/src/store.ts`
|
|
28
|
+
if (value != null &&
|
|
29
|
+
typeof value === "object" &&
|
|
30
|
+
!seen.has(value) &&
|
|
31
|
+
((isArray = Array.isArray(value)) ||
|
|
32
|
+
value[$PROXY] ||
|
|
33
|
+
!(proto = Object.getPrototypeOf(value)) ||
|
|
34
|
+
proto === Object.prototype)) {
|
|
35
|
+
seen.add(value);
|
|
36
|
+
for (const child of isArray ? value : Object.values(value))
|
|
37
|
+
traverse(child, seen);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @deprecated Renamed to {@link trackDeep}
|
|
42
|
+
*/
|
|
43
|
+
const deepTrack = trackDeep;
|
|
44
|
+
export { trackDeep, deepTrack };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Store } from "solid-js/store";
|
|
2
|
+
/**
|
|
3
|
+
* Tracks all nested changes to passed {@link store}.
|
|
4
|
+
*
|
|
5
|
+
* @param store a reactive store proxy
|
|
6
|
+
* @returns same {@link store} that was passed in
|
|
7
|
+
*
|
|
8
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackStore
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* createEffect(on(
|
|
13
|
+
* () => trackStore(store),
|
|
14
|
+
* () => {
|
|
15
|
+
* // this effect will run when any property of store changes
|
|
16
|
+
* }
|
|
17
|
+
* ));
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare function trackStore<T extends object>(store: Store<T>): T;
|
|
21
|
+
export { trackStore };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { $PROXY, $TRACK, createMemo, createRoot, createSignal, untrack } from "solid-js";
|
|
2
|
+
import { unwrap } from "solid-js/store";
|
|
3
|
+
const EQUALS_FALSE = { equals: false };
|
|
4
|
+
const TrackStoreCache = new WeakMap();
|
|
5
|
+
// for preventing the same object to be visited multiple times in the same trackStore call
|
|
6
|
+
let TrackVersion = 0;
|
|
7
|
+
function getTrackStoreNode(node) {
|
|
8
|
+
let track = TrackStoreCache.get(node);
|
|
9
|
+
if (!track) {
|
|
10
|
+
createRoot(() => {
|
|
11
|
+
const unwrapped = unwrap(node);
|
|
12
|
+
// custom lazy memo to support circular references
|
|
13
|
+
// maybe it won't be needed in solid 2.0
|
|
14
|
+
let is_reading = false;
|
|
15
|
+
let is_stale = true;
|
|
16
|
+
let version = 0;
|
|
17
|
+
const [signal, trigger] = createSignal(undefined, EQUALS_FALSE);
|
|
18
|
+
const memo = createMemo(() => {
|
|
19
|
+
if (is_reading) {
|
|
20
|
+
node[$TRACK]; // shallow track store node
|
|
21
|
+
// track each child node
|
|
22
|
+
for (const [key, child] of Object.entries(unwrapped)) {
|
|
23
|
+
let childNode;
|
|
24
|
+
if (child != null &&
|
|
25
|
+
typeof child === "object" &&
|
|
26
|
+
((childNode = child[$PROXY]) || $TRACK in (childNode = untrack(() => node[key])))) {
|
|
27
|
+
getTrackStoreNode(childNode)?.();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
signal();
|
|
33
|
+
is_stale = true;
|
|
34
|
+
}
|
|
35
|
+
}, undefined, EQUALS_FALSE);
|
|
36
|
+
track = () => {
|
|
37
|
+
is_reading = true;
|
|
38
|
+
if (is_stale) {
|
|
39
|
+
trigger();
|
|
40
|
+
is_stale = false;
|
|
41
|
+
}
|
|
42
|
+
const already_tracked = version === TrackVersion;
|
|
43
|
+
version = TrackVersion;
|
|
44
|
+
already_tracked || memo();
|
|
45
|
+
is_reading = false;
|
|
46
|
+
};
|
|
47
|
+
TrackStoreCache.set(node, track);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return track;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Tracks all nested changes to passed {@link store}.
|
|
54
|
+
*
|
|
55
|
+
* @param store a reactive store proxy
|
|
56
|
+
* @returns same {@link store} that was passed in
|
|
57
|
+
*
|
|
58
|
+
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackStore
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* createEffect(on(
|
|
63
|
+
* () => trackStore(store),
|
|
64
|
+
* () => {
|
|
65
|
+
* // this effect will run when any property of store changes
|
|
66
|
+
* }
|
|
67
|
+
* ));
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
function trackStore(store) {
|
|
71
|
+
TrackVersion++;
|
|
72
|
+
$TRACK in store && getTrackStoreNode(store)?.();
|
|
73
|
+
return store;
|
|
74
|
+
}
|
|
75
|
+
export { trackStore };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solid-primitives/deep",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Primitives for tracking and observing nested reactive objects in Solid.",
|
|
5
5
|
"author": "Samuel Burbano <me@iosamuel.dev>",
|
|
6
6
|
"contributors": [
|
|
@@ -39,23 +39,19 @@
|
|
|
39
39
|
"dist"
|
|
40
40
|
],
|
|
41
41
|
"type": "module",
|
|
42
|
-
"main": "./dist/index.cjs",
|
|
43
42
|
"module": "./dist/index.js",
|
|
44
43
|
"browser": {},
|
|
45
44
|
"types": "./dist/index.d.ts",
|
|
46
45
|
"exports": {
|
|
46
|
+
"@solid-primitives/source": "./src/index.ts",
|
|
47
47
|
"import": {
|
|
48
48
|
"types": "./dist/index.d.ts",
|
|
49
49
|
"default": "./dist/index.js"
|
|
50
|
-
},
|
|
51
|
-
"require": {
|
|
52
|
-
"types": "./dist/index.d.cts",
|
|
53
|
-
"default": "./dist/index.cjs"
|
|
54
50
|
}
|
|
55
51
|
},
|
|
56
52
|
"typesVersions": {},
|
|
57
53
|
"dependencies": {
|
|
58
|
-
"@solid-primitives/memo": "^1.
|
|
54
|
+
"@solid-primitives/memo": "^1.4.1"
|
|
59
55
|
},
|
|
60
56
|
"peerDependencies": {
|
|
61
57
|
"solid-js": "^1.6.12"
|
package/dist/index.cjs
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var solidJs = require('solid-js');
|
|
4
|
-
var store = require('solid-js/store');
|
|
5
|
-
var memo = require('@solid-primitives/memo');
|
|
6
|
-
var web = require('solid-js/web');
|
|
7
|
-
|
|
8
|
-
// src/track-deep.ts
|
|
9
|
-
function trackDeep(store) {
|
|
10
|
-
traverse(store, /* @__PURE__ */ new Set());
|
|
11
|
-
return store;
|
|
12
|
-
}
|
|
13
|
-
function traverse(value, seen) {
|
|
14
|
-
let isArray;
|
|
15
|
-
let proto;
|
|
16
|
-
if (value != null && typeof value === "object" && !seen.has(value) && ((isArray = Array.isArray(value)) || value[solidJs.$PROXY] || !(proto = Object.getPrototypeOf(value)) || proto === Object.prototype)) {
|
|
17
|
-
seen.add(value);
|
|
18
|
-
for (const child of isArray ? value : Object.values(value)) traverse(child, seen);
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
exports.deepTrack = trackDeep;
|
|
22
|
-
var EQUALS_FALSE = { equals: false };
|
|
23
|
-
var TrackStoreCache = /* @__PURE__ */ new WeakMap();
|
|
24
|
-
var TrackVersion = 0;
|
|
25
|
-
function getTrackStoreNode(node) {
|
|
26
|
-
let track = TrackStoreCache.get(node);
|
|
27
|
-
if (!track) {
|
|
28
|
-
solidJs.createRoot(() => {
|
|
29
|
-
const unwrapped = store.unwrap(node);
|
|
30
|
-
let is_reading = false;
|
|
31
|
-
let is_stale = true;
|
|
32
|
-
let version = 0;
|
|
33
|
-
const [signal, trigger] = solidJs.createSignal(void 0, EQUALS_FALSE);
|
|
34
|
-
const memo = solidJs.createMemo(
|
|
35
|
-
() => {
|
|
36
|
-
if (is_reading) {
|
|
37
|
-
node[solidJs.$TRACK];
|
|
38
|
-
for (const [key, child] of Object.entries(unwrapped)) {
|
|
39
|
-
let childNode;
|
|
40
|
-
if (child != null && typeof child === "object" && ((childNode = child[solidJs.$PROXY]) || solidJs.$TRACK in (childNode = solidJs.untrack(() => node[key])))) {
|
|
41
|
-
getTrackStoreNode(childNode)?.();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
} else {
|
|
45
|
-
signal();
|
|
46
|
-
is_stale = true;
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
void 0,
|
|
50
|
-
EQUALS_FALSE
|
|
51
|
-
);
|
|
52
|
-
track = () => {
|
|
53
|
-
is_reading = true;
|
|
54
|
-
if (is_stale) {
|
|
55
|
-
trigger();
|
|
56
|
-
is_stale = false;
|
|
57
|
-
}
|
|
58
|
-
const already_tracked = version === TrackVersion;
|
|
59
|
-
version = TrackVersion;
|
|
60
|
-
already_tracked || memo();
|
|
61
|
-
is_reading = false;
|
|
62
|
-
};
|
|
63
|
-
TrackStoreCache.set(node, track);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
return track;
|
|
67
|
-
}
|
|
68
|
-
function trackStore(store) {
|
|
69
|
-
TrackVersion++;
|
|
70
|
-
solidJs.$TRACK in store && getTrackStoreNode(store)?.();
|
|
71
|
-
return store;
|
|
72
|
-
}
|
|
73
|
-
function* entries(obj) {
|
|
74
|
-
if (Array.isArray(obj)) {
|
|
75
|
-
for (let i = 0; i < obj.length; i++) {
|
|
76
|
-
yield [i, obj[i]];
|
|
77
|
-
}
|
|
78
|
-
} else {
|
|
79
|
-
yield* Object.entries(obj)[Symbol.iterator]();
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
var StoreNodeChildrenCache = /* @__PURE__ */ new WeakMap();
|
|
83
|
-
function getStoreNodechildren(node) {
|
|
84
|
-
let signal = StoreNodeChildrenCache.get(node);
|
|
85
|
-
if (!signal) {
|
|
86
|
-
const unwrapped = store.unwrap(node), isArray = Array.isArray(unwrapped);
|
|
87
|
-
signal = solidJs.createRoot(
|
|
88
|
-
() => memo.createLazyMemo(() => {
|
|
89
|
-
node[solidJs.$TRACK];
|
|
90
|
-
const children = isArray ? [] : {};
|
|
91
|
-
for (const [key, child] of entries(unwrapped)) {
|
|
92
|
-
let childNode;
|
|
93
|
-
if (child != null && typeof child === "object" && ((childNode = child[solidJs.$PROXY]) || (childNode = solidJs.untrack(() => node[key])) && solidJs.$TRACK in childNode)) {
|
|
94
|
-
children[key] = childNode;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return children;
|
|
98
|
-
})
|
|
99
|
-
);
|
|
100
|
-
StoreNodeChildrenCache.set(node, signal);
|
|
101
|
-
}
|
|
102
|
-
return signal();
|
|
103
|
-
}
|
|
104
|
-
var CurrentUpdates;
|
|
105
|
-
var SeenNodes;
|
|
106
|
-
function newCacheNode(children) {
|
|
107
|
-
const record = { ...children };
|
|
108
|
-
for (const [key, node] of entries(children)) {
|
|
109
|
-
if (SeenNodes.has(node)) continue;
|
|
110
|
-
SeenNodes.add(node);
|
|
111
|
-
record[key] = newCacheNode(getStoreNodechildren(node));
|
|
112
|
-
}
|
|
113
|
-
return { children, record };
|
|
114
|
-
}
|
|
115
|
-
function compareStoreWithCache(node, parent, key, path) {
|
|
116
|
-
if (SeenNodes.has(node)) return;
|
|
117
|
-
SeenNodes.add(node);
|
|
118
|
-
const cacheNode = parent[key], children = getStoreNodechildren(node);
|
|
119
|
-
if (cacheNode && children === cacheNode.children) {
|
|
120
|
-
for (const [key2, child] of entries(children)) {
|
|
121
|
-
compareStoreWithCache(child, cacheNode.record, key2, [...path, key2]);
|
|
122
|
-
}
|
|
123
|
-
} else {
|
|
124
|
-
parent[key] = newCacheNode(children);
|
|
125
|
-
CurrentUpdates.push({ path, value: node });
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
function captureStoreUpdates(store) {
|
|
129
|
-
if (web.isServer || !(solidJs.$TRACK in store)) {
|
|
130
|
-
if (web.isDev) {
|
|
131
|
-
console.warn("createStoreDelta expects a store, got", store);
|
|
132
|
-
}
|
|
133
|
-
let init = true;
|
|
134
|
-
return () => init ? (init = false, [{ path: [], value: store }]) : [];
|
|
135
|
-
}
|
|
136
|
-
const cache = {};
|
|
137
|
-
return () => {
|
|
138
|
-
CurrentUpdates = [];
|
|
139
|
-
SeenNodes = /* @__PURE__ */ new WeakSet();
|
|
140
|
-
compareStoreWithCache(store, cache, "root", []);
|
|
141
|
-
return CurrentUpdates;
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
exports.captureStoreUpdates = captureStoreUpdates;
|
|
146
|
-
exports.trackDeep = trackDeep;
|
|
147
|
-
exports.trackStore = trackStore;
|
package/dist/index.d.cts
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { Store } from 'solid-js/store';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tracks all properties of a {@link store} by iterating over them recursively.
|
|
5
|
-
*
|
|
6
|
-
* @param store reactive store dependency
|
|
7
|
-
* @returns same {@link store} that was passed in
|
|
8
|
-
*
|
|
9
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackDeep
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* createEffect(on(
|
|
14
|
-
* () => trackDeep(store),
|
|
15
|
-
* () => {
|
|
16
|
-
* // this effect will run when any property of store changes
|
|
17
|
-
* }
|
|
18
|
-
* ));
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
declare function trackDeep<T extends Store<object>>(store: T): T;
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated Renamed to {@link trackDeep}
|
|
24
|
-
*/
|
|
25
|
-
declare const deepTrack: typeof trackDeep;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Tracks all nested changes to passed {@link store}.
|
|
29
|
-
*
|
|
30
|
-
* @param store a reactive store proxy
|
|
31
|
-
* @returns same {@link store} that was passed in
|
|
32
|
-
*
|
|
33
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#trackStore
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* createEffect(on(
|
|
38
|
-
* () => trackStore(store),
|
|
39
|
-
* () => {
|
|
40
|
-
* // this effect will run when any property of store changes
|
|
41
|
-
* }
|
|
42
|
-
* ));
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
declare function trackStore<T extends object>(store: Store<T>): T;
|
|
46
|
-
|
|
47
|
-
type Static<T = unknown> = {
|
|
48
|
-
[K in number | string]: T;
|
|
49
|
-
} | T[];
|
|
50
|
-
type AllNestedObjects<T> = T extends Static<infer U> ? AllNestedObjects<U> | T : never;
|
|
51
|
-
type NestedUpdate<T> = {
|
|
52
|
-
path: (string | number)[];
|
|
53
|
-
value: AllNestedObjects<T>;
|
|
54
|
-
};
|
|
55
|
-
/**
|
|
56
|
-
* Creates a function for tracking and capturing updates to a {@link store}.
|
|
57
|
-
*
|
|
58
|
-
* Each execution of the returned function will return an array of updates to the store since the last execution.
|
|
59
|
-
*
|
|
60
|
-
* @param store - The store to track.
|
|
61
|
-
* @returns A function that returns an array of updates to the store since the last execution.
|
|
62
|
-
*
|
|
63
|
-
* @see https://github.com/solidjs-community/solid-primitives/tree/main/packages/deep#captureStoreUpdates
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```ts
|
|
67
|
-
* const [state, setState] = createStore({ todos: [] });
|
|
68
|
-
*
|
|
69
|
-
* const getDelta = captureStoreUpdates(state);
|
|
70
|
-
*
|
|
71
|
-
* getDelta(); // [{ path: [], value: { todos: [] } }]
|
|
72
|
-
*
|
|
73
|
-
* setState("todos", ["foo"]);
|
|
74
|
-
*
|
|
75
|
-
* getDelta(); // [{ path: ["todos"], value: ["foo"] }]
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
declare function captureStoreUpdates<T extends Static>(store: T): () => NestedUpdate<T>[];
|
|
79
|
-
|
|
80
|
-
export { type AllNestedObjects, type NestedUpdate, captureStoreUpdates, deepTrack, trackDeep, trackStore };
|