@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.
Files changed (45) hide show
  1. package/README.md +9 -7
  2. package/bench/reactive/array.bench.ts +17 -17
  3. package/build/compiler/array.d.ts +2 -2
  4. package/build/compiler/array.js +12 -54
  5. package/build/compiler/constants.d.ts +4 -4
  6. package/build/compiler/constants.js +20 -4
  7. package/build/compiler/index.js +25 -38
  8. package/build/compiler/object.d.ts +2 -2
  9. package/build/compiler/object.js +10 -21
  10. package/build/compiler/plugins/vite.d.ts +4 -2
  11. package/build/compiler/primitives.d.ts +6 -2
  12. package/build/compiler/primitives.js +35 -42
  13. package/build/compiler/types.d.ts +3 -1
  14. package/build/constants.d.ts +2 -2
  15. package/build/constants.js +2 -2
  16. package/build/reactive/array.d.ts +1 -1
  17. package/build/reactive/array.js +26 -15
  18. package/build/reactive/index.js +5 -14
  19. package/build/reactive/object.js +1 -1
  20. package/build/system.d.ts +10 -13
  21. package/build/system.js +105 -83
  22. package/build/types.d.ts +2 -7
  23. package/package.json +5 -5
  24. package/pnpm-workspace.yaml +3 -0
  25. package/src/compiler/array.ts +14 -64
  26. package/src/compiler/constants.ts +21 -5
  27. package/src/compiler/index.ts +39 -70
  28. package/src/compiler/object.ts +12 -29
  29. package/src/compiler/primitives.ts +55 -53
  30. package/src/compiler/types.ts +4 -1
  31. package/src/constants.ts +4 -4
  32. package/src/reactive/array.ts +33 -17
  33. package/src/reactive/index.ts +5 -17
  34. package/src/reactive/object.ts +1 -1
  35. package/src/system.ts +138 -101
  36. package/src/types.ts +2 -9
  37. package/test/async-computed.test.ts +2 -2
  38. package/test/compiler/compiler.test.ts +40 -23
  39. package/test/effects.test.ts +37 -1
  40. package/test/lib/uncaught.ts +26 -0
  41. package/test/reactive/array.test.ts +169 -99
  42. package/test/reactive/nested.test.ts +14 -14
  43. package/test/reactive/objects.test.ts +1 -1
  44. package/test/reactive/reactive.test.ts +49 -0
  45. 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
- for (let c = computed.subs; c; c = c.nextSub) {
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, del) {
221
- if (del) {
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 === STABILIZER_SCHEDULED) {
309
+ if (stabilizer !== STABILIZER_IDLE) {
293
310
  return;
294
311
  }
295
- if (stabilizer === STABILIZER_IDLE && !depth) {
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
- stabilizer = STABILIZER_RUNNING;
307
- for (heap_i = 0; heap_i <= heap_n; heap_i++) {
308
- if (pendingHead !== null) {
309
- drainPending();
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
- let computed = heap[heap_i];
312
- heap[heap_i] = undefined;
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 (heap_n > 0 && heap[heap_n] === undefined) {
320
- heap_n--;
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, true);
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, true);
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 fail = (e) => {
418
- if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
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
- }, id = ++v, result = read(factory);
438
+ }, result = read(factory);
423
439
  if (isPromise(result)) {
424
- if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
440
+ if (!stale()) {
425
441
  write(pending, true);
426
442
  }
427
443
  result.then((value) => {
428
- if (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
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 (id !== v || (factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
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 (id === v && !(factory.state & (STATE_IN_HEAP | STATE_NOTIFY_MASK))) {
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, false);
498
+ recompute(self);
501
499
  }
502
500
  else if (eager) {
503
501
  self.height = observer.height + 1;
504
- recompute(self, false);
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, false);
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
- schedule();
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
- while (stabilizer === STABILIZER_SCHEDULED) {
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, d = root.disposables, o = observer, s = scope, self = null, tracking = fn.length, value;
695
+ let c, o = observer, s = scope, self = null, tracking = fn.length, value;
675
696
  observer = null;
676
- root.disposables = 0;
677
- if (tracking) {
678
- scope = self = { cleanup: null, state: STATE_COMPUTED };
679
- value = fn(c = () => dispose(self));
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
- else {
682
- scope = null;
683
- value = fn();
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
- interface TransformResult {
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.29.5",
8
- "@types/node": "^26.1.1",
9
- "vite": "^8.1.5",
10
- "vitest": "^4.1.10"
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.33.0",
39
+ "version": "0.36.0",
40
40
  "scripts": {
41
41
  "agent:bench": "vitest bench --run",
42
42
  "agent:test": "tsc --noEmit && vitest run",
@@ -1,2 +1,5 @@
1
1
  allowBuilds:
2
2
  esbuild: true
3
+ minimumReleaseAgeExclude:
4
+ - '@esportsplus/typescript@0.31.0 || 0.32.0'
5
+ - tsc-alias@1.9.4
@@ -1,34 +1,18 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { ast, imports } from '@esportsplus/typescript/compiler';
2
+ import { ast } from '@esportsplus/typescript/compiler';
3
3
  import type { ReplacementIntent } from '@esportsplus/typescript/compiler';
4
- import { ENTRYPOINT, NAMESPACE, PACKAGE_NAME, TYPES } from './constants';
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
- checker: ts.TypeChecker | undefined;
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(ctx.checker, node) && node.arguments.length > 0) {
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}(...${expression.getText(sf)})`
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 && isAssignmentOperator(parent.operatorToken.kind)) {
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 === ts.SyntaxKind.EqualsToken) {
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 ${getOperator(op)} ${parent.right.getText(sf)}`
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.isTypeAssertionExpression(right)) {
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
- ts.forEachChild(node, n => visit(ctx, n));
187
+ node.forEachChild(n => visit(ctx, n));
238
188
  }
239
189
 
240
190
 
241
- export default (sourceFile: ts.SourceFile, bindings: Bindings, checker?: ts.TypeChecker): ReplacementIntent[] => {
191
+ export default (sourceFile: ts.SourceFile, bindings: Bindings, isReactiveCall: IsReactiveCall): ReplacementIntent[] => {
242
192
  let ctx: VisitContext = {
243
193
  bindings,
244
- checker,
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 ENTRYPOINT = 'reactive';
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 ENTRYPOINT_REGEX = /\breactive\b/;
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
- Object: 2,
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 { ENTRYPOINT, ENTRYPOINT_REGEX, NAMESPACE, TYPES };
37
+ export { COMPOUND_OPERATORS, ENTRYPOINT, NAMESPACE, TYPES };
22
38
  export { PACKAGE_NAME } from '../constants';