onejs-core 0.3.5
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/.gitattributes +2 -0
- package/.github/workflows/jsr.yml +19 -0
- package/.prettierrc +5 -0
- package/3rdparty/preact/LICENSE +21 -0
- package/3rdparty/preact/clone-element.ts +45 -0
- package/3rdparty/preact/compat/Children.ts +21 -0
- package/3rdparty/preact/compat/forwardRef.ts +49 -0
- package/3rdparty/preact/compat/index.ts +3 -0
- package/3rdparty/preact/compat/memo.ts +34 -0
- package/3rdparty/preact/compat/util.ts +38 -0
- package/3rdparty/preact/component.ts +235 -0
- package/3rdparty/preact/constants.ts +3 -0
- package/3rdparty/preact/create-context.ts +71 -0
- package/3rdparty/preact/create-element.ts +98 -0
- package/3rdparty/preact/diff/catch-error.ts +40 -0
- package/3rdparty/preact/diff/children.ts +355 -0
- package/3rdparty/preact/diff/index.ts +563 -0
- package/3rdparty/preact/diff/props.ts +174 -0
- package/3rdparty/preact/hooks/index.ts +536 -0
- package/3rdparty/preact/hooks/internal.d.ts +85 -0
- package/3rdparty/preact/hooks.d.ts +145 -0
- package/3rdparty/preact/index.ts +13 -0
- package/3rdparty/preact/internal.d.ts +155 -0
- package/3rdparty/preact/jsx-runtime/index.ts +80 -0
- package/3rdparty/preact/jsx.d.ts +1008 -0
- package/3rdparty/preact/options.ts +16 -0
- package/3rdparty/preact/preact.d.ts +317 -0
- package/3rdparty/preact/render.ts +76 -0
- package/3rdparty/preact/signals/index.ts +443 -0
- package/3rdparty/preact/signals/internal.d.ts +36 -0
- package/3rdparty/preact/signals-core/index.ts +663 -0
- package/3rdparty/preact/style.d.ts +205 -0
- package/3rdparty/preact/util.ts +29 -0
- package/@DO_NOT_CHANGE.txt +3 -0
- package/README.md +33 -0
- package/definitions/app.d.ts +52048 -0
- package/definitions/augments.d.ts +16 -0
- package/definitions/globals.d.ts +34 -0
- package/definitions/index.d.ts +9 -0
- package/definitions/jsx.d.ts +517 -0
- package/definitions/modules.d.ts +29 -0
- package/definitions/onejs.d.ts +164 -0
- package/definitions/preact.jsx.d.ts +7 -0
- package/definitions/proto-overrides.d.ts +13 -0
- package/definitions/puerts.d.ts +31 -0
- package/definitions/unity-engine.d.ts +23 -0
- package/hooks/eventful.ts +56 -0
- package/import-transform.mjs +42 -0
- package/index.ts +44 -0
- package/jsr.json +10 -0
- package/onejs-tw-config.cjs +188 -0
- package/package.json +9 -0
- package/preloads/inject.ts +44 -0
- package/styling/index.tsx +80 -0
- package/styling/utils/generateAlphabeticName.ts +21 -0
- package/styling/utils/generateComponentId.ts +6 -0
- package/styling/utils/hash.ts +46 -0
- package/switch.cjs +185 -0
- package/uss-transform-plugin.cjs +83 -0
- package/utils/color-palettes.ts +3 -0
- package/utils/color-parser.ts +249 -0
- package/utils/float-parser.ts +31 -0
- package/utils/index.ts +12 -0
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
function cycleDetected(): never {
|
|
2
|
+
throw new Error("Cycle detected");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// Flags for Computed and Effect.
|
|
6
|
+
const RUNNING = 1 << 0;
|
|
7
|
+
const NOTIFIED = 1 << 1;
|
|
8
|
+
const OUTDATED = 1 << 2;
|
|
9
|
+
const DISPOSED = 1 << 3;
|
|
10
|
+
const HAS_ERROR = 1 << 4;
|
|
11
|
+
const TRACKING = 1 << 5;
|
|
12
|
+
|
|
13
|
+
// A linked list node used to track dependencies (sources) and dependents (targets).
|
|
14
|
+
// Also used to remember the source's last version number that the target saw.
|
|
15
|
+
type Node = {
|
|
16
|
+
// A source whose value the target depends on.
|
|
17
|
+
_source: Signal;
|
|
18
|
+
_prevSource?: Node;
|
|
19
|
+
_nextSource?: Node;
|
|
20
|
+
|
|
21
|
+
// A target that depends on the source and should be notified when the source changes.
|
|
22
|
+
_target: Computed | Effect;
|
|
23
|
+
_prevTarget?: Node;
|
|
24
|
+
_nextTarget?: Node;
|
|
25
|
+
|
|
26
|
+
// The version number of the source that target has last seen. We use version numbers
|
|
27
|
+
// instead of storing the source value, because source values can take arbitrary amount
|
|
28
|
+
// of memory, and computeds could hang on to them forever because they're lazily evaluated.
|
|
29
|
+
// Use the special value -1 to mark potentially unused but recyclable nodes.
|
|
30
|
+
_version: number;
|
|
31
|
+
|
|
32
|
+
// Used to remember & roll back the source's previous `._node` value when entering &
|
|
33
|
+
// exiting a new evaluation context.
|
|
34
|
+
_rollbackNode?: Node;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function startBatch() {
|
|
38
|
+
batchDepth++;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function endBatch() {
|
|
42
|
+
if (batchDepth > 1) {
|
|
43
|
+
batchDepth--;
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let error: unknown;
|
|
48
|
+
let hasError = false;
|
|
49
|
+
|
|
50
|
+
while (batchedEffect !== undefined) {
|
|
51
|
+
let effect: Effect | undefined = batchedEffect;
|
|
52
|
+
batchedEffect = undefined;
|
|
53
|
+
|
|
54
|
+
batchIteration++;
|
|
55
|
+
|
|
56
|
+
while (effect !== undefined) {
|
|
57
|
+
const next: Effect | undefined = effect._nextBatchedEffect;
|
|
58
|
+
effect._nextBatchedEffect = undefined;
|
|
59
|
+
effect._flags &= ~NOTIFIED;
|
|
60
|
+
|
|
61
|
+
if (!(effect._flags & DISPOSED) && needsToRecompute(effect)) {
|
|
62
|
+
try {
|
|
63
|
+
effect._callback();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (!hasError) {
|
|
66
|
+
error = err;
|
|
67
|
+
hasError = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
effect = next;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
batchIteration = 0;
|
|
75
|
+
batchDepth--;
|
|
76
|
+
|
|
77
|
+
if (hasError) {
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function batch<T>(callback: () => T): T {
|
|
83
|
+
if (batchDepth > 0) {
|
|
84
|
+
return callback();
|
|
85
|
+
}
|
|
86
|
+
/*@__INLINE__**/ startBatch();
|
|
87
|
+
try {
|
|
88
|
+
return callback();
|
|
89
|
+
} finally {
|
|
90
|
+
endBatch();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Currently evaluated computed or effect.
|
|
95
|
+
let evalContext: Computed | Effect | undefined = undefined;
|
|
96
|
+
|
|
97
|
+
// Effects collected into a batch.
|
|
98
|
+
let batchedEffect: Effect | undefined = undefined;
|
|
99
|
+
let batchDepth = 0;
|
|
100
|
+
let batchIteration = 0;
|
|
101
|
+
|
|
102
|
+
// A global version number for signals, used for fast-pathing repeated
|
|
103
|
+
// computed.peek()/computed.value calls when nothing has changed globally.
|
|
104
|
+
let globalVersion = 0;
|
|
105
|
+
|
|
106
|
+
function addDependency(signal: Signal): Node | undefined {
|
|
107
|
+
if (evalContext === undefined) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let node = signal._node;
|
|
112
|
+
if (node === undefined || node._target !== evalContext) {
|
|
113
|
+
// `signal` is a new dependency. Create a new node dependency node, move it
|
|
114
|
+
// to the front of the current context's dependency list.
|
|
115
|
+
node = {
|
|
116
|
+
_version: 0,
|
|
117
|
+
_source: signal,
|
|
118
|
+
_prevSource: undefined,
|
|
119
|
+
_nextSource: evalContext._sources,
|
|
120
|
+
_target: evalContext,
|
|
121
|
+
_prevTarget: undefined,
|
|
122
|
+
_nextTarget: undefined,
|
|
123
|
+
_rollbackNode: node,
|
|
124
|
+
};
|
|
125
|
+
evalContext._sources = node;
|
|
126
|
+
signal._node = node;
|
|
127
|
+
|
|
128
|
+
// Subscribe to change notifications from this dependency if we're in an effect
|
|
129
|
+
// OR evaluating a computed signal that in turn has subscribers.
|
|
130
|
+
if (evalContext._flags & TRACKING) {
|
|
131
|
+
signal._subscribe(node);
|
|
132
|
+
}
|
|
133
|
+
return node;
|
|
134
|
+
} else if (node._version === -1) {
|
|
135
|
+
// `signal` is an existing dependency from a previous evaluation. Reuse it.
|
|
136
|
+
node._version = 0;
|
|
137
|
+
|
|
138
|
+
// If `node` is not already the current head of the dependency list (i.e.
|
|
139
|
+
// there is a previous node in the list), then make `node` the new head.
|
|
140
|
+
if (node._prevSource !== undefined) {
|
|
141
|
+
node._prevSource._nextSource = node._nextSource;
|
|
142
|
+
if (node._nextSource !== undefined) {
|
|
143
|
+
node._nextSource._prevSource = node._prevSource;
|
|
144
|
+
}
|
|
145
|
+
node._prevSource = undefined;
|
|
146
|
+
node._nextSource = evalContext._sources;
|
|
147
|
+
// evalCotext._sources must be !== undefined (and !== node), because
|
|
148
|
+
// `node` was originally pointing to some previous node.
|
|
149
|
+
evalContext._sources!._prevSource = node;
|
|
150
|
+
evalContext._sources = node;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// We can assume that the currently evaluated effect / computed signal is already
|
|
154
|
+
// subscribed to change notifications from `signal` if needed.
|
|
155
|
+
return node;
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
declare class Signal<T = any> {
|
|
161
|
+
/** @internal */
|
|
162
|
+
_value: unknown;
|
|
163
|
+
|
|
164
|
+
/** @internal
|
|
165
|
+
* Version numbers should always be >= 0, because the special value -1 is used
|
|
166
|
+
* by Nodes to signify potentially unused but recyclable notes.
|
|
167
|
+
*/
|
|
168
|
+
_version: number;
|
|
169
|
+
|
|
170
|
+
/** @internal */
|
|
171
|
+
_node?: Node;
|
|
172
|
+
|
|
173
|
+
/** @internal */
|
|
174
|
+
_targets?: Node;
|
|
175
|
+
|
|
176
|
+
constructor(value?: T);
|
|
177
|
+
|
|
178
|
+
/** @internal */
|
|
179
|
+
_refresh(): boolean;
|
|
180
|
+
|
|
181
|
+
/** @internal */
|
|
182
|
+
_subscribe(node: Node): void;
|
|
183
|
+
|
|
184
|
+
/** @internal */
|
|
185
|
+
_unsubscribe(node: Node): void;
|
|
186
|
+
|
|
187
|
+
subscribe(fn: (value: T) => void): () => void;
|
|
188
|
+
|
|
189
|
+
valueOf(): T;
|
|
190
|
+
|
|
191
|
+
toString(): string;
|
|
192
|
+
|
|
193
|
+
peek(): T;
|
|
194
|
+
|
|
195
|
+
get value(): T;
|
|
196
|
+
set value(value: T);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** @internal */
|
|
200
|
+
function Signal(this: Signal, value?: unknown) {
|
|
201
|
+
this._value = value;
|
|
202
|
+
this._version = 0;
|
|
203
|
+
this._node = undefined;
|
|
204
|
+
this._targets = undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
Signal.prototype._refresh = function () {
|
|
208
|
+
return true;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
Signal.prototype._subscribe = function (node) {
|
|
212
|
+
if (this._targets !== node && node._prevTarget === undefined) {
|
|
213
|
+
node._nextTarget = this._targets;
|
|
214
|
+
if (this._targets !== undefined) {
|
|
215
|
+
this._targets._prevTarget = node;
|
|
216
|
+
}
|
|
217
|
+
this._targets = node;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
Signal.prototype._unsubscribe = function (node) {
|
|
222
|
+
const prev = node._prevTarget;
|
|
223
|
+
const next = node._nextTarget;
|
|
224
|
+
if (prev !== undefined) {
|
|
225
|
+
prev._nextTarget = next;
|
|
226
|
+
node._prevTarget = undefined;
|
|
227
|
+
}
|
|
228
|
+
if (next !== undefined) {
|
|
229
|
+
next._prevTarget = prev;
|
|
230
|
+
node._nextTarget = undefined;
|
|
231
|
+
}
|
|
232
|
+
if (node === this._targets) {
|
|
233
|
+
this._targets = next;
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
Signal.prototype.subscribe = function (fn) {
|
|
238
|
+
const signal = this;
|
|
239
|
+
return effect(function (this: Effect) {
|
|
240
|
+
const value = signal.value;
|
|
241
|
+
const flag = this._flags & TRACKING;
|
|
242
|
+
this._flags &= ~TRACKING;
|
|
243
|
+
try {
|
|
244
|
+
fn(value);
|
|
245
|
+
} finally {
|
|
246
|
+
this._flags |= flag;
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
Signal.prototype.valueOf = function () {
|
|
252
|
+
return this.value;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
Signal.prototype.toString = function () {
|
|
256
|
+
return this.value + "";
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
Signal.prototype.peek = function () {
|
|
260
|
+
return this._value;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
Object.defineProperty(Signal.prototype, "value", {
|
|
264
|
+
get() {
|
|
265
|
+
const node = addDependency(this);
|
|
266
|
+
if (node !== undefined) {
|
|
267
|
+
node._version = this._version;
|
|
268
|
+
}
|
|
269
|
+
return this._value;
|
|
270
|
+
},
|
|
271
|
+
set(value) {
|
|
272
|
+
if (value !== this._value) {
|
|
273
|
+
if (batchIteration > 100) {
|
|
274
|
+
cycleDetected();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
this._value = value;
|
|
278
|
+
this._version++;
|
|
279
|
+
globalVersion++;
|
|
280
|
+
|
|
281
|
+
/**@__INLINE__*/ startBatch();
|
|
282
|
+
try {
|
|
283
|
+
for (
|
|
284
|
+
let node = this._targets;
|
|
285
|
+
node !== undefined;
|
|
286
|
+
node = node._nextTarget
|
|
287
|
+
) {
|
|
288
|
+
node._target._notify();
|
|
289
|
+
}
|
|
290
|
+
} finally {
|
|
291
|
+
endBatch();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
function signal<T>(value: T): Signal<T> {
|
|
298
|
+
return new Signal(value);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function needsToRecompute(target: Computed | Effect): boolean {
|
|
302
|
+
// Check the dependencies for changed values. The dependency list is already
|
|
303
|
+
// in order of use. Therefore if multiple dependencies have changed values, only
|
|
304
|
+
// the first used dependency is re-evaluated at this point.
|
|
305
|
+
for (
|
|
306
|
+
let node = target._sources;
|
|
307
|
+
node !== undefined;
|
|
308
|
+
node = node._nextSource
|
|
309
|
+
) {
|
|
310
|
+
// If there's a new version of the dependency before or after refreshing,
|
|
311
|
+
// or the dependency has something blocking it from refreshing at all (e.g. a
|
|
312
|
+
// dependency cycle), then we need to recompute.
|
|
313
|
+
if (
|
|
314
|
+
node._source._version !== node._version ||
|
|
315
|
+
!node._source._refresh() ||
|
|
316
|
+
node._source._version !== node._version
|
|
317
|
+
) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// If none of the dependencies have changed values since last recompute then the
|
|
322
|
+
// there's no need to recompute.
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function prepareSources(target: Computed | Effect) {
|
|
327
|
+
for (
|
|
328
|
+
let node = target._sources;
|
|
329
|
+
node !== undefined;
|
|
330
|
+
node = node._nextSource
|
|
331
|
+
) {
|
|
332
|
+
const rollbackNode = node._source._node;
|
|
333
|
+
if (rollbackNode !== undefined) {
|
|
334
|
+
node._rollbackNode = rollbackNode;
|
|
335
|
+
}
|
|
336
|
+
node._source._node = node;
|
|
337
|
+
node._version = -1;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
function cleanupSources(target: Computed | Effect) {
|
|
342
|
+
// At this point target._sources is a mishmash of current & former dependencies.
|
|
343
|
+
// The current dependencies are also in a reverse order of use.
|
|
344
|
+
// Therefore build a new, reverted list of dependencies containing only the current
|
|
345
|
+
// dependencies in a proper order of use.
|
|
346
|
+
// Drop former dependencies from the list and unsubscribe from their change notifications.
|
|
347
|
+
|
|
348
|
+
let node = target._sources;
|
|
349
|
+
let sources: any = undefined;
|
|
350
|
+
while (node !== undefined) {
|
|
351
|
+
const next = node._nextSource;
|
|
352
|
+
if (node._version === -1) {
|
|
353
|
+
node._source._unsubscribe(node);
|
|
354
|
+
node._nextSource = undefined;
|
|
355
|
+
} else {
|
|
356
|
+
if (sources !== undefined) {
|
|
357
|
+
sources._prevSource = node;
|
|
358
|
+
}
|
|
359
|
+
node._prevSource = undefined;
|
|
360
|
+
node._nextSource = sources;
|
|
361
|
+
sources = node;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
node._source._node = node._rollbackNode;
|
|
365
|
+
if (node._rollbackNode !== undefined) {
|
|
366
|
+
node._rollbackNode = undefined;
|
|
367
|
+
}
|
|
368
|
+
node = next;
|
|
369
|
+
}
|
|
370
|
+
target._sources = sources;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
declare class Computed<T = any> extends Signal<T> {
|
|
374
|
+
_compute: () => T;
|
|
375
|
+
_sources?: Node;
|
|
376
|
+
_globalVersion: number;
|
|
377
|
+
_flags: number;
|
|
378
|
+
|
|
379
|
+
constructor(compute: () => T);
|
|
380
|
+
|
|
381
|
+
_notify(): void;
|
|
382
|
+
get value(): T;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function Computed(this: Computed, compute: () => unknown) {
|
|
386
|
+
Signal.call(this, undefined);
|
|
387
|
+
|
|
388
|
+
this._compute = compute;
|
|
389
|
+
this._sources = undefined;
|
|
390
|
+
this._globalVersion = globalVersion - 1;
|
|
391
|
+
this._flags = OUTDATED;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
Computed.prototype = new Signal() as unknown as Computed;
|
|
395
|
+
|
|
396
|
+
(Computed.prototype as any)._refresh = function () {
|
|
397
|
+
this._flags &= ~NOTIFIED;
|
|
398
|
+
|
|
399
|
+
if (this._flags & RUNNING) {
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// If this computed signal has subscribed to updates from its dependencies
|
|
404
|
+
// (TRACKING flag set) and none of them have notified about changes (OUTDATED
|
|
405
|
+
// flag not set), then the computed value can't have changed.
|
|
406
|
+
if ((this._flags & (OUTDATED | TRACKING)) === TRACKING) {
|
|
407
|
+
return true;
|
|
408
|
+
}
|
|
409
|
+
this._flags &= ~OUTDATED;
|
|
410
|
+
|
|
411
|
+
if (this._globalVersion === globalVersion) {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
this._globalVersion = globalVersion;
|
|
415
|
+
|
|
416
|
+
// Mark this computed signal running before checking the dependencies for value
|
|
417
|
+
// changes, so that the RUNNIN flag can be used to notice cyclical dependencies.
|
|
418
|
+
this._flags |= RUNNING;
|
|
419
|
+
if (this._version > 0 && !needsToRecompute(this)) {
|
|
420
|
+
this._flags &= ~RUNNING;
|
|
421
|
+
return true;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
const prevContext = evalContext;
|
|
425
|
+
try {
|
|
426
|
+
prepareSources(this);
|
|
427
|
+
evalContext = this;
|
|
428
|
+
const value = this._compute();
|
|
429
|
+
if (
|
|
430
|
+
this._flags & HAS_ERROR ||
|
|
431
|
+
this._value !== value ||
|
|
432
|
+
this._version === 0
|
|
433
|
+
) {
|
|
434
|
+
this._value = value;
|
|
435
|
+
this._flags &= ~HAS_ERROR;
|
|
436
|
+
this._version++;
|
|
437
|
+
}
|
|
438
|
+
} catch (err) {
|
|
439
|
+
this._value = err;
|
|
440
|
+
this._flags |= HAS_ERROR;
|
|
441
|
+
this._version++;
|
|
442
|
+
}
|
|
443
|
+
evalContext = prevContext;
|
|
444
|
+
cleanupSources(this);
|
|
445
|
+
this._flags &= ~RUNNING;
|
|
446
|
+
return true;
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
(Computed.prototype as any)._subscribe = function (node) {
|
|
450
|
+
if (this._targets === undefined) {
|
|
451
|
+
this._flags |= OUTDATED | TRACKING;
|
|
452
|
+
|
|
453
|
+
// A computed signal subscribes lazily to its dependencies when the it
|
|
454
|
+
// gets its first subscriber.
|
|
455
|
+
for (
|
|
456
|
+
let node = this._sources;
|
|
457
|
+
node !== undefined;
|
|
458
|
+
node = node._nextSource
|
|
459
|
+
) {
|
|
460
|
+
node._source._subscribe(node);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
Signal.prototype._subscribe.call(this, node);
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
(Computed.prototype as any)._unsubscribe = function (node) {
|
|
467
|
+
Signal.prototype._unsubscribe.call(this, node);
|
|
468
|
+
|
|
469
|
+
// Computed signal unsubscribes from its dependencies from it loses its last subscriber.
|
|
470
|
+
if (this._targets === undefined) {
|
|
471
|
+
this._flags &= ~TRACKING;
|
|
472
|
+
|
|
473
|
+
for (
|
|
474
|
+
let node = this._sources;
|
|
475
|
+
node !== undefined;
|
|
476
|
+
node = node._nextSource
|
|
477
|
+
) {
|
|
478
|
+
node._source._unsubscribe(node);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
(Computed.prototype as any)._notify = function () {
|
|
484
|
+
if (!(this._flags & NOTIFIED)) {
|
|
485
|
+
this._flags |= OUTDATED | NOTIFIED;
|
|
486
|
+
|
|
487
|
+
for (
|
|
488
|
+
let node = this._targets;
|
|
489
|
+
node !== undefined;
|
|
490
|
+
node = node._nextTarget
|
|
491
|
+
) {
|
|
492
|
+
node._target._notify();
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
(Computed.prototype as any).peek = function () {
|
|
498
|
+
if (!this._refresh()) {
|
|
499
|
+
cycleDetected();
|
|
500
|
+
}
|
|
501
|
+
if (this._flags & HAS_ERROR) {
|
|
502
|
+
throw this._value;
|
|
503
|
+
}
|
|
504
|
+
return this._value;
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
Object.defineProperty(Computed.prototype, "value", {
|
|
508
|
+
get() {
|
|
509
|
+
if (this._flags & RUNNING) {
|
|
510
|
+
cycleDetected();
|
|
511
|
+
}
|
|
512
|
+
const node = addDependency(this);
|
|
513
|
+
this._refresh();
|
|
514
|
+
if (node !== undefined) {
|
|
515
|
+
node._version = this._version;
|
|
516
|
+
}
|
|
517
|
+
if (this._flags & HAS_ERROR) {
|
|
518
|
+
throw this._value;
|
|
519
|
+
}
|
|
520
|
+
return this._value;
|
|
521
|
+
},
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
interface ReadonlySignal<T = any> extends Signal<T> {
|
|
525
|
+
readonly value: T;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function computed<T>(compute: () => T): ReadonlySignal<T> {
|
|
529
|
+
// @ts-ignore
|
|
530
|
+
return new Computed(compute);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function cleanupEffect(effect: Effect) {
|
|
534
|
+
const cleanup = effect._cleanup;
|
|
535
|
+
effect._cleanup = undefined;
|
|
536
|
+
|
|
537
|
+
if (typeof cleanup === "function") {
|
|
538
|
+
/*@__INLINE__**/ startBatch();
|
|
539
|
+
|
|
540
|
+
// Run cleanup functions always outside of any context.
|
|
541
|
+
const prevContext = evalContext;
|
|
542
|
+
evalContext = undefined;
|
|
543
|
+
try {
|
|
544
|
+
cleanup();
|
|
545
|
+
} catch (err) {
|
|
546
|
+
effect._flags &= ~RUNNING;
|
|
547
|
+
effect._flags |= DISPOSED;
|
|
548
|
+
disposeEffect(effect);
|
|
549
|
+
throw err;
|
|
550
|
+
} finally {
|
|
551
|
+
evalContext = prevContext;
|
|
552
|
+
endBatch();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
function disposeEffect(effect: Effect) {
|
|
558
|
+
for (
|
|
559
|
+
let node = effect._sources;
|
|
560
|
+
node !== undefined;
|
|
561
|
+
node = node._nextSource
|
|
562
|
+
) {
|
|
563
|
+
node._source._unsubscribe(node);
|
|
564
|
+
}
|
|
565
|
+
effect._compute = undefined;
|
|
566
|
+
effect._sources = undefined;
|
|
567
|
+
|
|
568
|
+
cleanupEffect(effect);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function endEffect(this: Effect, prevContext?: Computed | Effect) {
|
|
572
|
+
if (evalContext !== this) {
|
|
573
|
+
throw new Error("Out-of-order effect");
|
|
574
|
+
}
|
|
575
|
+
cleanupSources(this);
|
|
576
|
+
evalContext = prevContext;
|
|
577
|
+
|
|
578
|
+
this._flags &= ~RUNNING;
|
|
579
|
+
if (this._flags & DISPOSED) {
|
|
580
|
+
disposeEffect(this);
|
|
581
|
+
}
|
|
582
|
+
endBatch();
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
declare class Effect {
|
|
586
|
+
_compute?: () => unknown;
|
|
587
|
+
_cleanup?: unknown;
|
|
588
|
+
_sources?: Node;
|
|
589
|
+
_nextBatchedEffect?: Effect;
|
|
590
|
+
_flags: number;
|
|
591
|
+
|
|
592
|
+
constructor(compute: () => void);
|
|
593
|
+
|
|
594
|
+
_callback(): void;
|
|
595
|
+
_start(): () => void;
|
|
596
|
+
_notify(): void;
|
|
597
|
+
_dispose(): void;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function Effect(this: Effect, compute: () => void) {
|
|
601
|
+
this._compute = compute;
|
|
602
|
+
this._cleanup = undefined;
|
|
603
|
+
this._sources = undefined;
|
|
604
|
+
this._nextBatchedEffect = undefined;
|
|
605
|
+
this._flags = TRACKING;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
Effect.prototype._callback = function () {
|
|
609
|
+
const finish = this._start();
|
|
610
|
+
try {
|
|
611
|
+
if (!(this._flags & DISPOSED) && this._compute !== undefined) {
|
|
612
|
+
this._cleanup = this._compute();
|
|
613
|
+
}
|
|
614
|
+
} finally {
|
|
615
|
+
finish();
|
|
616
|
+
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
Effect.prototype._start = function () {
|
|
620
|
+
if (this._flags & RUNNING) {
|
|
621
|
+
cycleDetected();
|
|
622
|
+
}
|
|
623
|
+
this._flags |= RUNNING;
|
|
624
|
+
this._flags &= ~DISPOSED;
|
|
625
|
+
cleanupEffect(this);
|
|
626
|
+
prepareSources(this);
|
|
627
|
+
|
|
628
|
+
/*@__INLINE__**/ startBatch();
|
|
629
|
+
const prevContext = evalContext;
|
|
630
|
+
evalContext = this;
|
|
631
|
+
return endEffect.bind(this, prevContext);
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
Effect.prototype._notify = function () {
|
|
635
|
+
if (!(this._flags & NOTIFIED)) {
|
|
636
|
+
this._flags |= NOTIFIED;
|
|
637
|
+
this._nextBatchedEffect = batchedEffect;
|
|
638
|
+
batchedEffect = this;
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
Effect.prototype._dispose = function () {
|
|
643
|
+
this._flags |= DISPOSED;
|
|
644
|
+
|
|
645
|
+
if (!(this._flags & RUNNING)) {
|
|
646
|
+
disposeEffect(this);
|
|
647
|
+
}
|
|
648
|
+
};
|
|
649
|
+
|
|
650
|
+
function effect(compute: () => unknown): () => void {
|
|
651
|
+
const effect = new Effect(compute);
|
|
652
|
+
try {
|
|
653
|
+
effect._callback();
|
|
654
|
+
} catch (err) {
|
|
655
|
+
effect._dispose();
|
|
656
|
+
throw err;
|
|
657
|
+
}
|
|
658
|
+
// Return a bound function instead of a wrapper like `() => effect._dispose()`,
|
|
659
|
+
// because bound functions seem to be just as fast and take up a lot less memory.
|
|
660
|
+
return effect._dispose.bind(effect);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
export { signal, computed, effect, batch, Signal, type ReadonlySignal };
|