subay 0.0.7 → 0.0.10
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 +543 -239
- package/README.zh.md +682 -377
- package/examples/todo-app.html +286 -0
- package/lib/h.d.ts.map +1 -1
- package/lib/h.js +50 -32
- package/lib/h.js.map +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.dev.js +3 -0
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/name.d.ts +2 -0
- package/lib/name.d.ts.map +1 -0
- package/lib/name.js +58 -0
- package/lib/name.js.map +1 -0
- package/lib/s.d.ts +12 -1
- package/lib/s.d.ts.map +1 -1
- package/lib/s.js +62 -64
- package/lib/s.js.map +1 -1
- package/package.json +13 -8
- package/src/h.ts +56 -42
- package/src/index.ts +0 -2
- package/src/name.ts +60 -0
- package/src/s.ts +96 -95
package/src/h.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
|
-
/** @format */
|
|
2
|
-
|
|
3
1
|
import { cleanup, isReactive, root, S, sample } from './s.js';
|
|
4
2
|
|
|
5
3
|
const enum InterNodeType {
|
|
6
4
|
Text = 1,
|
|
7
5
|
Element = 2,
|
|
8
|
-
Attribute = 3,
|
|
9
6
|
}
|
|
10
7
|
|
|
11
8
|
type InterTreeNode = InterTextNode | InterElementNode;
|
|
@@ -25,9 +22,8 @@ type InterElementNode = {
|
|
|
25
22
|
};
|
|
26
23
|
|
|
27
24
|
type InterAttributeNode = {
|
|
28
|
-
_type: InterNodeType.Attribute;
|
|
29
25
|
_name: Array<string | number>;
|
|
30
|
-
_value: Array<string | number>;
|
|
26
|
+
_value: Array<string | number | true>;
|
|
31
27
|
};
|
|
32
28
|
|
|
33
29
|
const cache = new Map<TemplateStringsArray, InterTreeNode[]>();
|
|
@@ -66,7 +62,7 @@ const parseText = (data: string) => {
|
|
|
66
62
|
return result;
|
|
67
63
|
};
|
|
68
64
|
|
|
69
|
-
const unparseText = (data: Array<string | number>, values: any[]) => {
|
|
65
|
+
const unparseText = (data: Array<string | number | boolean>, values: any[]) => {
|
|
70
66
|
if (data.length === 1) {
|
|
71
67
|
if (typeof data[0] === 'number') {
|
|
72
68
|
return values[data[0]!];
|
|
@@ -122,7 +118,7 @@ const parse = (
|
|
|
122
118
|
) {
|
|
123
119
|
do
|
|
124
120
|
remove_and_dec_index: {
|
|
125
|
-
if (item.nodeType === Node.ELEMENT_NODE) {
|
|
121
|
+
if (item.nodeType === 1 /* Node.ELEMENT_NODE*/) {
|
|
126
122
|
const el = item as Element;
|
|
127
123
|
const tag = parseText(el.tagName.toLowerCase());
|
|
128
124
|
const isVariableElem = tag.length > 1 || typeof tag[0] === 'number';
|
|
@@ -147,9 +143,8 @@ const parse = (
|
|
|
147
143
|
}
|
|
148
144
|
return [
|
|
149
145
|
{
|
|
150
|
-
_type: InterNodeType.Attribute,
|
|
151
146
|
_name: name,
|
|
152
|
-
_value: value,
|
|
147
|
+
_value: value.length === 0 ? [true] : value,
|
|
153
148
|
} satisfies InterAttributeNode,
|
|
154
149
|
];
|
|
155
150
|
}),
|
|
@@ -161,7 +156,7 @@ const parse = (
|
|
|
161
156
|
if (!isVariableElem) {
|
|
162
157
|
break remove_and_dec_index;
|
|
163
158
|
}
|
|
164
|
-
} else if (item.nodeType === Node.TEXT_NODE) {
|
|
159
|
+
} else if (item.nodeType === 3 /*Node.TEXT_NODE*/) {
|
|
165
160
|
const data = (item as Text).data;
|
|
166
161
|
const result = parseText(data);
|
|
167
162
|
if (result.length > 0) {
|
|
@@ -191,29 +186,49 @@ const parse = (
|
|
|
191
186
|
const element = (
|
|
192
187
|
ns: string | null,
|
|
193
188
|
sourceNode: Element | null,
|
|
194
|
-
tagName: string | (() => string),
|
|
195
|
-
|
|
189
|
+
tagName: string | (() => string) | ((...props: any[]) => Node[]),
|
|
190
|
+
attrs: InterAttributeNode[],
|
|
191
|
+
values: any[],
|
|
192
|
+
child: () => Node[],
|
|
196
193
|
): Node[] => {
|
|
197
194
|
if (typeof tagName === 'function') {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return element(ns, null, realTagName, child);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
|
|
195
|
+
if (isReactive(tagName)) {
|
|
196
|
+
return fragment(() => {
|
|
197
|
+
const realTagName = tagName();
|
|
198
|
+
return element(ns, null, realTagName, attrs, values, child);
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
return fragment(() => {
|
|
202
|
+
return tagName(
|
|
203
|
+
...attrs.flatMap(({ _name: name, _value: value }) => {
|
|
204
|
+
if (name[0] === '...' && typeof name[1] === 'number') {
|
|
205
|
+
return unparseText(name.slice(1), values);
|
|
206
|
+
} else {
|
|
207
|
+
return unparseText(value, values);
|
|
208
|
+
}
|
|
209
|
+
}),
|
|
210
|
+
child,
|
|
211
|
+
) as Node[];
|
|
212
|
+
});
|
|
213
|
+
}
|
|
205
214
|
}
|
|
206
215
|
if (typeof tagName !== 'string') {
|
|
207
216
|
return [];
|
|
208
217
|
}
|
|
209
218
|
const el = sourceNode || (ns ? document.createElementNS(ns, tagName) : document.createElement(tagName));
|
|
210
219
|
S(() => {
|
|
211
|
-
const
|
|
220
|
+
for (const { _name: name, _value: value } of attrs) {
|
|
221
|
+
if (name[0] === '...' && typeof name[1] === 'number') {
|
|
222
|
+
spreadAttrs(el, unparseText(name.slice(1), values));
|
|
223
|
+
} else {
|
|
224
|
+
attribute(el, unparseText(name, values), unparseText(value, values));
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const subnodes = child();
|
|
212
228
|
if (subnodes.length === 0) {
|
|
213
229
|
return;
|
|
214
230
|
}
|
|
215
231
|
if (!el.firstChild) {
|
|
216
|
-
// @domact
|
|
217
232
|
el.append(...subnodes);
|
|
218
233
|
} else {
|
|
219
234
|
for (let left: Element | null = el.firstElementChild, rightIndex = 0; rightIndex < subnodes.length; rightIndex++) {
|
|
@@ -221,7 +236,6 @@ const element = (
|
|
|
221
236
|
if (left === right) {
|
|
222
237
|
left = left.nextElementSibling;
|
|
223
238
|
} else {
|
|
224
|
-
// @domact
|
|
225
239
|
el.insertBefore(right, left);
|
|
226
240
|
}
|
|
227
241
|
}
|
|
@@ -230,20 +244,21 @@ const element = (
|
|
|
230
244
|
cleanup(() => el.remove());
|
|
231
245
|
return [el];
|
|
232
246
|
};
|
|
233
|
-
|
|
234
|
-
const fragment = (child: (
|
|
235
|
-
const firstNode =
|
|
247
|
+
let fragmentHeadNode: null | Comment = null;
|
|
248
|
+
const fragment = (child: () => Node[]): Node[] => {
|
|
249
|
+
const firstNode = new Comment('');
|
|
236
250
|
cleanup(() => firstNode.remove());
|
|
237
251
|
const tempArray: Node[] = [firstNode];
|
|
238
252
|
S(() => {
|
|
239
|
-
|
|
253
|
+
fragmentHeadNode = firstNode;
|
|
254
|
+
const c = child();
|
|
240
255
|
const carr = Array.isArray(c) ? c : [c];
|
|
241
256
|
|
|
242
257
|
const nodified = carr.map((c) => {
|
|
243
258
|
if (c instanceof Node) {
|
|
244
259
|
return c;
|
|
245
260
|
}
|
|
246
|
-
const node =
|
|
261
|
+
const node = new Text(c);
|
|
247
262
|
cleanup(() => node.remove());
|
|
248
263
|
return node;
|
|
249
264
|
});
|
|
@@ -373,7 +388,8 @@ interface MapState<T> {
|
|
|
373
388
|
_arrayValue: T[];
|
|
374
389
|
}
|
|
375
390
|
export const map = <T>(array: () => T[], f: (item: T) => Node[]): Node[] => {
|
|
376
|
-
return fragment((
|
|
391
|
+
return fragment(() => {
|
|
392
|
+
const headNode = fragmentHeadNode;
|
|
377
393
|
cleanup(() => {
|
|
378
394
|
mapState()._itemDestroyer.forEach((f) => f());
|
|
379
395
|
});
|
|
@@ -442,6 +458,7 @@ const isPropertyOf = (domEl: HTMLElement | SVGElement, traitName: string) => {
|
|
|
442
458
|
}
|
|
443
459
|
return true;
|
|
444
460
|
};
|
|
461
|
+
|
|
445
462
|
const attribute = (node: Element, key: string | (() => string), val: string | (() => any)) => {
|
|
446
463
|
if (typeof key !== 'function' && !isReactive(val)) {
|
|
447
464
|
if (!node) {
|
|
@@ -458,7 +475,7 @@ const attribute = (node: Element, key: string | (() => string), val: string | ((
|
|
|
458
475
|
(node as any)[key] = preValue;
|
|
459
476
|
});
|
|
460
477
|
} else {
|
|
461
|
-
node.setAttribute(key, val);
|
|
478
|
+
node.setAttribute(key, String(val));
|
|
462
479
|
cleanup(() => {
|
|
463
480
|
node.removeAttribute(key);
|
|
464
481
|
});
|
|
@@ -483,33 +500,30 @@ const tree2dom = (ns: string | null, node: InterTreeNode, values: any[]): Node[]
|
|
|
483
500
|
if (node._type === InterNodeType.Text) {
|
|
484
501
|
return node._data.flatMap((part) => {
|
|
485
502
|
if (typeof part === 'string') {
|
|
486
|
-
const textNode =
|
|
503
|
+
const textNode = new Text(part);
|
|
487
504
|
cleanup(() => textNode.remove());
|
|
488
505
|
return textNode;
|
|
489
506
|
} else if (typeof part === 'number') {
|
|
490
507
|
const value = values[part];
|
|
491
508
|
if (typeof value === 'function') {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
509
|
+
if (isReactive(value)) {
|
|
510
|
+
return fragment(() => value());
|
|
511
|
+
} else {
|
|
512
|
+
return fragment(value);
|
|
513
|
+
}
|
|
514
|
+
} else if (typeof value !== 'object') {
|
|
515
|
+
const textNode = new Text(String(value));
|
|
495
516
|
cleanup(() => textNode.remove());
|
|
496
517
|
return textNode;
|
|
497
518
|
}
|
|
519
|
+
return value;
|
|
498
520
|
}
|
|
499
|
-
return [];
|
|
500
521
|
});
|
|
501
522
|
} else {
|
|
502
523
|
const { _attrs: attrs, _content: content, _tag: tag, _sourceNode: sourceNode } = node;
|
|
503
524
|
const tagValue = unparseText(tag, values);
|
|
504
525
|
const currentNs = node._ns ?? ns;
|
|
505
|
-
return element(currentNs, sourceNode, tagValue, (
|
|
506
|
-
for (const { _name: name, _value: value } of attrs) {
|
|
507
|
-
if (name[0] === '...' && typeof name[1] === 'number') {
|
|
508
|
-
spreadAttrs(node, unparseText(name.slice(1), values));
|
|
509
|
-
} else {
|
|
510
|
-
attribute(node, unparseText(name, values), unparseText(value, values));
|
|
511
|
-
}
|
|
512
|
-
}
|
|
526
|
+
return element(currentNs, sourceNode, tagValue, attrs, values, () => {
|
|
513
527
|
return content.flatMap((t) => tree2dom(currentNs, t, values));
|
|
514
528
|
});
|
|
515
529
|
}
|
package/src/index.ts
CHANGED
package/src/name.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const url2src = new Map<string, string>();
|
|
2
|
+
const getLeftName = function getLeftName() {
|
|
3
|
+
const stack = new Error().stack;
|
|
4
|
+
if (!stack) {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
const urlRegex = /((?:\w+:\/\/)[-a-zA-Z0-9:@;?&=\/%\+\.\*!'\(\),\$_\{\}\^~\[\]`#|]+):(\d+):(\d+)/;
|
|
8
|
+
const lines = stack.split('\n');
|
|
9
|
+
|
|
10
|
+
const currentLineIndex = lines.findIndex((line) => {
|
|
11
|
+
return line.includes(import.meta.url) && line.includes(getLeftName.name);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (currentLineIndex > -1) {
|
|
15
|
+
const offset = 3;
|
|
16
|
+
const line = lines[currentLineIndex + offset];
|
|
17
|
+
if (!line) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
const location = line.match(urlRegex);
|
|
21
|
+
if (!location) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const url = location[1];
|
|
25
|
+
const lineNo = location[2];
|
|
26
|
+
const colNo = location[3];
|
|
27
|
+
if (!url || !lineNo || !colNo) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
let src: string;
|
|
31
|
+
if (url2src.has(url)) {
|
|
32
|
+
src = url2src.get(url)!;
|
|
33
|
+
} else {
|
|
34
|
+
const xhr = new XMLHttpRequest();
|
|
35
|
+
xhr.open('GET', url, false);
|
|
36
|
+
xhr.send();
|
|
37
|
+
src = xhr.responseText;
|
|
38
|
+
url2src.set(url, src);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const reg = new RegExp(`(?:.*\r?\n){${Number(lineNo) - 1}}.{${Number(colNo) - 1}}`);
|
|
42
|
+
const segment = src.match(reg);
|
|
43
|
+
if (!segment) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
return segment[0].match(/(\w+)\s*=\s*$/)?.[1];
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export const namedFunc = <F extends (...args: any[]) => any>(f: F, fallback?: string): F => {
|
|
52
|
+
const leftName = getLeftName() ?? fallback;
|
|
53
|
+
if (!leftName) {
|
|
54
|
+
return f;
|
|
55
|
+
}
|
|
56
|
+
const g = new Function(`return function ${leftName} (...args) {return ${leftName}._(...args)}`)();
|
|
57
|
+
g._ = f;
|
|
58
|
+
Object.setPrototypeOf(g, f);
|
|
59
|
+
return g;
|
|
60
|
+
};
|
package/src/s.ts
CHANGED
|
@@ -1,167 +1,169 @@
|
|
|
1
|
-
|
|
2
|
-
(): void;
|
|
3
|
-
_whoIOwn: Array<IUpdater>;
|
|
4
|
-
_whoINeed: Array<ITrigger>;
|
|
5
|
-
_dirty: boolean;
|
|
6
|
-
_onCleanupCallback: Array<() => void>;
|
|
7
|
-
}
|
|
1
|
+
declare const __SUBAY_DEV__: boolean | undefined;
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
_whoNeedMe: Array<IUpdater>;
|
|
11
|
-
_data: IO<any>;
|
|
12
|
-
_pendingValue: any;
|
|
13
|
-
}
|
|
3
|
+
const namedFunc = typeof __SUBAY_DEV__ === 'boolean' && __SUBAY_DEV__ ? (await import('./name.js')).namedFunc : <T>(f: T) => f;
|
|
14
4
|
|
|
15
5
|
interface IS<T> {
|
|
16
|
-
(): T;
|
|
6
|
+
(force?: typeof Yes): T;
|
|
7
|
+
_whoIOwn: Array<IS<any>>;
|
|
8
|
+
_whoINeed: Set<IO<any> | IS<any>>;
|
|
9
|
+
_whoNeedMe: Array<IS<any>>;
|
|
10
|
+
_dirty: boolean;
|
|
11
|
+
_onCleanupCallback: Array<() => void>;
|
|
12
|
+
$o: number;
|
|
13
|
+
_lastAccess: number;
|
|
17
14
|
}
|
|
18
15
|
|
|
16
|
+
type IPartialS<T> = {
|
|
17
|
+
(force?: typeof Yes): T;
|
|
18
|
+
_whoIOwn?: Array<IS<any>>;
|
|
19
|
+
_whoINeed?: Set<IO<any> | IS<any>>;
|
|
20
|
+
_whoNeedMe?: Array<IS<any>>;
|
|
21
|
+
_dirty?: boolean;
|
|
22
|
+
_onCleanupCallback?: Array<() => void>;
|
|
23
|
+
$o?: number;
|
|
24
|
+
_lastAccess?: number;
|
|
25
|
+
};
|
|
26
|
+
|
|
19
27
|
interface IO<T> {
|
|
20
28
|
(): T;
|
|
21
29
|
(nextValue: T): T;
|
|
30
|
+
_whoNeedMe: Array<IS<any>>;
|
|
31
|
+
_pendingValue: any;
|
|
32
|
+
$o: number;
|
|
22
33
|
}
|
|
23
34
|
|
|
24
|
-
let currentUpdate:
|
|
35
|
+
let currentUpdate: IS<any> | undefined = undefined;
|
|
25
36
|
const NoValue: unique symbol = Symbol();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class Computation extends Reactive {}
|
|
31
|
-
const cProto = new Computation();
|
|
32
|
-
const oProto = new Observable();
|
|
37
|
+
const Yes: unique symbol = Symbol();
|
|
38
|
+
let triggerQueue: Array<IO<any>> | undefined = undefined;
|
|
39
|
+
const fun2s = new WeakMap<(...args: any[]) => any, IS<any>>();
|
|
40
|
+
let clock = 0;
|
|
33
41
|
|
|
34
42
|
export const root = <T>(fn: (destroy: () => void) => T): T => {
|
|
35
43
|
const backup = currentUpdate;
|
|
36
|
-
const rootUpdate:
|
|
37
|
-
|
|
44
|
+
const rootUpdate: IS<any> = (() => {}) as any;
|
|
45
|
+
resetS(rootUpdate);
|
|
38
46
|
rootUpdate._dirty = false;
|
|
39
47
|
currentUpdate = rootUpdate;
|
|
40
48
|
const result = fn(() => {
|
|
41
|
-
|
|
49
|
+
recycleS(rootUpdate);
|
|
42
50
|
currentUpdate = undefined;
|
|
43
51
|
});
|
|
44
52
|
currentUpdate = backup;
|
|
45
53
|
return result;
|
|
46
54
|
};
|
|
47
55
|
export const S = <T>(fn: (pv: T | undefined) => T, value?: T): IS<T> => {
|
|
48
|
-
const data = (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
56
|
+
const data: IS<T> = namedFunc(
|
|
57
|
+
resetS((force?: typeof Yes) => {
|
|
58
|
+
if (force === Yes || data._dirty) {
|
|
59
|
+
data._lastAccess = clock;
|
|
60
|
+
const backup = currentUpdate;
|
|
61
|
+
recycleS(data);
|
|
62
|
+
data._dirty = false;
|
|
63
|
+
currentUpdate = data;
|
|
64
|
+
value = fn(value);
|
|
65
|
+
currentUpdate = backup;
|
|
66
|
+
} else if (currentUpdate && !currentUpdate._whoINeed.has(data)) {
|
|
67
|
+
currentUpdate._whoINeed.add(data);
|
|
68
|
+
data._whoNeedMe.push(currentUpdate);
|
|
69
|
+
}
|
|
70
|
+
return value as T;
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
data.$o = 0xc03;
|
|
74
|
+
data._lastAccess = clock++;
|
|
65
75
|
if (currentUpdate) {
|
|
66
|
-
currentUpdate._whoIOwn.push(
|
|
76
|
+
currentUpdate._whoIOwn.push(data);
|
|
67
77
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
fun2s.set(fn, update);
|
|
78
|
+
data(Yes);
|
|
79
|
+
fun2s.set(fn, data);
|
|
71
80
|
return data;
|
|
72
81
|
};
|
|
73
82
|
export const o = <T>(value: T): IO<T> => {
|
|
74
|
-
function
|
|
75
|
-
function
|
|
76
|
-
function
|
|
83
|
+
function d(): T;
|
|
84
|
+
function d(nextValue: T): T;
|
|
85
|
+
function d(nextValue?: T) {
|
|
77
86
|
if (arguments.length === 0) {
|
|
78
|
-
if (currentUpdate) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
if (!trigger._whoNeedMe.includes(currentUpdate)) {
|
|
83
|
-
trigger._whoNeedMe.push(currentUpdate);
|
|
84
|
-
}
|
|
87
|
+
if (currentUpdate && !currentUpdate._whoINeed.has(data)) {
|
|
88
|
+
currentUpdate._whoINeed.add(data);
|
|
89
|
+
data._whoNeedMe.push(currentUpdate);
|
|
85
90
|
}
|
|
86
91
|
return value;
|
|
87
92
|
}
|
|
88
|
-
|
|
93
|
+
clock++;
|
|
89
94
|
if (triggerQueue) {
|
|
90
|
-
if (
|
|
91
|
-
triggerQueue.push(
|
|
95
|
+
if (data._pendingValue === NoValue) {
|
|
96
|
+
triggerQueue.push(data);
|
|
92
97
|
}
|
|
93
|
-
|
|
98
|
+
(data as IO<T>)._pendingValue = nextValue as T;
|
|
94
99
|
return nextValue;
|
|
95
100
|
}
|
|
96
|
-
|
|
97
101
|
value = nextValue!;
|
|
98
|
-
|
|
99
102
|
const backup = currentUpdate;
|
|
100
103
|
currentUpdate = undefined;
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
104
|
+
const collection = new Set<IS<any>>();
|
|
105
|
+
const collect = (it: IS<any>, by: IS<any> | IO<any>): void => {
|
|
106
|
+
if (!it._whoINeed.has(by) || collection.has(it)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
collection.add(it);
|
|
110
|
+
it._whoNeedMe.forEach((i) => collect(i, it));
|
|
111
|
+
};
|
|
112
|
+
data._whoNeedMe.forEach((it) => collect(it, data));
|
|
113
|
+
const whoNeedMeLasttime = Array.from(collection);
|
|
105
114
|
whoNeedMeLasttime.forEach((u) => {
|
|
106
115
|
u._dirty = true;
|
|
107
116
|
});
|
|
108
|
-
|
|
109
|
-
whoNeedMeLasttime.forEach((u) => {
|
|
117
|
+
data._whoNeedMe = [];
|
|
118
|
+
whoNeedMeLasttime.sort(byLastAccess).forEach((u) => {
|
|
110
119
|
if (u._dirty) {
|
|
111
120
|
u();
|
|
112
121
|
}
|
|
113
122
|
});
|
|
114
|
-
|
|
115
123
|
currentUpdate = backup;
|
|
116
|
-
|
|
117
124
|
return value;
|
|
118
125
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
_data: data,
|
|
125
|
-
_pendingValue: NoValue,
|
|
126
|
-
};
|
|
127
|
-
|
|
126
|
+
const data = namedFunc(d as IO<T>);
|
|
127
|
+
data._whoNeedMe = [] as Array<IS<any>>;
|
|
128
|
+
data._pendingValue = NoValue;
|
|
129
|
+
data.$o = 0x0b5;
|
|
130
|
+
clock++;
|
|
128
131
|
return data;
|
|
129
132
|
};
|
|
130
|
-
const
|
|
131
|
-
|
|
133
|
+
const byLastAccess = (a: IS<any>, b: IS<any>) => a._lastAccess - b._lastAccess;
|
|
134
|
+
const recycleS = (update: IS<any>) => {
|
|
135
|
+
update._whoIOwn.forEach(recycleS);
|
|
132
136
|
update._dirty = false;
|
|
133
137
|
update._onCleanupCallback.forEach((f) => f());
|
|
134
|
-
|
|
138
|
+
resetS(update);
|
|
135
139
|
};
|
|
136
140
|
|
|
137
|
-
const
|
|
138
|
-
update._whoINeed =
|
|
141
|
+
const resetS = (update: IPartialS<any>) => {
|
|
142
|
+
update._whoINeed = new Set();
|
|
143
|
+
update._whoNeedMe = [];
|
|
139
144
|
update._whoIOwn = [];
|
|
140
145
|
update._onCleanupCallback = [];
|
|
146
|
+
return update as IS<any>;
|
|
141
147
|
};
|
|
142
148
|
|
|
143
149
|
export const cleanup = <T extends () => void>(f: T): T => {
|
|
144
|
-
|
|
145
|
-
currentUpdate._onCleanupCallback.push(f);
|
|
146
|
-
}
|
|
150
|
+
currentUpdate?._onCleanupCallback.push(f);
|
|
147
151
|
return f;
|
|
148
152
|
};
|
|
149
153
|
|
|
150
154
|
export const transaction = <T>(f: () => T): T => {
|
|
151
155
|
const fallback = triggerQueue;
|
|
152
|
-
triggerQueue = [] as
|
|
156
|
+
triggerQueue = [] as IO<any>[];
|
|
153
157
|
const result = f();
|
|
154
158
|
const workedQueue = triggerQueue;
|
|
155
159
|
triggerQueue = fallback;
|
|
156
|
-
|
|
157
160
|
workedQueue.forEach((t) => {
|
|
158
161
|
if (t._pendingValue !== NoValue) {
|
|
159
162
|
const pv = t._pendingValue;
|
|
160
163
|
t._pendingValue = NoValue;
|
|
161
|
-
t
|
|
164
|
+
t(pv);
|
|
162
165
|
}
|
|
163
166
|
});
|
|
164
|
-
|
|
165
167
|
return result;
|
|
166
168
|
};
|
|
167
169
|
|
|
@@ -184,10 +186,9 @@ export const subscribe = (f: () => void) => {
|
|
|
184
186
|
export const unsubscribe = (f: () => void) => {
|
|
185
187
|
const update = fun2s.get(f);
|
|
186
188
|
if (update) {
|
|
187
|
-
|
|
189
|
+
recycleS(update);
|
|
188
190
|
}
|
|
189
191
|
};
|
|
190
|
-
|
|
191
|
-
export const
|
|
192
|
-
export const
|
|
193
|
-
export const isReactive = (o: any): o is IO<any> | IS<any> => o instanceof Reactive;
|
|
192
|
+
export const isObservable = (o: any): o is IO<any> => o?.$o === 0x0b5;
|
|
193
|
+
export const isComputed = (o: any): o is IS<any> => o?.$o === 0xc03;
|
|
194
|
+
export const isReactive = (o: any): o is IO<any> | IS<any> => o?.$o;
|