@esportsplus/reactivity 0.33.0 → 0.36.0
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 +9 -7
- package/bench/reactive/array.bench.ts +17 -17
- package/build/compiler/array.d.ts +2 -2
- package/build/compiler/array.js +12 -54
- package/build/compiler/constants.d.ts +4 -4
- package/build/compiler/constants.js +20 -4
- package/build/compiler/index.js +25 -38
- package/build/compiler/object.d.ts +2 -2
- package/build/compiler/object.js +10 -21
- package/build/compiler/plugins/vite.d.ts +4 -2
- package/build/compiler/primitives.d.ts +6 -2
- package/build/compiler/primitives.js +35 -42
- package/build/compiler/types.d.ts +3 -1
- package/build/constants.d.ts +2 -2
- package/build/constants.js +2 -2
- package/build/reactive/array.d.ts +1 -1
- package/build/reactive/array.js +26 -15
- package/build/reactive/index.js +5 -14
- package/build/reactive/object.js +1 -1
- package/build/system.d.ts +10 -13
- package/build/system.js +105 -83
- package/build/types.d.ts +2 -7
- package/package.json +5 -5
- package/pnpm-workspace.yaml +3 -0
- package/src/compiler/array.ts +14 -64
- package/src/compiler/constants.ts +21 -5
- package/src/compiler/index.ts +39 -70
- package/src/compiler/object.ts +12 -29
- package/src/compiler/primitives.ts +55 -53
- package/src/compiler/types.ts +4 -1
- package/src/constants.ts +4 -4
- package/src/reactive/array.ts +33 -17
- package/src/reactive/index.ts +5 -17
- package/src/reactive/object.ts +1 -1
- package/src/system.ts +138 -101
- package/src/types.ts +2 -9
- package/test/async-computed.test.ts +2 -2
- package/test/compiler/compiler.test.ts +40 -23
- package/test/effects.test.ts +37 -1
- package/test/lib/uncaught.ts +26 -0
- package/test/reactive/array.test.ts +169 -99
- package/test/reactive/nested.test.ts +14 -14
- package/test/reactive/objects.test.ts +1 -1
- package/test/reactive/reactive.test.ts +49 -0
- package/test/system.test.ts +130 -22
package/build/system.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_EFFECT, STATE_ERROR, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
1
|
+
import { PACKAGE_NAME, SIGNAL, STABILIZER_DEFERRED, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_EFFECT, STATE_ERROR, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
|
|
2
2
|
import { isObject, isPromise } from '@esportsplus/utilities';
|
|
3
|
-
let asyncMeta = new WeakMap(), depth = 0, disposeHead = null, draining = false, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, pendingHead = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0, walkPoolHead = null, writes = 0;
|
|
3
|
+
let asyncMeta = new WeakMap(), depth = 0, disposeHead = null, draining = false, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, pendingHead = null, pulled = null, puller = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0, walkPoolHead = null, writes = 0;
|
|
4
|
+
function noop() {
|
|
5
|
+
}
|
|
4
6
|
function walkPop(walk) {
|
|
5
7
|
let prev = walk.prev;
|
|
6
8
|
walk.computed = null;
|
|
@@ -105,6 +107,9 @@ function insertIntoHeap(computed) {
|
|
|
105
107
|
heap.length = Math.max(height + 1, Math.ceil(heap.length * 2));
|
|
106
108
|
}
|
|
107
109
|
}
|
|
110
|
+
else if (height < heap_i) {
|
|
111
|
+
stabilizer = STABILIZER_RESCHEDULE;
|
|
112
|
+
}
|
|
108
113
|
}
|
|
109
114
|
function link(dep, sub) {
|
|
110
115
|
if (dep.rv === version) {
|
|
@@ -204,12 +209,24 @@ function pull(node) {
|
|
|
204
209
|
}
|
|
205
210
|
let o = observer;
|
|
206
211
|
observer = null;
|
|
212
|
+
pulled = node;
|
|
213
|
+
puller = o;
|
|
207
214
|
update(node);
|
|
208
215
|
observer = o;
|
|
216
|
+
pulled = null;
|
|
217
|
+
puller = null;
|
|
209
218
|
}
|
|
210
219
|
function propagate(computed) {
|
|
211
|
-
|
|
220
|
+
let c = computed.subs;
|
|
221
|
+
if (c === null) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
let skip = computed === pulled ? puller : null;
|
|
225
|
+
for (; c; c = c.nextSub) {
|
|
212
226
|
let s = c.sub, state = s.state;
|
|
227
|
+
if (s === skip) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
213
230
|
if (state & STATE_CHECK) {
|
|
214
231
|
s.state = state | STATE_DIRTY;
|
|
215
232
|
}
|
|
@@ -217,14 +234,10 @@ function propagate(computed) {
|
|
|
217
234
|
}
|
|
218
235
|
schedule();
|
|
219
236
|
}
|
|
220
|
-
function recompute(computed
|
|
221
|
-
if (
|
|
237
|
+
function recompute(computed) {
|
|
238
|
+
if (computed.state & STATE_IN_HEAP) {
|
|
222
239
|
deleteFromHeap(computed);
|
|
223
240
|
}
|
|
224
|
-
else {
|
|
225
|
-
computed.nextHeap = undefined;
|
|
226
|
-
computed.prevHeap = computed;
|
|
227
|
-
}
|
|
228
241
|
if (computed.cleanup) {
|
|
229
242
|
try {
|
|
230
243
|
cleanup(computed);
|
|
@@ -251,7 +264,7 @@ function recompute(computed, del) {
|
|
|
251
264
|
depth--;
|
|
252
265
|
version++;
|
|
253
266
|
observer = o;
|
|
254
|
-
computed.state = STATE_COMPUTED | flags;
|
|
267
|
+
computed.state = STATE_COMPUTED | flags | (computed.state & STATE_IN_HEAP);
|
|
255
268
|
computed.gv = w;
|
|
256
269
|
let depsTail = computed.depsTail, remove = depsTail ? depsTail.nextDep : computed.deps;
|
|
257
270
|
if (remove) {
|
|
@@ -287,46 +300,44 @@ function recompute(computed, del) {
|
|
|
287
300
|
propagate(computed);
|
|
288
301
|
}
|
|
289
302
|
}
|
|
303
|
+
if (!depth && stabilizer === STABILIZER_DEFERRED) {
|
|
304
|
+
stabilizer = STABILIZER_SCHEDULED;
|
|
305
|
+
microtask(stabilize);
|
|
306
|
+
}
|
|
290
307
|
}
|
|
291
308
|
function schedule() {
|
|
292
|
-
if (stabilizer
|
|
309
|
+
if (stabilizer !== STABILIZER_IDLE) {
|
|
293
310
|
return;
|
|
294
311
|
}
|
|
295
|
-
if (
|
|
312
|
+
if (depth) {
|
|
313
|
+
stabilizer = STABILIZER_DEFERRED;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
296
316
|
stabilizer = STABILIZER_SCHEDULED;
|
|
297
317
|
microtask(stabilize);
|
|
298
318
|
}
|
|
299
|
-
else if (stabilizer === STABILIZER_RUNNING) {
|
|
300
|
-
stabilizer = STABILIZER_RESCHEDULE;
|
|
301
|
-
}
|
|
302
319
|
}
|
|
303
320
|
function stabilize() {
|
|
304
321
|
let o = observer;
|
|
305
322
|
observer = null;
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
323
|
+
do {
|
|
324
|
+
stabilizer = STABILIZER_RUNNING;
|
|
325
|
+
for (heap_i = 0; heap_i <= heap_n; heap_i++) {
|
|
326
|
+
if (pendingHead !== null) {
|
|
327
|
+
drainPending();
|
|
328
|
+
}
|
|
329
|
+
let computed;
|
|
330
|
+
while ((computed = heap[heap_i]) !== undefined) {
|
|
331
|
+
recompute(computed);
|
|
332
|
+
}
|
|
310
333
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
while (computed !== undefined) {
|
|
314
|
-
let next = computed.nextHeap;
|
|
315
|
-
recompute(computed, false);
|
|
316
|
-
computed = next;
|
|
334
|
+
while (heap_n > 0 && heap[heap_n] === undefined) {
|
|
335
|
+
heap_n--;
|
|
317
336
|
}
|
|
318
|
-
|
|
319
|
-
while (
|
|
320
|
-
|
|
321
|
-
}
|
|
337
|
+
heap_i = 0;
|
|
338
|
+
} while (pendingHead !== null || stabilizer === STABILIZER_RESCHEDULE);
|
|
339
|
+
stabilizer = STABILIZER_IDLE;
|
|
322
340
|
observer = o;
|
|
323
|
-
if (stabilizer === STABILIZER_RESCHEDULE) {
|
|
324
|
-
stabilizer = STABILIZER_SCHEDULED;
|
|
325
|
-
microtask(stabilize);
|
|
326
|
-
}
|
|
327
|
-
else {
|
|
328
|
-
stabilizer = STABILIZER_IDLE;
|
|
329
|
-
}
|
|
330
341
|
}
|
|
331
342
|
function unlink(link) {
|
|
332
343
|
let dep = link.dep, nextDep = link.nextDep, nextSub = link.nextSub, prevSub = link.prevSub;
|
|
@@ -369,7 +380,7 @@ function update(root) {
|
|
|
369
380
|
resuming = true;
|
|
370
381
|
}
|
|
371
382
|
else if (node.state & STATE_DIRTY) {
|
|
372
|
-
recompute(node
|
|
383
|
+
recompute(node);
|
|
373
384
|
node.state &= ~STATE_NOTIFY_MASK;
|
|
374
385
|
}
|
|
375
386
|
else {
|
|
@@ -394,7 +405,7 @@ function update(root) {
|
|
|
394
405
|
continue;
|
|
395
406
|
}
|
|
396
407
|
if (node.state & STATE_DIRTY) {
|
|
397
|
-
recompute(node
|
|
408
|
+
recompute(node);
|
|
398
409
|
}
|
|
399
410
|
else {
|
|
400
411
|
node.gv = w;
|
|
@@ -414,18 +425,23 @@ function update(root) {
|
|
|
414
425
|
function makeAsyncComputed(factory) {
|
|
415
426
|
let error = signal(undefined), node = signal(undefined), pending = signal(false), v = 0;
|
|
416
427
|
let stop = effect(() => {
|
|
417
|
-
let
|
|
418
|
-
if (
|
|
428
|
+
let id = ++v, stale = () => {
|
|
429
|
+
if (pendingHead !== null) {
|
|
430
|
+
drainPending();
|
|
431
|
+
}
|
|
432
|
+
return id !== v || (factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK)) !== 0;
|
|
433
|
+
}, fail = (e) => {
|
|
434
|
+
if (!stale()) {
|
|
419
435
|
write(error, e === undefined ? new Error('reactivity: async computed rejected with undefined') : e);
|
|
420
436
|
write(pending, false);
|
|
421
437
|
}
|
|
422
|
-
},
|
|
438
|
+
}, result = read(factory);
|
|
423
439
|
if (isPromise(result)) {
|
|
424
|
-
if (
|
|
440
|
+
if (!stale()) {
|
|
425
441
|
write(pending, true);
|
|
426
442
|
}
|
|
427
443
|
result.then((value) => {
|
|
428
|
-
if (
|
|
444
|
+
if (!stale()) {
|
|
429
445
|
write(error, undefined);
|
|
430
446
|
write(node, value);
|
|
431
447
|
write(pending, false);
|
|
@@ -438,7 +454,7 @@ function makeAsyncComputed(factory) {
|
|
|
438
454
|
it.return?.();
|
|
439
455
|
});
|
|
440
456
|
let step = (r) => {
|
|
441
|
-
if (
|
|
457
|
+
if (stale()) {
|
|
442
458
|
return;
|
|
443
459
|
}
|
|
444
460
|
if (!r.done) {
|
|
@@ -451,7 +467,7 @@ function makeAsyncComputed(factory) {
|
|
|
451
467
|
write(pending, false);
|
|
452
468
|
}
|
|
453
469
|
};
|
|
454
|
-
if (
|
|
470
|
+
if (!stale()) {
|
|
455
471
|
write(pending, true);
|
|
456
472
|
}
|
|
457
473
|
untrack(() => it.next()).then(step, fail);
|
|
@@ -475,33 +491,15 @@ function makeAsyncComputed(factory) {
|
|
|
475
491
|
return wrapper;
|
|
476
492
|
}
|
|
477
493
|
function makeComputed(fn, eager = false) {
|
|
478
|
-
let self =
|
|
479
|
-
cleanup: null,
|
|
480
|
-
deps: null,
|
|
481
|
-
depsTail: null,
|
|
482
|
-
disposal: null,
|
|
483
|
-
equals: null,
|
|
484
|
-
error: null,
|
|
485
|
-
fn: fn,
|
|
486
|
-
gv: 0,
|
|
487
|
-
height: 0,
|
|
488
|
-
nextHeap: undefined,
|
|
489
|
-
prevHeap: null,
|
|
490
|
-
rv: 0,
|
|
491
|
-
state: STATE_COMPUTED,
|
|
492
|
-
subs: null,
|
|
493
|
-
subsTail: null,
|
|
494
|
-
value: undefined,
|
|
495
|
-
};
|
|
496
|
-
self.prevHeap = self;
|
|
494
|
+
let self = makeNode(fn);
|
|
497
495
|
if (observer) {
|
|
498
496
|
if (observer.depsTail === null) {
|
|
499
497
|
self.height = observer.height;
|
|
500
|
-
recompute(self
|
|
498
|
+
recompute(self);
|
|
501
499
|
}
|
|
502
500
|
else if (eager) {
|
|
503
501
|
self.height = observer.height + 1;
|
|
504
|
-
recompute(self
|
|
502
|
+
recompute(self);
|
|
505
503
|
}
|
|
506
504
|
else {
|
|
507
505
|
self.height = observer.height + 1;
|
|
@@ -512,14 +510,36 @@ function makeComputed(fn, eager = false) {
|
|
|
512
510
|
onCleanup(() => dispose(self));
|
|
513
511
|
}
|
|
514
512
|
else {
|
|
515
|
-
recompute(self
|
|
516
|
-
root.disposables++;
|
|
513
|
+
recompute(self);
|
|
517
514
|
if (scope) {
|
|
518
515
|
onCleanup(() => dispose(self));
|
|
519
516
|
}
|
|
520
517
|
}
|
|
521
518
|
return self;
|
|
522
519
|
}
|
|
520
|
+
function makeNode(fn) {
|
|
521
|
+
let self = {
|
|
522
|
+
cleanup: null,
|
|
523
|
+
deps: null,
|
|
524
|
+
depsTail: null,
|
|
525
|
+
disposal: null,
|
|
526
|
+
equals: null,
|
|
527
|
+
error: null,
|
|
528
|
+
fn,
|
|
529
|
+
gv: 0,
|
|
530
|
+
height: 0,
|
|
531
|
+
nextHeap: undefined,
|
|
532
|
+
pending: null,
|
|
533
|
+
prevHeap: null,
|
|
534
|
+
rv: 0,
|
|
535
|
+
state: STATE_COMPUTED,
|
|
536
|
+
subs: null,
|
|
537
|
+
subsTail: null,
|
|
538
|
+
value: undefined,
|
|
539
|
+
};
|
|
540
|
+
self.prevHeap = self;
|
|
541
|
+
return self;
|
|
542
|
+
}
|
|
523
543
|
const batch = (fn) => {
|
|
524
544
|
depth++;
|
|
525
545
|
try {
|
|
@@ -527,8 +547,9 @@ const batch = (fn) => {
|
|
|
527
547
|
}
|
|
528
548
|
finally {
|
|
529
549
|
depth--;
|
|
530
|
-
if (!depth) {
|
|
531
|
-
|
|
550
|
+
if (!depth && stabilizer === STABILIZER_DEFERRED) {
|
|
551
|
+
stabilizer = STABILIZER_SCHEDULED;
|
|
552
|
+
microtask(stabilize);
|
|
532
553
|
}
|
|
533
554
|
}
|
|
534
555
|
};
|
|
@@ -610,7 +631,7 @@ const effect = (fn, apply) => {
|
|
|
610
631
|
};
|
|
611
632
|
};
|
|
612
633
|
const flush = () => {
|
|
613
|
-
|
|
634
|
+
if (stabilizer === STABILIZER_SCHEDULED) {
|
|
614
635
|
stabilize();
|
|
615
636
|
}
|
|
616
637
|
};
|
|
@@ -671,26 +692,27 @@ const read = (node) => {
|
|
|
671
692
|
return node.value;
|
|
672
693
|
};
|
|
673
694
|
const root = (fn) => {
|
|
674
|
-
let c,
|
|
695
|
+
let c, o = observer, s = scope, self = null, tracking = fn.length, value;
|
|
675
696
|
observer = null;
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
697
|
+
try {
|
|
698
|
+
if (tracking) {
|
|
699
|
+
scope = self = makeNode(noop);
|
|
700
|
+
value = fn(c = () => dispose(self));
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
scope = null;
|
|
704
|
+
value = fn();
|
|
705
|
+
}
|
|
680
706
|
}
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
707
|
+
finally {
|
|
708
|
+
observer = o;
|
|
709
|
+
scope = s;
|
|
684
710
|
}
|
|
685
|
-
observer = o;
|
|
686
|
-
root.disposables = d;
|
|
687
|
-
scope = s;
|
|
688
711
|
if (c) {
|
|
689
712
|
onCleanup(c);
|
|
690
713
|
}
|
|
691
714
|
return value;
|
|
692
715
|
};
|
|
693
|
-
root.disposables = 0;
|
|
694
716
|
const signal = (value, equals = null) => {
|
|
695
717
|
return {
|
|
696
718
|
equals: equals,
|
package/build/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { ts } from '@esportsplus/typescript';
|
|
2
1
|
import { SIGNAL } from './constants.js';
|
|
3
2
|
import { ReactiveArray } from './reactive/index.js';
|
|
4
3
|
interface Computed<T> {
|
|
@@ -12,6 +11,7 @@ interface Computed<T> {
|
|
|
12
11
|
gv: number;
|
|
13
12
|
height: number;
|
|
14
13
|
nextHeap: Computed<unknown> | undefined;
|
|
14
|
+
pending: Signal<boolean> | null;
|
|
15
15
|
prevHeap: Computed<unknown>;
|
|
16
16
|
rv: number;
|
|
17
17
|
state: number;
|
|
@@ -56,9 +56,4 @@ type Signal<T> = {
|
|
|
56
56
|
type: typeof SIGNAL;
|
|
57
57
|
value: T;
|
|
58
58
|
};
|
|
59
|
-
|
|
60
|
-
changed: boolean;
|
|
61
|
-
code: string;
|
|
62
|
-
sourceFile: ts.SourceFile;
|
|
63
|
-
}
|
|
64
|
-
export type { Computed, ComputedResult, Link, Reactive, SelectorSignal, Settled, Signal, TransformResult };
|
|
59
|
+
export type { Computed, ComputedResult, Link, Reactive, SelectorSignal, Settled, Signal };
|
package/package.json
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
"@esportsplus/utilities": "^0.28.0"
|
|
5
5
|
},
|
|
6
6
|
"devDependencies": {
|
|
7
|
-
"@esportsplus/typescript": "^0.
|
|
8
|
-
"@types/node": "^26.
|
|
9
|
-
"vite": "^8.
|
|
10
|
-
"vitest": "^4.1.
|
|
7
|
+
"@esportsplus/typescript": "^0.32.0",
|
|
8
|
+
"@types/node": "^26.4.1",
|
|
9
|
+
"vite": "^8.2.2",
|
|
10
|
+
"vitest": "^4.1.11"
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"type": "module",
|
|
38
38
|
"types": "build/index.d.ts",
|
|
39
|
-
"version": "0.
|
|
39
|
+
"version": "0.36.0",
|
|
40
40
|
"scripts": {
|
|
41
41
|
"agent:bench": "vitest bench --run",
|
|
42
42
|
"agent:test": "tsc --noEmit && vitest run",
|
package/pnpm-workspace.yaml
CHANGED
package/src/compiler/array.ts
CHANGED
|
@@ -1,34 +1,18 @@
|
|
|
1
1
|
import { ts } from '@esportsplus/typescript';
|
|
2
|
-
import { ast
|
|
2
|
+
import { ast } from '@esportsplus/typescript/compiler';
|
|
3
3
|
import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
|
|
4
|
-
import {
|
|
5
|
-
import type { Bindings } from './types';
|
|
4
|
+
import { COMPOUND_OPERATORS, NAMESPACE, TYPES } from './constants';
|
|
5
|
+
import type { Bindings, IsReactiveCall } from './types';
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
type VisitContext = {
|
|
9
9
|
bindings: Bindings;
|
|
10
|
-
|
|
10
|
+
isReactiveCall: IsReactiveCall;
|
|
11
11
|
replacements: ReplacementIntent[];
|
|
12
12
|
sourceFile: ts.SourceFile;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
function isReactiveCall(checker: ts.TypeChecker | undefined, node: ts.Node): node is ts.CallExpression {
|
|
17
|
-
if (!ts.isCallExpression(node) || !ts.isIdentifier(node.expression)) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
let expr = node.expression;
|
|
22
|
-
|
|
23
|
-
// Use checker to verify symbol origin (handles re-exports)
|
|
24
|
-
if (checker) {
|
|
25
|
-
return imports.includes(checker, expr, PACKAGE_NAME, ENTRYPOINT);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Fallback without checker: match by name only
|
|
29
|
-
return expr.text === ENTRYPOINT;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
16
|
function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): string | null {
|
|
33
17
|
if (ts.isArrayTypeNode(typeNode)) {
|
|
34
18
|
return typeNode.elementType.getText(sourceFile);
|
|
@@ -47,42 +31,8 @@ function getElementTypeText(typeNode: ts.TypeNode, sourceFile: ts.SourceFile): s
|
|
|
47
31
|
return null;
|
|
48
32
|
}
|
|
49
33
|
|
|
50
|
-
function getOperator(kind: ts.SyntaxKind) {
|
|
51
|
-
switch (kind) {
|
|
52
|
-
case ts.SyntaxKind.PlusEqualsToken: return '+';
|
|
53
|
-
case ts.SyntaxKind.MinusEqualsToken: return '-';
|
|
54
|
-
case ts.SyntaxKind.AsteriskEqualsToken: return '*';
|
|
55
|
-
case ts.SyntaxKind.SlashEqualsToken: return '/';
|
|
56
|
-
case ts.SyntaxKind.PercentEqualsToken: return '%';
|
|
57
|
-
case ts.SyntaxKind.AsteriskAsteriskEqualsToken: return '**';
|
|
58
|
-
case ts.SyntaxKind.AmpersandEqualsToken: return '&';
|
|
59
|
-
case ts.SyntaxKind.BarEqualsToken: return '|';
|
|
60
|
-
case ts.SyntaxKind.CaretEqualsToken: return '^';
|
|
61
|
-
default: return '+';
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function isAssignmentOperator(kind: ts.SyntaxKind) {
|
|
66
|
-
return kind === ts.SyntaxKind.EqualsToken ||
|
|
67
|
-
kind === ts.SyntaxKind.PlusEqualsToken ||
|
|
68
|
-
kind === ts.SyntaxKind.MinusEqualsToken ||
|
|
69
|
-
kind === ts.SyntaxKind.AsteriskEqualsToken ||
|
|
70
|
-
kind === ts.SyntaxKind.SlashEqualsToken ||
|
|
71
|
-
kind === ts.SyntaxKind.PercentEqualsToken ||
|
|
72
|
-
kind === ts.SyntaxKind.AsteriskAsteriskEqualsToken ||
|
|
73
|
-
kind === ts.SyntaxKind.LessThanLessThanEqualsToken ||
|
|
74
|
-
kind === ts.SyntaxKind.GreaterThanGreaterThanEqualsToken ||
|
|
75
|
-
kind === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken ||
|
|
76
|
-
kind === ts.SyntaxKind.AmpersandEqualsToken ||
|
|
77
|
-
kind === ts.SyntaxKind.BarEqualsToken ||
|
|
78
|
-
kind === ts.SyntaxKind.CaretEqualsToken ||
|
|
79
|
-
kind === ts.SyntaxKind.BarBarEqualsToken ||
|
|
80
|
-
kind === ts.SyntaxKind.AmpersandAmpersandEqualsToken ||
|
|
81
|
-
kind === ts.SyntaxKind.QuestionQuestionEqualsToken;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
34
|
function visit(ctx: VisitContext, node: ts.Node): void {
|
|
85
|
-
if (isReactiveCall(
|
|
35
|
+
if (ctx.isReactiveCall(node) && node.arguments.length > 0) {
|
|
86
36
|
let arg = node.arguments[0],
|
|
87
37
|
expression = ts.isAsExpression(arg) ? arg.expression : arg;
|
|
88
38
|
|
|
@@ -105,7 +55,7 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
105
55
|
ctx.replacements.push({
|
|
106
56
|
node,
|
|
107
57
|
generate: (sf) => expression.elements.length > 0
|
|
108
|
-
? ` new ${NAMESPACE}.ReactiveArray${typeParam}(
|
|
58
|
+
? ` new ${NAMESPACE}.ReactiveArray${typeParam}(${expression.getText(sf)})`
|
|
109
59
|
: ` new ${NAMESPACE}.ReactiveArray${typeParam}()`
|
|
110
60
|
});
|
|
111
61
|
}
|
|
@@ -148,10 +98,10 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
148
98
|
parent = node.parent;
|
|
149
99
|
|
|
150
100
|
// arr.length = value OR arr.length += value
|
|
151
|
-
if (parent && ts.isBinaryExpression(parent) && parent.left === node &&
|
|
152
|
-
let op = parent.operatorToken.kind;
|
|
101
|
+
if (parent && ts.isBinaryExpression(parent) && parent.left === node && (parent.operatorToken.kind === ts.SyntaxKind.EqualsToken || COMPOUND_OPERATORS.has(parent.operatorToken.kind))) {
|
|
102
|
+
let op = COMPOUND_OPERATORS.get(parent.operatorToken.kind);
|
|
153
103
|
|
|
154
|
-
if (op ===
|
|
104
|
+
if (op === undefined) {
|
|
155
105
|
ctx.replacements.push({
|
|
156
106
|
node: parent,
|
|
157
107
|
generate: (sf) => `${expr.getText(sf)}.$length = ${parent.right.getText(sf)}`
|
|
@@ -160,7 +110,7 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
160
110
|
else {
|
|
161
111
|
ctx.replacements.push({
|
|
162
112
|
node: parent,
|
|
163
|
-
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${
|
|
113
|
+
generate: (sf) => `${expr.getText(sf)}.$length = ${expr.getText(sf)}.length ${op} ${parent.right.getText(sf)}`
|
|
164
114
|
});
|
|
165
115
|
}
|
|
166
116
|
}
|
|
@@ -220,7 +170,7 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
220
170
|
right = node.right;
|
|
221
171
|
|
|
222
172
|
// Unwrap "as" expressions: arr = [] as Type[]
|
|
223
|
-
while (ts.isAsExpression(right) || ts.
|
|
173
|
+
while (ts.isAsExpression(right) || ts.isTypeAssertion(right)) {
|
|
224
174
|
right = right.expression;
|
|
225
175
|
}
|
|
226
176
|
|
|
@@ -234,14 +184,14 @@ function visit(ctx: VisitContext, node: ts.Node): void {
|
|
|
234
184
|
}
|
|
235
185
|
}
|
|
236
186
|
|
|
237
|
-
|
|
187
|
+
node.forEachChild(n => visit(ctx, n));
|
|
238
188
|
}
|
|
239
189
|
|
|
240
190
|
|
|
241
|
-
export default (sourceFile: ts.SourceFile, bindings: Bindings,
|
|
191
|
+
export default (sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: IsReactiveCall): ReplacementIntent[] => {
|
|
242
192
|
let ctx: VisitContext = {
|
|
243
193
|
bindings,
|
|
244
|
-
|
|
194
|
+
isReactiveCall,
|
|
245
195
|
replacements: [],
|
|
246
196
|
sourceFile
|
|
247
197
|
};
|
|
@@ -1,9 +1,26 @@
|
|
|
1
|
+
import { ts } from '@esportsplus/typescript';
|
|
1
2
|
import { uid } from '@esportsplus/typescript/compiler';
|
|
2
3
|
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
+
const COMPOUND_OPERATORS = new Map<ts.SyntaxKind, string>([
|
|
6
|
+
[ts.SyntaxKind.AmpersandAmpersandEqualsToken, '&&'],
|
|
7
|
+
[ts.SyntaxKind.AmpersandEqualsToken, '&'],
|
|
8
|
+
[ts.SyntaxKind.AsteriskAsteriskEqualsToken, '**'],
|
|
9
|
+
[ts.SyntaxKind.AsteriskEqualsToken, '*'],
|
|
10
|
+
[ts.SyntaxKind.BarBarEqualsToken, '||'],
|
|
11
|
+
[ts.SyntaxKind.BarEqualsToken, '|'],
|
|
12
|
+
[ts.SyntaxKind.CaretEqualsToken, '^'],
|
|
13
|
+
[ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, '>>'],
|
|
14
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, '>>>'],
|
|
15
|
+
[ts.SyntaxKind.LessThanLessThanEqualsToken, '<<'],
|
|
16
|
+
[ts.SyntaxKind.MinusEqualsToken, '-'],
|
|
17
|
+
[ts.SyntaxKind.PercentEqualsToken, '%'],
|
|
18
|
+
[ts.SyntaxKind.PlusEqualsToken, '+'],
|
|
19
|
+
[ts.SyntaxKind.QuestionQuestionEqualsToken, '??'],
|
|
20
|
+
[ts.SyntaxKind.SlashEqualsToken, '/']
|
|
21
|
+
]);
|
|
5
22
|
|
|
6
|
-
const
|
|
23
|
+
const ENTRYPOINT = 'reactive';
|
|
7
24
|
|
|
8
25
|
const NAMESPACE = uid('reactivity');
|
|
9
26
|
|
|
@@ -11,12 +28,11 @@ const NAMESPACE = uid('reactivity');
|
|
|
11
28
|
const TYPES = {
|
|
12
29
|
Array: 0,
|
|
13
30
|
Computed: 1,
|
|
14
|
-
|
|
15
|
-
Signal: 3
|
|
31
|
+
Signal: 2
|
|
16
32
|
} as const;
|
|
17
33
|
|
|
18
34
|
type TYPES = typeof TYPES[keyof typeof TYPES];
|
|
19
35
|
|
|
20
36
|
|
|
21
|
-
export {
|
|
37
|
+
export { COMPOUND_OPERATORS, ENTRYPOINT, NAMESPACE, TYPES };
|
|
22
38
|
export { PACKAGE_NAME } from '../constants';
|