@praxisjs/core 2.2.0 → 2.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/CHANGELOG.md +24 -0
- package/dist/component/internals.d.ts +12 -2
- package/dist/component/internals.d.ts.map +1 -1
- package/dist/component/internals.js +25 -7
- package/dist/component/internals.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -2
- package/dist/internal.js.map +1 -1
- package/dist/signal/batch.d.ts +1 -3
- package/dist/signal/batch.d.ts.map +1 -1
- package/dist/signal/batch.js +4 -30
- package/dist/signal/batch.js.map +1 -1
- package/dist/signal/computed.d.ts.map +1 -1
- package/dist/signal/computed.js +13 -66
- package/dist/signal/computed.js.map +1 -1
- package/dist/signal/effect.d.ts +6 -14
- package/dist/signal/effect.d.ts.map +1 -1
- package/dist/signal/effect.js +11 -83
- package/dist/signal/effect.js.map +1 -1
- package/dist/signal/graph.d.ts +111 -0
- package/dist/signal/graph.d.ts.map +1 -0
- package/dist/signal/graph.js +513 -0
- package/dist/signal/graph.js.map +1 -0
- package/dist/signal/index.d.ts +1 -0
- package/dist/signal/index.d.ts.map +1 -1
- package/dist/signal/index.js +1 -0
- package/dist/signal/index.js.map +1 -1
- package/dist/signal/peek.d.ts.map +1 -1
- package/dist/signal/peek.js +2 -9
- package/dist/signal/peek.js.map +1 -1
- package/dist/signal/selector.d.ts +5 -0
- package/dist/signal/selector.d.ts.map +1 -0
- package/dist/signal/selector.js +128 -0
- package/dist/signal/selector.js.map +1 -0
- package/dist/signal/signal.d.ts +0 -5
- package/dist/signal/signal.d.ts.map +1 -1
- package/dist/signal/signal.js +11 -70
- package/dist/signal/signal.js.map +1 -1
- package/package.json +13 -4
- package/src/component/internals.ts +35 -10
- package/src/internal.ts +26 -0
- package/src/signal/batch.ts +5 -33
- package/src/signal/computed.ts +13 -66
- package/src/signal/effect.ts +12 -92
- package/src/signal/graph.ts +562 -0
- package/src/signal/index.ts +1 -0
- package/src/signal/peek.ts +2 -8
- package/src/signal/selector.ts +140 -0
- package/src/signal/signal.ts +11 -74
- package/dist/__tests__/batch.test.d.ts +0 -2
- package/dist/__tests__/batch.test.d.ts.map +0 -1
- package/dist/__tests__/batch.test.js +0 -124
- package/dist/__tests__/batch.test.js.map +0 -1
- package/dist/__tests__/component.test.d.ts +0 -2
- package/dist/__tests__/component.test.d.ts.map +0 -1
- package/dist/__tests__/component.test.js +0 -124
- package/dist/__tests__/component.test.js.map +0 -1
- package/dist/__tests__/computed.test.d.ts +0 -2
- package/dist/__tests__/computed.test.d.ts.map +0 -1
- package/dist/__tests__/computed.test.js +0 -289
- package/dist/__tests__/computed.test.js.map +0 -1
- package/dist/__tests__/effect.test.d.ts +0 -2
- package/dist/__tests__/effect.test.d.ts.map +0 -1
- package/dist/__tests__/effect.test.js +0 -200
- package/dist/__tests__/effect.test.js.map +0 -1
- package/dist/__tests__/peek.test.d.ts +0 -2
- package/dist/__tests__/peek.test.d.ts.map +0 -1
- package/dist/__tests__/peek.test.js +0 -26
- package/dist/__tests__/peek.test.js.map +0 -1
- package/dist/__tests__/persisted-signal.test.d.ts +0 -2
- package/dist/__tests__/persisted-signal.test.d.ts.map +0 -1
- package/dist/__tests__/persisted-signal.test.js +0 -252
- package/dist/__tests__/persisted-signal.test.js.map +0 -1
- package/dist/__tests__/reactive.test.d.ts +0 -2
- package/dist/__tests__/reactive.test.d.ts.map +0 -1
- package/dist/__tests__/reactive.test.js +0 -299
- package/dist/__tests__/reactive.test.js.map +0 -1
- package/dist/__tests__/resource-focus.test.d.ts +0 -2
- package/dist/__tests__/resource-focus.test.d.ts.map +0 -1
- package/dist/__tests__/resource-focus.test.js +0 -50
- package/dist/__tests__/resource-focus.test.js.map +0 -1
- package/dist/__tests__/resource-server-pass.test.d.ts +0 -2
- package/dist/__tests__/resource-server-pass.test.d.ts.map +0 -1
- package/dist/__tests__/resource-server-pass.test.js +0 -78
- package/dist/__tests__/resource-server-pass.test.js.map +0 -1
- package/dist/__tests__/resource.test.d.ts +0 -2
- package/dist/__tests__/resource.test.d.ts.map +0 -1
- package/dist/__tests__/resource.test.js +0 -336
- package/dist/__tests__/resource.test.js.map +0 -1
- package/dist/__tests__/signal.test.d.ts +0 -2
- package/dist/__tests__/signal.test.d.ts.map +0 -1
- package/dist/__tests__/signal.test.js +0 -254
- package/dist/__tests__/signal.test.js.map +0 -1
- package/dist/__tests__/synced.test.d.ts +0 -2
- package/dist/__tests__/synced.test.d.ts.map +0 -1
- package/dist/__tests__/synced.test.js +0 -142
- package/dist/__tests__/synced.test.js.map +0 -1
- package/dist/__tests__/track.test.d.ts +0 -2
- package/dist/__tests__/track.test.d.ts.map +0 -1
- package/dist/__tests__/track.test.js +0 -54
- package/dist/__tests__/track.test.js.map +0 -1
- package/src/__tests__/batch.test.ts +0 -138
- package/src/__tests__/component.test.ts +0 -165
- package/src/__tests__/computed.test.ts +0 -333
- package/src/__tests__/effect.test.ts +0 -241
- package/src/__tests__/peek.test.ts +0 -31
- package/src/__tests__/persisted-signal.test.ts +0 -305
- package/src/__tests__/reactive.test.ts +0 -343
- package/src/__tests__/resource-focus.test.ts +0 -61
- package/src/__tests__/resource-server-pass.test.ts +0 -90
- package/src/__tests__/resource.test.ts +0 -421
- package/src/__tests__/signal.test.ts +0 -291
- package/src/__tests__/synced.test.ts +0 -173
- package/src/__tests__/track.test.ts +0 -61
- package/tsconfig.json +0 -8
package/dist/signal/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAqD,eAAe,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAqB,YAAY,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signal/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,EAAqD,eAAe,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAqB,YAAY,EAAE,MAAM,UAAU,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAsC,QAAQ,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peek.d.ts","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"peek.d.ts","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAIzD,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAE1D"}
|
package/dist/signal/peek.js
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { untrack } from "./effect";
|
|
2
2
|
export function peek(source) {
|
|
3
|
-
|
|
4
|
-
runEffect(null);
|
|
5
|
-
try {
|
|
6
|
-
return source();
|
|
7
|
-
}
|
|
8
|
-
finally {
|
|
9
|
-
runEffect(prev);
|
|
10
|
-
}
|
|
3
|
+
return untrack(source);
|
|
11
4
|
}
|
|
12
5
|
//# sourceMappingURL=peek.js.map
|
package/dist/signal/peek.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"peek.js","sourceRoot":"","sources":["../../src/signal/peek.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,MAAM,UAAU,IAAI,CAAI,MAA+B;IACrD,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type Selector<K> = (key: K) => boolean;
|
|
2
|
+
export type SelectorEquals<K, T> = (key: K, value: T) => boolean;
|
|
3
|
+
export declare function selector<T>(source: () => T): Selector<T>;
|
|
4
|
+
export declare function selector<T, K>(source: () => T, equals: SelectorEquals<K, T>): Selector<K>;
|
|
5
|
+
//# sourceMappingURL=selector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/signal/selector.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;AAC9C,MAAM,MAAM,cAAc,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,OAAO,CAAC;AAyCjE,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;AAC1D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { activeSub, createEffect, DIRTY, disposeEffect, endBatch, notify, PENDING, startBatch, track, WatchedNode } from "./graph";
|
|
2
|
+
// Keys live in a Map, so the default comparison has to be the Map's own (SameValueZero): a key
|
|
3
|
+
// that compares equal must be the one the lookup finds, or a bucket would never be notified.
|
|
4
|
+
function sameValueZero(a, b) {
|
|
5
|
+
return a === b || (Number.isNaN(a) && Number.isNaN(b));
|
|
6
|
+
}
|
|
7
|
+
// One class for every selector's buckets (rather than one declared per selector call), so the
|
|
8
|
+
// graph code that reads and links them always sees the same shape.
|
|
9
|
+
// Declared and assigned rather than parameter properties, which compile to class fields that are
|
|
10
|
+
// defined one by one and make buckets (one per row reading a selector) slower to create.
|
|
11
|
+
class Bucket extends WatchedNode {
|
|
12
|
+
constructor(key, owner) {
|
|
13
|
+
super();
|
|
14
|
+
this.key = key;
|
|
15
|
+
this.owner = owner;
|
|
16
|
+
}
|
|
17
|
+
// A bucket drops itself from its selector once its last subscriber unlinks (re-run or disposal).
|
|
18
|
+
unwatched() {
|
|
19
|
+
this.owner.drop(this.key);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Keeps the bucket shape alive across full GCs; see the shape keepers in graph.ts.
|
|
23
|
+
Object.defineProperty(Bucket, "shapeKeeper", {
|
|
24
|
+
value: new Bucket(undefined, {
|
|
25
|
+
drop() {
|
|
26
|
+
// Never called: the keeper has no subscriber to lose.
|
|
27
|
+
},
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
export function selector(source, equals) {
|
|
31
|
+
const compare = equals ?? sameValueZero;
|
|
32
|
+
const buckets = new Map();
|
|
33
|
+
let current;
|
|
34
|
+
let sourceEffect = null;
|
|
35
|
+
let releaseScheduled = false;
|
|
36
|
+
// The source subscription goes with the last bucket, so removed rows leave nothing behind.
|
|
37
|
+
const owner = {
|
|
38
|
+
drop(key) {
|
|
39
|
+
buckets.delete(key);
|
|
40
|
+
if (buckets.size === 0)
|
|
41
|
+
scheduleRelease();
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
function changedBuckets(prev, next) {
|
|
45
|
+
const changed = [];
|
|
46
|
+
if (equals === undefined) {
|
|
47
|
+
if (sameValueZero(prev, next))
|
|
48
|
+
return changed;
|
|
49
|
+
const prevBucket = buckets.get(prev);
|
|
50
|
+
const nextBucket = buckets.get(next);
|
|
51
|
+
if (prevBucket !== undefined)
|
|
52
|
+
changed.push(prevBucket);
|
|
53
|
+
if (nextBucket !== undefined)
|
|
54
|
+
changed.push(nextBucket);
|
|
55
|
+
return changed;
|
|
56
|
+
}
|
|
57
|
+
for (const [key, bucket] of buckets) {
|
|
58
|
+
if (compare(key, prev) !== compare(key, next))
|
|
59
|
+
changed.push(bucket);
|
|
60
|
+
}
|
|
61
|
+
return changed;
|
|
62
|
+
}
|
|
63
|
+
// Batched so an effect reading several affected keys re-runs once, and so no subscriber runs
|
|
64
|
+
// (and unlinks) while the buckets are still being walked.
|
|
65
|
+
function notifyChanged(prev, next) {
|
|
66
|
+
const changed = changedBuckets(prev, next);
|
|
67
|
+
if (changed.length === 0)
|
|
68
|
+
return;
|
|
69
|
+
startBatch();
|
|
70
|
+
try {
|
|
71
|
+
for (const bucket of changed) {
|
|
72
|
+
bucket.version++;
|
|
73
|
+
notify(bucket);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
endBatch();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function activate() {
|
|
81
|
+
let initialized = false;
|
|
82
|
+
sourceEffect = createEffect(() => {
|
|
83
|
+
const next = source();
|
|
84
|
+
const prev = current;
|
|
85
|
+
current = next;
|
|
86
|
+
if (!initialized) {
|
|
87
|
+
initialized = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
notifyChanged(prev, next);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
// An effect re-running unlinks its bucket right before reading the key again, and a list
|
|
94
|
+
// re-render unlinks every bucket before the new rows subscribe. Waiting a microtask keeps the
|
|
95
|
+
// source subscribed through both instead of stopping and re-reading it each time.
|
|
96
|
+
function scheduleRelease() {
|
|
97
|
+
if (releaseScheduled)
|
|
98
|
+
return;
|
|
99
|
+
releaseScheduled = true;
|
|
100
|
+
queueMicrotask(() => {
|
|
101
|
+
releaseScheduled = false;
|
|
102
|
+
if (buckets.size > 0)
|
|
103
|
+
return;
|
|
104
|
+
const stop = sourceEffect;
|
|
105
|
+
sourceEffect = null;
|
|
106
|
+
disposeEffect(stop);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
return function select(key) {
|
|
110
|
+
if (activeSub === undefined) {
|
|
111
|
+
// `current` lags the source while the source effect waits to run (a write inside a batch, or
|
|
112
|
+
// a computed source, whose effects run a microtask later). A tracked read keeps the cached
|
|
113
|
+
// value, which the effect's run then corrects through the buckets; nothing corrects this one.
|
|
114
|
+
const fresh = sourceEffect !== null && (sourceEffect.flags & (DIRTY | PENDING)) === 0;
|
|
115
|
+
return compare(key, fresh ? current : source());
|
|
116
|
+
}
|
|
117
|
+
if (sourceEffect === null)
|
|
118
|
+
activate();
|
|
119
|
+
let bucket = buckets.get(key);
|
|
120
|
+
if (bucket === undefined) {
|
|
121
|
+
bucket = new Bucket(key, owner);
|
|
122
|
+
buckets.set(key, bucket);
|
|
123
|
+
}
|
|
124
|
+
track(bucket);
|
|
125
|
+
return compare(key, current);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=selector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/signal/selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,EAAmB,MAAM,SAAS,CAAC;AAKpJ,+FAA+F;AAC/F,6FAA6F;AAC7F,SAAS,aAAa,CAAC,CAAU,EAAE,CAAU;IAC3C,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAMD,8FAA8F;AAC9F,mEAAmE;AACnE,iGAAiG;AACjG,yFAAyF;AACzF,MAAM,MAAU,SAAQ,WAAW;IAIjC,YAAY,GAAM,EAAE,KAAqB;QACvC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,iGAAiG;IACjG,SAAS;QACP,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;CACF;AAED,mFAAmF;AACnF,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE;IAC3C,KAAK,EAAE,IAAI,MAAM,CAAU,SAAS,EAAE;QACpC,IAAI;YACF,sDAAsD;QACxD,CAAC;KACF,CAAC;CACH,CAAC,CAAC;AAIH,MAAM,UAAU,QAAQ,CAAO,MAAe,EAAE,MAA6B;IAC3E,MAAM,OAAO,GAAG,MAAM,IAAK,aAAsC,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,IAAI,OAAU,CAAC;IACf,IAAI,YAAY,GAAsB,IAAI,CAAC;IAC3C,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAE7B,2FAA2F;IAC3F,MAAM,KAAK,GAAmB;QAC5B,IAAI,CAAC,GAAG;YACN,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACpB,IAAI,OAAO,CAAC,IAAI,KAAK,CAAC;gBAAE,eAAe,EAAE,CAAC;QAC5C,CAAC;KACF,CAAC;IAEF,SAAS,cAAc,CAAC,IAAO,EAAE,IAAO;QACtC,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,IAAI,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC;gBAAE,OAAO,OAAO,CAAC;YAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,IAAoB,CAAC,CAAC;YACrD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,IAAoB,CAAC,CAAC;YACrD,IAAI,UAAU,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACvD,IAAI,UAAU,KAAK,SAAS;gBAAE,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YACvD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;YACpC,IAAI,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,6FAA6F;IAC7F,0DAA0D;IAC1D,SAAS,aAAa,CAAC,IAAO,EAAE,IAAO;QACrC,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QACjC,UAAU,EAAE,CAAC;QACb,IAAI,CAAC;YACH,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,CAAC,MAAM,CAAC,CAAC;YACjB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,SAAS,QAAQ;QACf,IAAI,WAAW,GAAG,KAAK,CAAC;QACxB,YAAY,GAAG,YAAY,CAAC,GAAG,EAAE;YAC/B,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC;YACtB,MAAM,IAAI,GAAG,OAAO,CAAC;YACrB,OAAO,GAAG,IAAI,CAAC;YACf,IAAI,CAAC,WAAW,EAAE,CAAC;gBACjB,WAAW,GAAG,IAAI,CAAC;gBACnB,OAAO;YACT,CAAC;YACD,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,yFAAyF;IACzF,8FAA8F;IAC9F,kFAAkF;IAClF,SAAS,eAAe;QACtB,IAAI,gBAAgB;YAAE,OAAO;QAC7B,gBAAgB,GAAG,IAAI,CAAC;QACxB,cAAc,CAAC,GAAG,EAAE;YAClB,gBAAgB,GAAG,KAAK,CAAC;YACzB,IAAI,OAAO,CAAC,IAAI,GAAG,CAAC;gBAAE,OAAO;YAC7B,MAAM,IAAI,GAAG,YAA0B,CAAC;YACxC,YAAY,GAAG,IAAI,CAAC;YACpB,aAAa,CAAC,IAAI,CAAC,CAAC;QACtB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,MAAM,CAAC,GAAM;QAC3B,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,6FAA6F;YAC7F,2FAA2F;YAC3F,8FAA8F;YAC9F,MAAM,KAAK,GAAG,YAAY,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC,KAAK,GAAG,CAAC,KAAK,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;YACtF,OAAO,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,YAAY,KAAK,IAAI;YAAE,QAAQ,EAAE,CAAC;QACtC,IAAI,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,MAAM,GAAG,IAAI,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAChC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,MAAM,CAAC,CAAC;QACd,OAAO,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/B,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/signal/signal.d.ts
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
1
|
import type { Signal } from "@praxisjs/shared";
|
|
2
|
-
import { removeSub, type Effect, type SubList, type SubscriberHolder } from "./effect";
|
|
3
|
-
export type { SubList } from "./effect";
|
|
4
|
-
export declare function addSub(holder: SubscriberHolder, eff: Effect): void;
|
|
5
|
-
export { removeSub };
|
|
6
|
-
export declare function notifySubs(subs: Exclude<SubList, null>, batching: boolean): void;
|
|
7
2
|
export declare function signal<T>(initialValue: T): Signal<T>;
|
|
8
3
|
//# sourceMappingURL=signal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAK/C,wBAAgB,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAepD"}
|
package/dist/signal/signal.js
CHANGED
|
@@ -1,75 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export function addSub(holder, eff) {
|
|
4
|
-
const subs = holder.subs;
|
|
5
|
-
if (subs === null) {
|
|
6
|
-
holder.subs = eff;
|
|
7
|
-
}
|
|
8
|
-
else if (typeof subs === "function") {
|
|
9
|
-
if (subs !== eff)
|
|
10
|
-
holder.subs = [subs, eff];
|
|
11
|
-
}
|
|
12
|
-
else if (subs instanceof Set) {
|
|
13
|
-
subs.add(eff);
|
|
14
|
-
}
|
|
15
|
-
else if (!subs.includes(eff)) {
|
|
16
|
-
subs.push(eff);
|
|
17
|
-
if (subs.length > SUBS_SET_THRESHOLD)
|
|
18
|
-
holder.subs = new Set(subs);
|
|
19
|
-
}
|
|
20
|
-
recordDependency(eff, holder);
|
|
21
|
-
}
|
|
22
|
-
export { removeSub };
|
|
23
|
-
export function notifySubs(subs, batching) {
|
|
24
|
-
if (typeof subs === "function") {
|
|
25
|
-
if (batching)
|
|
26
|
-
enqueueEffect(subs);
|
|
27
|
-
else
|
|
28
|
-
subs();
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const snapshot = [...subs];
|
|
32
|
-
if (batching) {
|
|
33
|
-
for (const sub of snapshot)
|
|
34
|
-
enqueueEffect(sub);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
let lastError;
|
|
38
|
-
let hasError = false;
|
|
39
|
-
for (const sub of snapshot) {
|
|
40
|
-
try {
|
|
41
|
-
sub();
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
lastError = e;
|
|
45
|
-
hasError = true;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (hasError)
|
|
49
|
-
throw lastError;
|
|
50
|
-
}
|
|
1
|
+
import { untrack } from "./effect";
|
|
2
|
+
import { createEffect, disposeEffect, readSignal, SignalNode, writeSignal } from "./graph";
|
|
51
3
|
export function signal(initialValue) {
|
|
52
|
-
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
addSub(holder, activeEffect);
|
|
57
|
-
return value;
|
|
58
|
-
}
|
|
59
|
-
const s = read;
|
|
60
|
-
s.set = (newValue) => {
|
|
61
|
-
if (Object.is(value, newValue))
|
|
62
|
-
return;
|
|
63
|
-
value = newValue;
|
|
64
|
-
if (holder.subs !== null)
|
|
65
|
-
notifySubs(holder.subs, isBatching());
|
|
66
|
-
};
|
|
67
|
-
s.update = (fn) => { s.set(fn(value)); };
|
|
4
|
+
const node = new SignalNode(initialValue);
|
|
5
|
+
const s = (() => readSignal(node));
|
|
6
|
+
s.set = (newValue) => { writeSignal(node, newValue); };
|
|
7
|
+
s.update = (fn) => { writeSignal(node, fn(node.value)); };
|
|
68
8
|
s.subscribe = (fn) => {
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
9
|
+
const sub = createEffect(() => {
|
|
10
|
+
const value = readSignal(node);
|
|
11
|
+
untrack(() => { fn(value); });
|
|
12
|
+
});
|
|
13
|
+
return () => { disposeEffect(sub); };
|
|
73
14
|
};
|
|
74
15
|
s.__isSignal = true;
|
|
75
16
|
return s;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"signal.js","sourceRoot":"","sources":["../../src/signal/signal.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3F,MAAM,UAAU,MAAM,CAAI,YAAe;IACvC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC;IAE1C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAc,CAAC;IAChD,CAAC,CAAC,GAAG,GAAG,CAAC,QAAW,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,MAAM,GAAG,CAAC,EAAkB,EAAE,EAAE,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,SAAS,GAAG,CAAC,EAAsB,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,YAAY,CAAC,GAAG,EAAE;YAC5B,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAChC,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,EAAE,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC;IACF,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,CAAC;AACX,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisjs/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.1",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"types": "./dist/index.d.ts",
|
|
7
8
|
"exports": {
|
|
@@ -14,14 +15,22 @@
|
|
|
14
15
|
"types": "./dist/internal.d.ts"
|
|
15
16
|
}
|
|
16
17
|
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"src",
|
|
21
|
+
"!dist/**/__tests__",
|
|
22
|
+
"!src/**/__tests__",
|
|
23
|
+
"CHANGELOG.md"
|
|
24
|
+
],
|
|
17
25
|
"devDependencies": {
|
|
18
26
|
"typescript": "^6.0.3"
|
|
19
27
|
},
|
|
20
28
|
"dependencies": {
|
|
21
|
-
"@praxisjs/shared": "0.3.
|
|
29
|
+
"@praxisjs/shared": "^0.3.2"
|
|
22
30
|
},
|
|
23
31
|
"scripts": {
|
|
24
|
-
"build": "tsc",
|
|
25
|
-
"dev": "tsc --watch"
|
|
32
|
+
"build": "tsc -p tsconfig.build.json",
|
|
33
|
+
"dev": "tsc -p tsconfig.build.json --watch",
|
|
34
|
+
"typecheck": "tsc --noEmit"
|
|
26
35
|
}
|
|
27
36
|
}
|
|
@@ -3,15 +3,22 @@ export const componentPropsType: unique symbol = Symbol("praxis.componentPropsTy
|
|
|
3
3
|
interface ComponentInternals<T extends object> {
|
|
4
4
|
rawProps: T;
|
|
5
5
|
mounted: boolean;
|
|
6
|
-
anchor
|
|
6
|
+
anchor: Node | undefined;
|
|
7
|
+
start: Comment | undefined;
|
|
7
8
|
defaults: Record<string, unknown>;
|
|
8
9
|
stateDirty: boolean;
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Kept on the instance under a symbol rather than in a WeakMap: every @Prop read goes through
|
|
13
|
+
// here, and a property load is much cheaper than a WeakMap lookup.
|
|
14
|
+
const INTERNALS: unique symbol = Symbol("praxis.internals");
|
|
15
|
+
|
|
16
|
+
interface InternalsHost {
|
|
17
|
+
[INTERNALS]?: ComponentInternals<object>;
|
|
18
|
+
}
|
|
12
19
|
|
|
13
20
|
function getInternals(instance: object): ComponentInternals<object> {
|
|
14
|
-
const internals =
|
|
21
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
15
22
|
if (!internals) {
|
|
16
23
|
throw new TypeError("Expected a PraxisJS component instance.");
|
|
17
24
|
}
|
|
@@ -22,12 +29,14 @@ export function initComponentInternals(
|
|
|
22
29
|
instance: object,
|
|
23
30
|
props: object,
|
|
24
31
|
): void {
|
|
25
|
-
|
|
32
|
+
(instance as InternalsHost)[INTERNALS] = {
|
|
26
33
|
rawProps: { ...props },
|
|
27
34
|
mounted: false,
|
|
35
|
+
anchor: undefined,
|
|
36
|
+
start: undefined,
|
|
28
37
|
defaults: {},
|
|
29
38
|
stateDirty: false,
|
|
30
|
-
}
|
|
39
|
+
};
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export function getComponentProps(instance: object): object {
|
|
@@ -73,13 +82,17 @@ export function getComponentDefaults(
|
|
|
73
82
|
return getInternals(instance).defaults;
|
|
74
83
|
}
|
|
75
84
|
|
|
76
|
-
|
|
85
|
+
/**
|
|
86
|
+
* A node of the mounted component's output: its only root node, or the comment that closes its
|
|
87
|
+
* region when it renders several nodes. Its `parentElement` is the element the component sits in.
|
|
88
|
+
*/
|
|
89
|
+
export function getComponentAnchor(instance: object): Node | undefined {
|
|
77
90
|
return getInternals(instance).anchor;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
export function setComponentAnchor(
|
|
81
94
|
instance: object,
|
|
82
|
-
anchor:
|
|
95
|
+
anchor: Node | undefined,
|
|
83
96
|
): void {
|
|
84
97
|
getInternals(instance).anchor = anchor;
|
|
85
98
|
}
|
|
@@ -96,14 +109,14 @@ export function setComponentMounted(
|
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
export function markStateDirty(instance: object): void {
|
|
99
|
-
const internals =
|
|
112
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
100
113
|
if (internals) {
|
|
101
114
|
internals.stateDirty = true;
|
|
102
115
|
}
|
|
103
116
|
}
|
|
104
117
|
|
|
105
118
|
export function isStateDirty(instance: object): boolean {
|
|
106
|
-
const internals =
|
|
119
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
107
120
|
if (internals) return internals.stateDirty;
|
|
108
121
|
return false;
|
|
109
122
|
}
|
|
@@ -112,8 +125,20 @@ export function setStateDirty(
|
|
|
112
125
|
instance: object,
|
|
113
126
|
stateDirty: boolean,
|
|
114
127
|
): void {
|
|
115
|
-
const internals =
|
|
128
|
+
const internals = (instance as InternalsHost)[INTERNALS];
|
|
116
129
|
if (internals) {
|
|
117
130
|
internals.stateDirty = stateDirty;
|
|
118
131
|
}
|
|
119
132
|
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The comment that opens a component's region, when it has one: a component whose render sets it
|
|
136
|
+
* (with the closing comment as its anchor) brings its own boundaries, and mountComponent adds none.
|
|
137
|
+
*/
|
|
138
|
+
export function getComponentStart(instance: object): Comment | undefined {
|
|
139
|
+
return getInternals(instance).start;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function setComponentStart(instance: object, start: Comment | undefined): void {
|
|
143
|
+
getInternals(instance).start = start;
|
|
144
|
+
}
|
package/src/internal.ts
CHANGED
|
@@ -15,11 +15,35 @@ export {
|
|
|
15
15
|
batch,
|
|
16
16
|
effect,
|
|
17
17
|
untrack,
|
|
18
|
+
selector,
|
|
19
|
+
type Selector,
|
|
20
|
+
type SelectorEquals,
|
|
18
21
|
type PersistedSignalOptions,
|
|
19
22
|
type PersistedSignal,
|
|
20
23
|
type SyncedSignal,
|
|
21
24
|
} from "./signal";
|
|
22
25
|
export { RootComponent } from "./component";
|
|
26
|
+
export {
|
|
27
|
+
createEffect,
|
|
28
|
+
createContextEffect,
|
|
29
|
+
disposeEffect,
|
|
30
|
+
startBatch,
|
|
31
|
+
endBatch,
|
|
32
|
+
EffectNode,
|
|
33
|
+
SignalNode,
|
|
34
|
+
readSignal,
|
|
35
|
+
writeSignal,
|
|
36
|
+
ComputedNode,
|
|
37
|
+
readComputed,
|
|
38
|
+
type ReactiveNode,
|
|
39
|
+
pauseTracking,
|
|
40
|
+
resumeTracking,
|
|
41
|
+
setReactiveObserver,
|
|
42
|
+
setCurrentOwner,
|
|
43
|
+
getEffectOwner,
|
|
44
|
+
type ReactiveObserver,
|
|
45
|
+
type EffectCleanup,
|
|
46
|
+
} from "./signal/graph";
|
|
23
47
|
export {
|
|
24
48
|
componentPropsType,
|
|
25
49
|
getComponentAnchor,
|
|
@@ -27,6 +51,8 @@ export {
|
|
|
27
51
|
getComponentDefaults,
|
|
28
52
|
getComponentProps,
|
|
29
53
|
getComponentRawProp,
|
|
54
|
+
getComponentStart,
|
|
55
|
+
setComponentStart,
|
|
30
56
|
isComponentMounted,
|
|
31
57
|
isStateDirty,
|
|
32
58
|
markStateDirty,
|
package/src/signal/batch.ts
CHANGED
|
@@ -1,39 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { endBatch, startBatch } from "./graph";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function isBatching(): boolean {
|
|
7
|
-
return batchDepth > 0;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function enqueueEffect(effect: Effect): void {
|
|
11
|
-
batchEffects.add(effect);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function batch(fn: () => void) {
|
|
15
|
-
batchDepth++;
|
|
3
|
+
/** Runs `fn` and defers every effect it invalidates until it returns, so each runs once. */
|
|
4
|
+
export function batch(fn: () => void): void {
|
|
5
|
+
startBatch();
|
|
16
6
|
try {
|
|
17
7
|
fn();
|
|
18
8
|
} finally {
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// The queue is detached before running so a throwing effect can't leave stale entries behind,
|
|
24
|
-
// and every queued effect still runs — matching how notifySubs treats errors outside a batch.
|
|
25
|
-
function flush(): void {
|
|
26
|
-
const queued = [...batchEffects];
|
|
27
|
-
batchEffects.clear();
|
|
28
|
-
let lastError: unknown;
|
|
29
|
-
let hasError = false;
|
|
30
|
-
for (const effect of queued) {
|
|
31
|
-
try {
|
|
32
|
-
effect();
|
|
33
|
-
} catch (e) {
|
|
34
|
-
lastError = e;
|
|
35
|
-
hasError = true;
|
|
36
|
-
}
|
|
9
|
+
endBatch();
|
|
37
10
|
}
|
|
38
|
-
if (hasError) throw lastError;
|
|
39
11
|
}
|
package/src/signal/computed.ts
CHANGED
|
@@ -1,82 +1,29 @@
|
|
|
1
1
|
import type { Computed, WritableComputed } from "@praxisjs/shared";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
type ComputedRecompute = Effect & { __isComputedRecompute: true };
|
|
7
|
-
|
|
8
|
-
function isComputedRecompute(eff: Effect): boolean {
|
|
9
|
-
return (eff as Partial<ComputedRecompute>).__isComputedRecompute === true;
|
|
10
|
-
}
|
|
3
|
+
import { untrack } from "./effect";
|
|
4
|
+
import { ComputedNode, createEffect, DIRTY, disposeEffect, readComputed, unlinkDeps } from "./graph";
|
|
11
5
|
|
|
12
6
|
export function computed<T>(getter: () => T): Computed<T> {
|
|
13
|
-
|
|
14
|
-
let dirty = true;
|
|
15
|
-
|
|
16
|
-
const computedHolder = { subs: null as SubList };
|
|
17
|
-
const leafHolder = { subs: null as SubList };
|
|
18
|
-
let scheduled = false;
|
|
19
|
-
|
|
20
|
-
let recompute: ComputedRecompute | null = null;
|
|
21
|
-
|
|
22
|
-
function getRecompute(): ComputedRecompute {
|
|
23
|
-
if (recompute !== null) return recompute;
|
|
24
|
-
recompute = function markDirty(): void {
|
|
25
|
-
if (dirty) return;
|
|
26
|
-
dirty = true;
|
|
27
|
-
if (computedHolder.subs !== null) notifySubs(computedHolder.subs, false);
|
|
28
|
-
if (leafHolder.subs !== null && !scheduled) {
|
|
29
|
-
scheduled = true;
|
|
30
|
-
queueMicrotask(() => {
|
|
31
|
-
scheduled = false;
|
|
32
|
-
if (leafHolder.subs !== null) notifySubs(leafHolder.subs, false);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
} as ComputedRecompute;
|
|
36
|
-
recompute.__isComputedRecompute = true;
|
|
37
|
-
return recompute;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function read(): T {
|
|
41
|
-
if (activeEffect !== null) {
|
|
42
|
-
if (isComputedRecompute(activeEffect)) {
|
|
43
|
-
addSub(computedHolder, activeEffect);
|
|
44
|
-
} else {
|
|
45
|
-
addSub(leafHolder, activeEffect);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (dirty) {
|
|
49
|
-
const prevEffect = activeEffect;
|
|
50
|
-
const recomputeEffect = getRecompute();
|
|
51
|
-
cleanupEffectDeps(recomputeEffect);
|
|
52
|
-
runEffect(recomputeEffect);
|
|
53
|
-
try {
|
|
54
|
-
cachedValue = getter();
|
|
55
|
-
dirty = false;
|
|
56
|
-
} finally {
|
|
57
|
-
runEffect(prevEffect);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return cachedValue as T;
|
|
61
|
-
}
|
|
7
|
+
const node = new ComputedNode(getter);
|
|
62
8
|
|
|
63
|
-
const c =
|
|
9
|
+
const c = (() => readComputed(node)) as Computed<T> & { dispose(): void };
|
|
64
10
|
|
|
65
11
|
// Drops this computed's subscriptions to its sources so an unreachable one (e.g. an evicted
|
|
66
12
|
// @Memo entry) isn't kept alive by the signals it read. Does nothing while anything still
|
|
67
13
|
// depends on it: detaching then would leave those dependents silently stale.
|
|
68
14
|
c.dispose = () => {
|
|
69
|
-
if (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
15
|
+
if (node.subs !== undefined) return;
|
|
16
|
+
unlinkDeps(node);
|
|
17
|
+
node.flags |= DIRTY;
|
|
18
|
+
node.value = undefined;
|
|
73
19
|
};
|
|
74
20
|
|
|
75
21
|
c.subscribe = (fn: (value: T) => void) => {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
22
|
+
const sub = createEffect(() => {
|
|
23
|
+
const value = readComputed(node);
|
|
24
|
+
untrack(() => { fn(value); });
|
|
25
|
+
});
|
|
26
|
+
return () => { disposeEffect(sub); };
|
|
80
27
|
};
|
|
81
28
|
|
|
82
29
|
c.__isComputed = true;
|