@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.
@@ -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
  }
@@ -18,10 +18,10 @@ class ReactiveArray extends Array {
18
18
  return read(this._length);
19
19
  }
20
20
  set $length(value) {
21
- if (value > super.length) {
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, super.length);
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 >= super.length) {
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, super.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
- while (listeners.length && listeners[listeners.length - 1] === null) {
84
- listeners.pop();
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, super.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 super.length;
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, super.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(this.length);
153
- for (let i = 0, n = before.length; i < n; i++) {
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(), cursors = new Map(), order = new Array(this.length);
158
- for (let i = 0, n = before.length; i < n; i++) {
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, n = this.length; i < n; i++) {
168
- let value = this[i], list = buckets.get(value);
169
- if (!list) {
170
- order[i] = i;
171
- continue;
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, super.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
  }
@@ -8,8 +8,9 @@ class ReactiveObject {
8
8
  if (data == null) {
9
9
  return;
10
10
  }
11
- for (let key in data) {
12
- let value = data[key], type = typeof value;
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 = STATE_DIRTY) {
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.1",
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",
@@ -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
  }
@@ -69,11 +69,11 @@ class ReactiveArray<T> extends Array<T> {
69
69
  }
70
70
 
71
71
  set $length(value: number) {
72
- if (value > super.length) {
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, super.length);
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 >= super.length) {
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, super.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
- while (listeners.length && listeners[listeners.length - 1] === null) {
158
- listeners.pop();
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, super.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 super.length;
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, super.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 before = new Array(this.length) as T[];
256
+ let n = this.length,
257
+ before = new Array(n) as T[];
251
258
 
252
- for (let i = 0, n = before.length; i < n; i++) {
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
- cursors = new Map<any, number>(),
260
- order = new Array(this.length);
266
+ order = new Array(n);
261
267
 
262
- for (let i = 0, n = before.length; i < n; i++) {
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, n = this.length; i < n; i++) {
275
- let value = this[i],
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
- let cursor = cursors.get(value) || 0;
283
+ order[i] = list.length === 1 ? list[0] : list[list.length - 1];
284
284
 
285
- order[i] = list[cursor];
286
- cursors.set(value, cursor + 1);
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, super.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]);
@@ -14,8 +14,11 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
14
14
  return;
15
15
  }
16
16
 
17
- for (let key in data) {
18
- let value = data[key as keyof T],
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 = STATE_DIRTY) {
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
  }