@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
|
@@ -63,7 +63,7 @@ describe('nested reactive patterns', () => {
|
|
|
63
63
|
let todo1 = new ReactiveObject({ done: false, text: 'Learn reactivity' }) as any,
|
|
64
64
|
todo2 = new ReactiveObject({ done: true, text: 'Build app' }) as any,
|
|
65
65
|
todo3 = new ReactiveObject({ done: false, text: 'Test everything' }) as any,
|
|
66
|
-
todos = new ReactiveArray(todo1, todo2, todo3),
|
|
66
|
+
todos = new ReactiveArray([todo1, todo2, todo3]),
|
|
67
67
|
completedCounts: number[] = [];
|
|
68
68
|
|
|
69
69
|
effect(() => {
|
|
@@ -88,7 +88,7 @@ describe('nested reactive patterns', () => {
|
|
|
88
88
|
|
|
89
89
|
it('push new reactive object to array', () => {
|
|
90
90
|
let todo1 = new ReactiveObject({ done: false, text: 'First' }) as any,
|
|
91
|
-
todos = new ReactiveArray(todo1);
|
|
91
|
+
todos = new ReactiveArray([todo1]);
|
|
92
92
|
|
|
93
93
|
expect(todos.length).toBe(1);
|
|
94
94
|
|
|
@@ -104,10 +104,10 @@ describe('nested reactive patterns', () => {
|
|
|
104
104
|
|
|
105
105
|
describe('matrix pattern', () => {
|
|
106
106
|
it('array of arrays', () => {
|
|
107
|
-
let row1 = new ReactiveArray(1, 2, 3),
|
|
108
|
-
row2 = new ReactiveArray(4, 5, 6),
|
|
109
|
-
row3 = new ReactiveArray(7, 8, 9),
|
|
110
|
-
matrix = new ReactiveArray(row1, row2, row3);
|
|
107
|
+
let row1 = new ReactiveArray([1, 2, 3]),
|
|
108
|
+
row2 = new ReactiveArray([4, 5, 6]),
|
|
109
|
+
row3 = new ReactiveArray([7, 8, 9]),
|
|
110
|
+
matrix = new ReactiveArray([row1, row2, row3]);
|
|
111
111
|
|
|
112
112
|
expect(matrix.length).toBe(3);
|
|
113
113
|
expect([...matrix[0]]).toEqual([1, 2, 3]);
|
|
@@ -116,9 +116,9 @@ describe('nested reactive patterns', () => {
|
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
it('modifying inner array', () => {
|
|
119
|
-
let row1 = new ReactiveArray(1, 2, 3),
|
|
120
|
-
row2 = new ReactiveArray(4, 5, 6),
|
|
121
|
-
matrix = new ReactiveArray(row1, row2);
|
|
119
|
+
let row1 = new ReactiveArray([1, 2, 3]),
|
|
120
|
+
row2 = new ReactiveArray([4, 5, 6]),
|
|
121
|
+
matrix = new ReactiveArray([row1, row2]);
|
|
122
122
|
|
|
123
123
|
row1.$set(0, 100);
|
|
124
124
|
row2.push(60);
|
|
@@ -128,10 +128,10 @@ describe('nested reactive patterns', () => {
|
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it('push new row to matrix', () => {
|
|
131
|
-
let row1 = new ReactiveArray(1, 2, 3),
|
|
132
|
-
matrix = new ReactiveArray(row1);
|
|
131
|
+
let row1 = new ReactiveArray([1, 2, 3]),
|
|
132
|
+
matrix = new ReactiveArray([row1]);
|
|
133
133
|
|
|
134
|
-
let row2 = new ReactiveArray(4, 5, 6);
|
|
134
|
+
let row2 = new ReactiveArray([4, 5, 6]);
|
|
135
135
|
|
|
136
136
|
matrix.push(row2);
|
|
137
137
|
|
|
@@ -143,7 +143,7 @@ describe('nested reactive patterns', () => {
|
|
|
143
143
|
|
|
144
144
|
describe('cross-object array + multiplier', () => {
|
|
145
145
|
it('computed sum reacts to multiplier changes', async () => {
|
|
146
|
-
let items = new ReactiveArray(10, 20, 30),
|
|
146
|
+
let items = new ReactiveArray([10, 20, 30]),
|
|
147
147
|
multiplier = signal(2),
|
|
148
148
|
sum = computed(() => {
|
|
149
149
|
let total = 0;
|
|
@@ -171,7 +171,7 @@ describe('nested reactive patterns', () => {
|
|
|
171
171
|
});
|
|
172
172
|
|
|
173
173
|
it('computed sum reacts to reactive length changes', async () => {
|
|
174
|
-
let items = new ReactiveArray(10, 20, 30),
|
|
174
|
+
let items = new ReactiveArray([10, 20, 30]),
|
|
175
175
|
multiplier = signal(2),
|
|
176
176
|
sum = computed(() => {
|
|
177
177
|
let length = items.$length,
|
|
@@ -126,7 +126,7 @@ describe('reactive object patterns', () => {
|
|
|
126
126
|
});
|
|
127
127
|
|
|
128
128
|
it('array computed using reduce via external signal', async () => {
|
|
129
|
-
let items = new ReactiveArray(1, 2, 3),
|
|
129
|
+
let items = new ReactiveArray([1, 2, 3]),
|
|
130
130
|
total = computed(() => {
|
|
131
131
|
let sum = 0;
|
|
132
132
|
|
|
@@ -391,6 +391,55 @@ describe('reactive()', () => {
|
|
|
391
391
|
expect(() => reactive('hello' as any)).toThrow();
|
|
392
392
|
});
|
|
393
393
|
});
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
describe('auto-dispose', () => {
|
|
397
|
+
it('disposes an object with a computed property when its owning effect stops', async () => {
|
|
398
|
+
let s = signal(1),
|
|
399
|
+
runs = 0,
|
|
400
|
+
stop = effect(() => {
|
|
401
|
+
reactive({
|
|
402
|
+
doubled: () => {
|
|
403
|
+
runs++;
|
|
404
|
+
return read(s) * 2;
|
|
405
|
+
}
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
expect(runs).toBe(1);
|
|
410
|
+
|
|
411
|
+
stop();
|
|
412
|
+
|
|
413
|
+
write(s, 2);
|
|
414
|
+
await Promise.resolve();
|
|
415
|
+
|
|
416
|
+
expect(runs).toBe(1);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it('disposes nested objects of an array when its owning effect stops', () => {
|
|
420
|
+
let inner = new ReactiveObject({ x: 1 }),
|
|
421
|
+
spy = vi.spyOn(inner, 'dispose'),
|
|
422
|
+
stop = effect(() => {
|
|
423
|
+
reactive([inner]);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
stop();
|
|
427
|
+
|
|
428
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it('top-level reactive() is not auto-disposed', async () => {
|
|
432
|
+
let s = signal(1),
|
|
433
|
+
obj = reactive({ doubled: () => read(s) * 2 }) as any;
|
|
434
|
+
|
|
435
|
+
expect(obj.doubled).toBe(2);
|
|
436
|
+
|
|
437
|
+
write(s, 3);
|
|
438
|
+
await Promise.resolve();
|
|
439
|
+
|
|
440
|
+
expect(obj.doubled).toBe(6);
|
|
441
|
+
});
|
|
442
|
+
});
|
|
394
443
|
});
|
|
395
444
|
|
|
396
445
|
|
package/test/system.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write } from '~/system';
|
|
2
|
+
import { computed, dispose, effect, flush, isComputed, isSignal, onCleanup, peek, read, root, signal, write } from '~/system';
|
|
3
3
|
import { waitFor } from './lib/wait-for';
|
|
4
4
|
|
|
5
5
|
|
|
@@ -576,32 +576,32 @@ describe('root', () => {
|
|
|
576
576
|
expect(inner).toBe(1);
|
|
577
577
|
});
|
|
578
578
|
|
|
579
|
-
it('
|
|
580
|
-
let
|
|
579
|
+
it('restores scope when an inner root throws', () => {
|
|
580
|
+
let spy = vi.fn(),
|
|
581
|
+
stop: VoidFunction | undefined;
|
|
581
582
|
|
|
582
|
-
root(() => {
|
|
583
|
-
|
|
584
|
-
computed(() => 2);
|
|
585
|
-
computed(() => 3);
|
|
586
|
-
});
|
|
583
|
+
root((dispose) => {
|
|
584
|
+
stop = dispose;
|
|
587
585
|
|
|
588
|
-
|
|
589
|
-
|
|
586
|
+
try {
|
|
587
|
+
root((inner) => {
|
|
588
|
+
inner;
|
|
589
|
+
throw new Error('reactivity: inner boom');
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
}
|
|
590
594
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
computed(() => 10);
|
|
595
|
+
onCleanup(spy);
|
|
596
|
+
});
|
|
594
597
|
|
|
595
|
-
|
|
596
|
-
computed(() => 20);
|
|
597
|
-
computed(() => 30);
|
|
598
|
-
});
|
|
598
|
+
expect(spy).not.toHaveBeenCalled();
|
|
599
599
|
|
|
600
|
-
|
|
601
|
-
});
|
|
600
|
+
stop!();
|
|
602
601
|
|
|
603
|
-
expect(
|
|
602
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
604
603
|
});
|
|
604
|
+
|
|
605
605
|
});
|
|
606
606
|
|
|
607
607
|
|
|
@@ -686,10 +686,19 @@ describe('computed object size', () => {
|
|
|
686
686
|
expect('type' in c).toBe(false);
|
|
687
687
|
});
|
|
688
688
|
|
|
689
|
-
it('has no more own properties than
|
|
689
|
+
it('has no more own properties than 17 (14 slimmed + gv + rv + pending)', () => {
|
|
690
690
|
let c = computed(() => 42);
|
|
691
691
|
|
|
692
|
-
expect(Object.keys(c).length).toBeLessThanOrEqual(
|
|
692
|
+
expect(Object.keys(c).length).toBeLessThanOrEqual(17);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('shares one shape between sync and async computeds', () => {
|
|
696
|
+
let a = computed(() => 42),
|
|
697
|
+
b = computed(() => Promise.resolve(42));
|
|
698
|
+
|
|
699
|
+
expect(Object.keys(a)).toEqual(Object.keys(b));
|
|
700
|
+
expect(a.pending).toBeNull();
|
|
701
|
+
expect(isSignal(b.pending)).toBe(true);
|
|
693
702
|
});
|
|
694
703
|
});
|
|
695
704
|
|
|
@@ -793,6 +802,105 @@ describe('globalVersion fast path', () => {
|
|
|
793
802
|
});
|
|
794
803
|
|
|
795
804
|
|
|
805
|
+
describe('stale reads after a pass', () => {
|
|
806
|
+
it('an observer created between a write and its flush never sees the stale value', async () => {
|
|
807
|
+
let s = signal(0),
|
|
808
|
+
c = computed(() => read(s)),
|
|
809
|
+
seen: number[] = [];
|
|
810
|
+
|
|
811
|
+
write(s, 1);
|
|
812
|
+
flush();
|
|
813
|
+
|
|
814
|
+
write(s, 2);
|
|
815
|
+
|
|
816
|
+
effect(() => {
|
|
817
|
+
seen.push(read(c));
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
await Promise.resolve();
|
|
821
|
+
await Promise.resolve();
|
|
822
|
+
|
|
823
|
+
expect(seen[0]).toBe(2);
|
|
824
|
+
expect(seen).not.toContain(1);
|
|
825
|
+
expect(read(c)).toBe(2);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
it('a computed created between a write and its flush computes from the new value', async () => {
|
|
829
|
+
let s = signal(0),
|
|
830
|
+
seen: number[] = [];
|
|
831
|
+
|
|
832
|
+
write(s, 1);
|
|
833
|
+
flush();
|
|
834
|
+
|
|
835
|
+
write(s, 2);
|
|
836
|
+
|
|
837
|
+
effect(() => {
|
|
838
|
+
let c = computed(() => read(s) * 10);
|
|
839
|
+
|
|
840
|
+
seen.push(read(c));
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
await Promise.resolve();
|
|
844
|
+
await Promise.resolve();
|
|
845
|
+
|
|
846
|
+
expect(seen[0]).toBe(20);
|
|
847
|
+
expect(seen).not.toContain(10);
|
|
848
|
+
});
|
|
849
|
+
});
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
describe('same-height stabilization', () => {
|
|
853
|
+
it('settles a sibling pulled from the active heap bucket', () => {
|
|
854
|
+
let s = signal(0),
|
|
855
|
+
flag = signal(false),
|
|
856
|
+
M = computed(() => 0),
|
|
857
|
+
N = computed(() => read(flag) ? (read(M), read(s), 0) : 0),
|
|
858
|
+
R = computed(() => read(s) + read(N));
|
|
859
|
+
|
|
860
|
+
effect(() => { read(R); });
|
|
861
|
+
effect(() => { read(N); });
|
|
862
|
+
effect(() => { read(M); });
|
|
863
|
+
|
|
864
|
+
write(flag, true);
|
|
865
|
+
flush();
|
|
866
|
+
write(s, 1);
|
|
867
|
+
|
|
868
|
+
expect(() => {
|
|
869
|
+
peek(M);
|
|
870
|
+
flush();
|
|
871
|
+
}).not.toThrow();
|
|
872
|
+
expect(read(R)).toBe(1);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it('observes one settled value per write when a sibling grows', () => {
|
|
876
|
+
let s = signal(0),
|
|
877
|
+
flag = signal(false),
|
|
878
|
+
M = computed(() => 0),
|
|
879
|
+
N = computed(() => read(flag) ? read(M) + read(s) : 0),
|
|
880
|
+
values: number[] = [],
|
|
881
|
+
R = computed(() => {
|
|
882
|
+
let value = read(s) + read(N);
|
|
883
|
+
|
|
884
|
+
values.push(value);
|
|
885
|
+
|
|
886
|
+
return value;
|
|
887
|
+
});
|
|
888
|
+
|
|
889
|
+
effect(() => { read(R); });
|
|
890
|
+
effect(() => { read(N); });
|
|
891
|
+
effect(() => { read(M); });
|
|
892
|
+
|
|
893
|
+
write(flag, true);
|
|
894
|
+
flush();
|
|
895
|
+
values.length = 0;
|
|
896
|
+
write(s, 1);
|
|
897
|
+
flush();
|
|
898
|
+
|
|
899
|
+
expect(values).toEqual([2]);
|
|
900
|
+
});
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
|
|
796
904
|
describe('edge cases', () => {
|
|
797
905
|
it('diamond graph dedup — notify state mask prevents redundant recomputation', async () => {
|
|
798
906
|
let s = signal(1),
|