@esportsplus/reactivity 0.30.1 → 0.30.3
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/build/compiler/object.js +9 -0
- package/build/reactive/array.js +24 -22
- package/build/reactive/object.js +3 -2
- package/build/system.js +4 -3
- package/package.json +3 -2
- package/src/compiler/object.ts +15 -0
- package/src/reactive/array.ts +27 -26
- package/src/reactive/object.ts +5 -2
- package/src/system.ts +5 -3
- package/tests/array.ts +657 -0
- package/tests/bench/array.ts +162 -0
- package/tests/bench/system.ts +230 -0
- package/tests/effects.ts +211 -0
- package/tests/nested.ts +293 -0
- package/tests/objects.ts +185 -0
- package/tests/primitives.ts +280 -0
- package/tests/reactive.ts +324 -0
- package/tests/system.ts +655 -0
- package/vitest.config.ts +18 -0
- package/test/arrays.ts +0 -146
- package/test/debug.ts +0 -7
- package/test/effects.ts +0 -168
- package/test/index.ts +0 -8
- package/test/nested.ts +0 -201
- package/test/objects.ts +0 -106
- package/test/primitives.ts +0 -87
- package/test/range.ts +0 -45
- package/test/vite.config.ts +0 -41
package/build/compiler/object.js
CHANGED
|
@@ -17,6 +17,15 @@ function analyzeProperty(prop, sourceFile) {
|
|
|
17
17
|
while (ts.isAsExpression(unwrapped) || ts.isTypeAssertionExpression(unwrapped) || ts.isParenthesizedExpression(unwrapped)) {
|
|
18
18
|
unwrapped = unwrapped.expression;
|
|
19
19
|
}
|
|
20
|
+
if (ts.isAsExpression(value) || ts.isTypeAssertionExpression(value)) {
|
|
21
|
+
let type = value.type;
|
|
22
|
+
if (ts.isArrayTypeNode(type) ||
|
|
23
|
+
(ts.isTypeReferenceNode(type) &&
|
|
24
|
+
ts.isIdentifier(type.typeName) &&
|
|
25
|
+
type.typeName.text === 'Array')) {
|
|
26
|
+
return { isStatic: false, key, type: TYPES.Array, valueText };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
20
29
|
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
|
|
21
30
|
return { isStatic: false, key, type: TYPES.Computed, valueText };
|
|
22
31
|
}
|
package/build/reactive/array.js
CHANGED
|
@@ -18,10 +18,10 @@ class ReactiveArray extends Array {
|
|
|
18
18
|
return read(this._length);
|
|
19
19
|
}
|
|
20
20
|
set $length(value) {
|
|
21
|
-
if (value >
|
|
21
|
+
if (value > this.length) {
|
|
22
22
|
throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
|
|
23
23
|
}
|
|
24
|
-
this.splice(value,
|
|
24
|
+
this.splice(value, this.length);
|
|
25
25
|
}
|
|
26
26
|
$set(i, value) {
|
|
27
27
|
let prev = this[i];
|
|
@@ -29,7 +29,7 @@ class ReactiveArray extends Array {
|
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
31
|
this[i] = value;
|
|
32
|
-
if (i >=
|
|
32
|
+
if (i >= this.length) {
|
|
33
33
|
write(this._length, i + 1);
|
|
34
34
|
}
|
|
35
35
|
this.dispatch('set', { index: i, item: value });
|
|
@@ -55,7 +55,7 @@ class ReactiveArray extends Array {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
if (added.length) {
|
|
58
|
-
write(this._length,
|
|
58
|
+
write(this._length, this.length);
|
|
59
59
|
this.dispatch('concat', { items: added });
|
|
60
60
|
}
|
|
61
61
|
return this;
|
|
@@ -65,6 +65,7 @@ class ReactiveArray extends Array {
|
|
|
65
65
|
if (!listeners) {
|
|
66
66
|
return;
|
|
67
67
|
}
|
|
68
|
+
let dirty = false;
|
|
68
69
|
for (let i = 0, n = listeners.length; i < n; i++) {
|
|
69
70
|
let listener = listeners[i];
|
|
70
71
|
if (listener === null) {
|
|
@@ -73,15 +74,19 @@ class ReactiveArray extends Array {
|
|
|
73
74
|
try {
|
|
74
75
|
listener(value);
|
|
75
76
|
if (listener.once !== undefined) {
|
|
77
|
+
dirty = true;
|
|
76
78
|
listeners[i] = null;
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
catch {
|
|
82
|
+
dirty = true;
|
|
80
83
|
listeners[i] = null;
|
|
81
84
|
}
|
|
82
85
|
}
|
|
83
|
-
|
|
84
|
-
listeners.
|
|
86
|
+
if (dirty) {
|
|
87
|
+
while (listeners.length && listeners[listeners.length - 1] === null) {
|
|
88
|
+
listeners.pop();
|
|
89
|
+
}
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
dispose() {
|
|
@@ -120,14 +125,14 @@ class ReactiveArray extends Array {
|
|
|
120
125
|
let item = super.pop();
|
|
121
126
|
if (item !== undefined) {
|
|
122
127
|
dispose(item);
|
|
123
|
-
write(this._length,
|
|
128
|
+
write(this._length, this.length);
|
|
124
129
|
this.dispatch('pop', { item });
|
|
125
130
|
}
|
|
126
131
|
return item;
|
|
127
132
|
}
|
|
128
133
|
push(...items) {
|
|
129
134
|
if (!items.length) {
|
|
130
|
-
return
|
|
135
|
+
return this.length;
|
|
131
136
|
}
|
|
132
137
|
let length = super.push(...items);
|
|
133
138
|
write(this._length, length);
|
|
@@ -143,19 +148,19 @@ class ReactiveArray extends Array {
|
|
|
143
148
|
let item = super.shift();
|
|
144
149
|
if (item !== undefined) {
|
|
145
150
|
dispose(item);
|
|
146
|
-
write(this._length,
|
|
151
|
+
write(this._length, this.length);
|
|
147
152
|
this.dispatch('shift', { item });
|
|
148
153
|
}
|
|
149
154
|
return item;
|
|
150
155
|
}
|
|
151
156
|
sort(fn) {
|
|
152
|
-
let before = new Array(
|
|
153
|
-
for (let i = 0
|
|
157
|
+
let n = this.length, before = new Array(n);
|
|
158
|
+
for (let i = 0; i < n; i++) {
|
|
154
159
|
before[i] = this[i];
|
|
155
160
|
}
|
|
156
161
|
super.sort(fn);
|
|
157
|
-
let buckets = new Map(),
|
|
158
|
-
for (let i = 0
|
|
162
|
+
let buckets = new Map(), order = new Array(n);
|
|
163
|
+
for (let i = 0; i < n; i++) {
|
|
159
164
|
let value = before[i], list = buckets.get(value);
|
|
160
165
|
if (!list) {
|
|
161
166
|
buckets.set(value, [i]);
|
|
@@ -164,15 +169,12 @@ class ReactiveArray extends Array {
|
|
|
164
169
|
list.push(i);
|
|
165
170
|
}
|
|
166
171
|
}
|
|
167
|
-
for (let i = 0
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
for (let i = 0; i < n; i++) {
|
|
173
|
+
let list = buckets.get(this[i]);
|
|
174
|
+
order[i] = list.length === 1 ? list[0] : list[list.length - 1];
|
|
175
|
+
if (list.length > 1) {
|
|
176
|
+
list.pop();
|
|
172
177
|
}
|
|
173
|
-
let cursor = cursors.get(value) || 0;
|
|
174
|
-
order[i] = list[cursor];
|
|
175
|
-
cursors.set(value, cursor + 1);
|
|
176
178
|
}
|
|
177
179
|
this.dispatch('sort', { order });
|
|
178
180
|
return this;
|
|
@@ -180,7 +182,7 @@ class ReactiveArray extends Array {
|
|
|
180
182
|
splice(start, deleteCount = this.length, ...items) {
|
|
181
183
|
let removed = super.splice(start, deleteCount, ...items);
|
|
182
184
|
if (items.length > 0 || removed.length > 0) {
|
|
183
|
-
write(this._length,
|
|
185
|
+
write(this._length, this.length);
|
|
184
186
|
for (let i = 0, n = removed.length; i < n; i++) {
|
|
185
187
|
dispose(removed[i]);
|
|
186
188
|
}
|
package/build/reactive/object.js
CHANGED
|
@@ -8,8 +8,9 @@ class ReactiveObject {
|
|
|
8
8
|
if (data == null) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
let keys = Object.keys(data);
|
|
12
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
13
|
+
let key = keys[i], value = data[key], type = typeof value;
|
|
13
14
|
if (type === 'function') {
|
|
14
15
|
let node = this[COMPUTED](value);
|
|
15
16
|
defineProperty(this, key, {
|
package/build/system.js
CHANGED
|
@@ -112,7 +112,7 @@ function link(dep, sub) {
|
|
|
112
112
|
dep.subs = newLink;
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
function notify(computed, newState
|
|
115
|
+
function notify(computed, newState) {
|
|
116
116
|
let state = computed.state;
|
|
117
117
|
if ((state & STATE_NOTIFY_MASK) >= newState) {
|
|
118
118
|
return;
|
|
@@ -202,6 +202,7 @@ function stabilize() {
|
|
|
202
202
|
}
|
|
203
203
|
observer = o;
|
|
204
204
|
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
205
|
+
stabilizer = STABILIZER_SCHEDULED;
|
|
205
206
|
microtask(stabilize);
|
|
206
207
|
}
|
|
207
208
|
else {
|
|
@@ -337,7 +338,7 @@ const read = (node) => {
|
|
|
337
338
|
notified = true;
|
|
338
339
|
for (let i = 0; i <= heap_n; i++) {
|
|
339
340
|
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
340
|
-
notify(computed);
|
|
341
|
+
notify(computed, STATE_DIRTY);
|
|
341
342
|
}
|
|
342
343
|
}
|
|
343
344
|
}
|
|
@@ -380,11 +381,11 @@ const write = (signal, value) => {
|
|
|
380
381
|
if (signal.value === value) {
|
|
381
382
|
return;
|
|
382
383
|
}
|
|
383
|
-
notified = false;
|
|
384
384
|
signal.value = value;
|
|
385
385
|
if (signal.subs === null) {
|
|
386
386
|
return;
|
|
387
387
|
}
|
|
388
|
+
notified = false;
|
|
388
389
|
for (let link = signal.subs; link; link = link.nextSub) {
|
|
389
390
|
insertIntoHeap(link.sub);
|
|
390
391
|
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"devDependencies": {
|
|
7
7
|
"@esportsplus/typescript": "^0.29.0",
|
|
8
8
|
"@types/node": "^25.0.9",
|
|
9
|
-
"vite": "^7.3.1"
|
|
9
|
+
"vite": "^7.3.1",
|
|
10
|
+
"vitest": "^4.0.18"
|
|
10
11
|
},
|
|
11
12
|
"exports": {
|
|
12
13
|
".": {
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
},
|
|
36
37
|
"type": "module",
|
|
37
38
|
"types": "build/index.d.ts",
|
|
38
|
-
"version": "0.30.
|
|
39
|
+
"version": "0.30.3",
|
|
39
40
|
"scripts": {
|
|
40
41
|
"build": "tsc",
|
|
41
42
|
"build:test": "pnpm build && vite build --config test/vite.config.ts",
|
package/src/compiler/object.ts
CHANGED
|
@@ -55,6 +55,21 @@ function analyzeProperty(prop: ts.ObjectLiteralElementLike, sourceFile: ts.Sourc
|
|
|
55
55
|
unwrapped = unwrapped.expression;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
if (ts.isAsExpression(value) || ts.isTypeAssertionExpression(value)) {
|
|
59
|
+
let type = (value as ts.AsExpression).type;
|
|
60
|
+
|
|
61
|
+
if (
|
|
62
|
+
ts.isArrayTypeNode(type) ||
|
|
63
|
+
(
|
|
64
|
+
ts.isTypeReferenceNode(type) &&
|
|
65
|
+
ts.isIdentifier(type.typeName) &&
|
|
66
|
+
type.typeName.text === 'Array'
|
|
67
|
+
)
|
|
68
|
+
) {
|
|
69
|
+
return { isStatic: false, key, type: TYPES.Array, valueText };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
58
73
|
if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {
|
|
59
74
|
return { isStatic: false, key, type: TYPES.Computed, valueText };
|
|
60
75
|
}
|
package/src/reactive/array.ts
CHANGED
|
@@ -69,11 +69,11 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
set $length(value: number) {
|
|
72
|
-
if (value >
|
|
72
|
+
if (value > this.length) {
|
|
73
73
|
throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
this.splice(value,
|
|
76
|
+
this.splice(value, this.length);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
|
|
@@ -86,7 +86,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
86
86
|
|
|
87
87
|
this[i] = value;
|
|
88
88
|
|
|
89
|
-
if (i >=
|
|
89
|
+
if (i >= this.length) {
|
|
90
90
|
write(this._length, i + 1);
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -121,7 +121,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
121
121
|
}
|
|
122
122
|
|
|
123
123
|
if (added.length) {
|
|
124
|
-
write(this._length,
|
|
124
|
+
write(this._length, this.length);
|
|
125
125
|
this.dispatch('concat', { items: added });
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -135,6 +135,8 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
135
135
|
return;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
let dirty = false;
|
|
139
|
+
|
|
138
140
|
for (let i = 0, n = listeners.length; i < n; i++) {
|
|
139
141
|
let listener = listeners[i];
|
|
140
142
|
|
|
@@ -146,16 +148,20 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
146
148
|
listener(value);
|
|
147
149
|
|
|
148
150
|
if (listener.once !== undefined) {
|
|
151
|
+
dirty = true;
|
|
149
152
|
listeners[i] = null;
|
|
150
153
|
}
|
|
151
154
|
}
|
|
152
155
|
catch {
|
|
156
|
+
dirty = true;
|
|
153
157
|
listeners[i] = null;
|
|
154
158
|
}
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
|
|
158
|
-
listeners.
|
|
161
|
+
if (dirty) {
|
|
162
|
+
while (listeners.length && listeners[listeners.length - 1] === null) {
|
|
163
|
+
listeners.pop();
|
|
164
|
+
}
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
|
|
@@ -205,7 +211,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
205
211
|
|
|
206
212
|
if (item !== undefined) {
|
|
207
213
|
dispose(item);
|
|
208
|
-
write(this._length,
|
|
214
|
+
write(this._length, this.length);
|
|
209
215
|
|
|
210
216
|
this.dispatch('pop', { item });
|
|
211
217
|
}
|
|
@@ -215,7 +221,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
215
221
|
|
|
216
222
|
push(...items: T[]) {
|
|
217
223
|
if (!items.length) {
|
|
218
|
-
return
|
|
224
|
+
return this.length;
|
|
219
225
|
}
|
|
220
226
|
|
|
221
227
|
let length = super.push(...items);
|
|
@@ -238,7 +244,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
238
244
|
|
|
239
245
|
if (item !== undefined) {
|
|
240
246
|
dispose(item);
|
|
241
|
-
write(this._length,
|
|
247
|
+
write(this._length, this.length);
|
|
242
248
|
|
|
243
249
|
this.dispatch('shift', { item });
|
|
244
250
|
}
|
|
@@ -247,19 +253,19 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
247
253
|
}
|
|
248
254
|
|
|
249
255
|
sort(fn?: (a: T, b: T) => number) {
|
|
250
|
-
let
|
|
256
|
+
let n = this.length,
|
|
257
|
+
before = new Array(n) as T[];
|
|
251
258
|
|
|
252
|
-
for (let i = 0
|
|
259
|
+
for (let i = 0; i < n; i++) {
|
|
253
260
|
before[i] = this[i];
|
|
254
261
|
}
|
|
255
262
|
|
|
256
263
|
super.sort(fn);
|
|
257
264
|
|
|
258
265
|
let buckets = new Map<any, number[]>(),
|
|
259
|
-
|
|
260
|
-
order = new Array(this.length);
|
|
266
|
+
order = new Array(n);
|
|
261
267
|
|
|
262
|
-
for (let i = 0
|
|
268
|
+
for (let i = 0; i < n; i++) {
|
|
263
269
|
let value = before[i],
|
|
264
270
|
list = buckets.get(value);
|
|
265
271
|
|
|
@@ -271,19 +277,14 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
271
277
|
}
|
|
272
278
|
}
|
|
273
279
|
|
|
274
|
-
for (let i = 0
|
|
275
|
-
let
|
|
276
|
-
list = buckets.get(value);
|
|
277
|
-
|
|
278
|
-
if (!list) {
|
|
279
|
-
order[i] = i;
|
|
280
|
-
continue;
|
|
281
|
-
}
|
|
280
|
+
for (let i = 0; i < n; i++) {
|
|
281
|
+
let list = buckets.get(this[i])!;
|
|
282
282
|
|
|
283
|
-
|
|
283
|
+
order[i] = list.length === 1 ? list[0] : list[list.length - 1];
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
285
|
+
if (list.length > 1) {
|
|
286
|
+
list.pop();
|
|
287
|
+
}
|
|
287
288
|
}
|
|
288
289
|
|
|
289
290
|
this.dispatch('sort', { order });
|
|
@@ -295,7 +296,7 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
295
296
|
let removed = super.splice(start, deleteCount, ...items);
|
|
296
297
|
|
|
297
298
|
if (items.length > 0 || removed.length > 0) {
|
|
298
|
-
write(this._length,
|
|
299
|
+
write(this._length, this.length);
|
|
299
300
|
|
|
300
301
|
for (let i = 0, n = removed.length; i < n; i++) {
|
|
301
302
|
dispose(removed[i]);
|
package/src/reactive/object.ts
CHANGED
|
@@ -14,8 +14,11 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
let keys = Object.keys(data);
|
|
18
|
+
|
|
19
|
+
for (let i = 0, n = keys.length; i < n; i++) {
|
|
20
|
+
let key = keys[i],
|
|
21
|
+
value = data[key as keyof T],
|
|
19
22
|
type = typeof value;
|
|
20
23
|
|
|
21
24
|
if (type === 'function') {
|
package/src/system.ts
CHANGED
|
@@ -173,7 +173,7 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
function notify<T>(computed: Computed<T>, newState
|
|
176
|
+
function notify<T>(computed: Computed<T>, newState: number) {
|
|
177
177
|
let state = computed.state;
|
|
178
178
|
|
|
179
179
|
if ((state & STATE_NOTIFY_MASK) >= newState) {
|
|
@@ -298,6 +298,7 @@ function stabilize() {
|
|
|
298
298
|
observer = o;
|
|
299
299
|
|
|
300
300
|
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
301
|
+
stabilizer = STABILIZER_SCHEDULED;
|
|
301
302
|
microtask(stabilize);
|
|
302
303
|
}
|
|
303
304
|
else {
|
|
@@ -474,7 +475,7 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
|
|
|
474
475
|
|
|
475
476
|
for (let i = 0; i <= heap_n; i++) {
|
|
476
477
|
for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
|
|
477
|
-
notify(computed);
|
|
478
|
+
notify(computed, STATE_DIRTY);
|
|
478
479
|
}
|
|
479
480
|
}
|
|
480
481
|
}
|
|
@@ -535,13 +536,14 @@ const write = <T>(signal: Signal<T>, value: T) => {
|
|
|
535
536
|
return;
|
|
536
537
|
}
|
|
537
538
|
|
|
538
|
-
notified = false;
|
|
539
539
|
signal.value = value;
|
|
540
540
|
|
|
541
541
|
if (signal.subs === null) {
|
|
542
542
|
return;
|
|
543
543
|
}
|
|
544
544
|
|
|
545
|
+
notified = false;
|
|
546
|
+
|
|
545
547
|
for (let link: Link | null = signal.subs; link; link = link.nextSub) {
|
|
546
548
|
insertIntoHeap(link.sub);
|
|
547
549
|
}
|