ripple 0.2.46 → 0.2.47

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 (36) hide show
  1. package/package.json +1 -1
  2. package/src/compiler/phases/1-parse/index.js +2 -2
  3. package/src/compiler/phases/2-analyze/index.js +640 -667
  4. package/src/compiler/phases/3-transform/index.js +1873 -1880
  5. package/src/compiler/phases/3-transform/segments.js +2 -2
  6. package/src/compiler/utils.js +596 -551
  7. package/src/jsx-runtime.js +12 -12
  8. package/src/runtime/array.js +611 -609
  9. package/src/runtime/index.js +29 -17
  10. package/src/runtime/internal/client/array.js +121 -121
  11. package/src/runtime/internal/client/blocks.js +206 -206
  12. package/src/runtime/internal/client/constants.js +2 -2
  13. package/src/runtime/internal/client/context.js +40 -40
  14. package/src/runtime/internal/client/events.js +191 -191
  15. package/src/runtime/internal/client/for.js +355 -355
  16. package/src/runtime/internal/client/if.js +25 -25
  17. package/src/runtime/internal/client/index.js +57 -56
  18. package/src/runtime/internal/client/operations.js +32 -32
  19. package/src/runtime/internal/client/portal.js +19 -19
  20. package/src/runtime/internal/client/render.js +132 -132
  21. package/src/runtime/internal/client/runtime.js +838 -835
  22. package/src/runtime/internal/client/template.js +36 -36
  23. package/src/runtime/internal/client/try.js +113 -113
  24. package/src/runtime/internal/client/types.d.ts +10 -10
  25. package/src/runtime/internal/client/utils.js +5 -5
  26. package/src/runtime/map.js +139 -139
  27. package/src/runtime/set.js +130 -130
  28. package/src/utils/ast.js +189 -189
  29. package/src/utils/builders.js +244 -244
  30. package/src/utils/sanitize_template_string.js +1 -1
  31. package/tests/__snapshots__/composite.test.ripple.snap +1 -1
  32. package/tests/accessors-props.test.ripple +9 -9
  33. package/tests/basic.test.ripple +4 -4
  34. package/tests/boundaries.test.ripple +17 -17
  35. package/tests/composite.test.ripple +43 -72
  36. package/types/index.d.ts +6 -2
@@ -1,4 +1,8 @@
1
- import { TRACKED_OBJECT, ARRAY_SET_INDEX_AT, MAX_ARRAY_LENGTH } from './internal/client/constants.js';
1
+ import {
2
+ TRACKED_OBJECT,
3
+ ARRAY_SET_INDEX_AT,
4
+ MAX_ARRAY_LENGTH,
5
+ } from './internal/client/constants.js';
2
6
  import { get, safe_scope, set, tracked } from './internal/client/runtime.js';
3
7
  import { is_ripple_array } from './internal/client/utils.js';
4
8
  /** @import { Block, Tracked } from '#client' */
@@ -8,35 +12,35 @@ const INIT_AFTER_NEW = Symbol();
8
12
 
9
13
  /** @type {(symbol | string | any)[]} */
10
14
  const introspect_methods = [
11
- 'concat',
12
- 'entries',
13
- 'every',
14
- 'filter',
15
- 'find',
16
- 'findIndex',
17
- 'findLast',
18
- 'findLastIndex',
19
- 'flat',
20
- 'flatMap',
21
- 'forEach',
22
- 'includes',
23
- 'indexOf',
24
- 'join',
25
- 'keys',
26
- 'lastIndexOf',
27
- 'map',
28
- 'reduce',
29
- 'reduceRight',
30
- 'some',
31
- 'slice',
32
- 'toLocaleString',
33
- 'toReversed',
34
- 'toSorted',
35
- 'toSpliced',
36
- 'toString',
37
- Symbol.iterator,
38
- 'values',
39
- 'with',
15
+ 'concat',
16
+ 'entries',
17
+ 'every',
18
+ 'filter',
19
+ 'find',
20
+ 'findIndex',
21
+ 'findLast',
22
+ 'findLastIndex',
23
+ 'flat',
24
+ 'flatMap',
25
+ 'forEach',
26
+ 'includes',
27
+ 'indexOf',
28
+ 'join',
29
+ 'keys',
30
+ 'lastIndexOf',
31
+ 'map',
32
+ 'reduce',
33
+ 'reduceRight',
34
+ 'some',
35
+ 'slice',
36
+ 'toLocaleString',
37
+ 'toReversed',
38
+ 'toSorted',
39
+ 'toSpliced',
40
+ 'toString',
41
+ Symbol.iterator,
42
+ 'values',
43
+ 'with',
40
44
  ];
41
45
 
42
46
  let is_proto_set = false;
@@ -46,549 +50,545 @@ let is_proto_set = false;
46
50
  * @extends {Array<T>}
47
51
  */
48
52
  export class RippleArray extends Array {
49
- /** @type {Array<Tracked>} */
50
- #tracked_elements = [];
51
- /** @type {Tracked} */
52
- // @ts-expect-error
53
- #tracked_index;
54
-
55
- /**
56
- * @template U
57
- * @param {ArrayLike<U> | Iterable<U>} arrayLike
58
- * @param {(v: U, k: number) => any | undefined} [mapFn]
59
- * @param {any} [thisArg]
60
- * @returns {RippleArray<U>}
61
- */
62
- static from(arrayLike, mapFn, thisArg) {
63
- var arr = mapFn ?
64
- Array.from(arrayLike, mapFn, thisArg)
65
- : Array.from(arrayLike);
66
-
67
- return get_instance_from_static(arr);
68
- }
69
-
70
- /**
71
- * @template U
72
- * @param {ArrayLike<U> | Iterable<U>} arrayLike
73
- * @param {(v: U, k: number) => any | undefined} [mapFn]
74
- * @param {any} [thisArg]
75
- * @returns {Promise<RippleArray<U>>}
76
- */
77
- static async fromAsync(arrayLike, mapFn, thisArg) {
78
- var block = safe_scope();
79
- // create empty array to get the right scope
80
- var result = new RippleArray();
81
-
82
- var arr = mapFn ?
83
- await Array.fromAsync(arrayLike, mapFn, thisArg)
84
- : await Array.fromAsync(arrayLike)
85
-
86
- var first = get_first_if_length(arr);
87
-
88
- if (first) {
89
- result[0] = first;
90
- } else {
91
- result.length = arr.length;
92
- for (let i = 0; i < arr.length; i++) {
93
- if (i in arr) {
94
- result[i] = arr[i];
95
- }
96
- }
97
- }
98
-
99
- result[INIT_AFTER_NEW](block);
100
-
101
- return result
102
- }
103
-
104
- /**
105
- * @template U
106
- * @param {...U} elements
107
- * @returns {RippleArray<U>}
108
- */
109
- static of(...elements) {
110
- var arr = Array.of(...elements);
111
-
112
- return get_instance_from_static(arr);
113
- }
114
-
115
- /**
116
- * @param {...T} elements
117
- */
118
- constructor(...elements) {
119
- super(...elements);
120
-
121
- this[INIT_AFTER_NEW]();
122
-
123
- if (!is_proto_set) {
124
- is_proto_set = true;
125
- this.#set_proto();
126
- }
127
- }
128
-
129
- [INIT_AFTER_NEW](block = safe_scope()) {
130
- if (this.length !== 0) {
131
- var tracked_elements = this.#tracked_elements;
132
- for (var i = 0; i < this.length; i++) {
133
- if (!(i in this)) {
134
- continue;
135
- }
136
- tracked_elements[i] = tracked(this[i], block);
137
- }
138
- }
139
-
140
- if (!this.#tracked_index) {
141
- this.#tracked_index = tracked(this.length, block);
142
- } else if (this.#tracked_index.v !== this.length) {
143
- set(this.#tracked_index, this.length, block);
144
- }
145
- }
146
-
147
- #set_proto() {
148
- var proto = RippleArray.prototype;
149
- var array_proto = Array.prototype;
150
-
151
- for (const method of introspect_methods) {
152
- if (!(method in array_proto)) {
153
- continue;
154
- }
155
-
156
- /** @param {...any} v */
157
- proto[method] = function (...v) {
158
- var result = array_proto[method].apply(this, v);
159
-
160
- if (is_ripple_array(result) && this !== result) {
161
- var tracked_elements = result[TRACKED_OBJECT];
162
- var block = safe_scope();
163
-
164
- if (tracked_elements.length != result.length) {
165
- for (var i = 0; i < result.length; i++) {
166
- if ((i in result) && tracked_elements[i] === undefined) {
167
- tracked_elements[i] = tracked(result[i], block);
168
- }
169
- }
170
- }
171
- }
172
-
173
- // the caller reruns on length changes
174
- this.$length;
175
- // the caller reruns on element changes
176
- establish_trackable_deps(this);
177
- return result;
178
- };
179
- }
180
- }
181
-
182
- /**
183
- * @param {number} target
184
- * @param {number} start
185
- * @param {number} [end]
186
- * @returns {this}
187
- */
188
- copyWithin(target, start, end) {
189
- var block = safe_scope();
190
- var tracked_elements = this.#tracked_elements;
191
- var length = this.length;
192
-
193
- super.copyWithin(target, start, end);
194
-
195
- if (!target && !start) {
196
- return this;
197
- } else if (!target && start) {
198
- target = 0;
199
- } else if (target && !start) {
200
- start = 0;
201
- }
202
-
203
- if (target < 0) {
204
- target = Math.max(length + target, 0);
205
- } else {
206
- target = Math.min(target, length);
207
- }
208
-
209
- if (start < 0) {
210
- start = Math.max(length + start, 0);
211
- } else {
212
- start = Math.min(start, length);
213
- }
214
-
215
- if (end === undefined) {
216
- end = length;
217
- } else if (end < 0) {
218
- end = Math.max(length + end, 0);
219
- } else {
220
- end = Math.min(end, length);
221
- }
222
-
223
- if (target >= length) {
224
- return this;
225
- }
226
-
227
- const copyCount = Math.min(end - start, length - target);
228
-
229
- // If no elements are copied (start >= end or copyCount <= 0), return early
230
- if (start >= end || copyCount <= 0) {
231
- return this;
232
- }
233
-
234
- for (let i = 0; i < copyCount; i++) {
235
- const index = target + i;
236
- // Only update if source and target are different positions
237
- // to avoid unnecessary updates when copying onto itself
238
- if (index !== start + i) {
239
- this.#update_tracked_at_index({array: this, i: index, block, tracked_elements});
240
- }
241
- }
242
-
243
- return this;
244
- }
245
-
246
- /**
247
- * @param {T} value
248
- * @param {number} [start]
249
- * @param {number} [end]
250
- * @returns {this}
251
- */
252
- fill(value, start, end) {
253
- var block = safe_scope();
254
- var tracked_elements = this.#tracked_elements;
255
- var length = this.length;
256
-
257
- // avoid unexpected behavior with method args being undefined
258
- if (value === undefined && start === undefined && end === undefined) {
259
- // @ts-ignore
260
- super.fill();
261
- } else if (start === undefined && end === undefined) {
262
- super.fill(value);
263
- } else if (end === undefined) {
264
- super.fill(value, start);
265
- } else {
266
- super.fill(value, start, end);
267
- }
268
-
269
- let actual_start = 0;
270
- if (start !== undefined) {
271
- if (start < 0) {
272
- actual_start = Math.max(length + start, 0);
273
- } else {
274
- actual_start = Math.min(start, length);
275
- }
276
- }
277
-
278
- let actual_end = length;
279
- if (end !== undefined) {
280
- if (end < 0) {
281
- actual_end = Math.max(length + end, 0);
282
- } else {
283
- actual_end = Math.min(end, length);
284
- }
285
- }
286
-
287
- for (let i = actual_start; i < actual_end; i++) {
288
- if (tracked_elements[i] === undefined) {
289
- tracked_elements[i] = tracked(this[i], block);
290
- } else {
291
- set(tracked_elements[i], this[i], block);
292
- }
293
- }
294
-
295
- return this;
296
- }
297
-
298
- reverse() {
299
- var result = /** @type {RippleArray<T>} */ (super.reverse());
300
- this.#update_all_tracked_from_array(result);
301
- return result;
302
- }
303
-
304
- /**
305
- * @param {(a: T, b: T) => number} [fn]
306
- * @returns {this}
307
- */
308
- sort(fn) {
309
- var result = super.sort(fn);
310
- this.#update_all_tracked_from_array(result);
311
- return result;
312
- }
313
-
314
- /**
315
- * @param {RippleArray<T>} array
316
- * @returns {RippleArray<T>}
317
- */
318
- #update_all_tracked_from_array(array) {
319
- var block = safe_scope();
320
- var tracked_elements = this.#tracked_elements;
321
-
322
- for (var i = 0; i < array.length; i++) {
323
- this.#update_tracked_at_index({array, i, block, tracked_elements});
324
- }
325
- return array;
326
- }
327
-
328
- /**
329
- * @param {Object} param0
330
- * @param {RippleArray<T>} param0.array
331
- * @param {number} param0.i
332
- * @param {Block} param0.block
333
- * @param {Tracked[]} param0.tracked_elements
334
- */
335
- #update_tracked_at_index({
336
- array,
337
- i,
338
- block = safe_scope(),
339
- tracked_elements = this.#tracked_elements
340
- }) {
341
- if ((i in array)) {
342
- if (tracked_elements[i] === undefined) {
343
- tracked_elements[i] = tracked(array[i], block);
344
- } else {
345
- set(tracked_elements[i], array[i], block);
346
- }
347
- } else {
348
- if (tracked_elements[i] !== undefined) {
349
- set(tracked_elements[i], undefined, block);
350
- }
351
- delete tracked_elements[i];
352
- }
353
- }
354
-
355
- /**
356
- * @param {...T} elements
357
- * @returns {number}
358
- */
359
- unshift(...elements) {
360
- var block = safe_scope();
361
- var tracked_elements = this.#tracked_elements;
362
- var length = this.length;
363
- var shift_len = elements.length;
364
- var new_len = length + shift_len;
365
-
366
- super.unshift(...elements);
367
-
368
- // extend the array to fit the new elements
369
- tracked_elements.push(...(elements.map(() => tracked(undefined, block))));
370
-
371
- // copy the existing ones to the end
372
- for (let i = length - 1; i >= 0; i--) {
373
- set(tracked_elements[i + shift_len], tracked_elements[i].v, block);
374
- }
375
-
376
- // set new values at the start
377
- for (let i = shift_len - 1; i >= 0; i--) {
378
- set(tracked_elements[i], elements[i], block);
379
- }
380
-
381
- set(this.#tracked_index, new_len, block);
382
- return new_len;
383
- }
384
-
385
- shift() {
386
- var block = safe_scope();
387
- var tracked_elements = this.#tracked_elements;
388
-
389
- var result = super.shift();
390
- for (var i = 0; i < tracked_elements.length; i++) {
391
- // the last must be set to undefined
392
- set(tracked_elements[i], tracked_elements[i + 1]?.v, block);
393
- }
394
- tracked_elements.pop();
395
-
396
- set(this.#tracked_index, this.length, block);
397
- return result;
398
- }
399
-
400
- /**
401
- * @param {...T} elements
402
- * @returns {number}
403
- */
404
- push(...elements) {
405
- var block = safe_scope();
406
- var start_index = this.length;
407
- var tracked_elements = this.#tracked_elements;
408
-
409
- super.push(...elements);
410
-
411
- for (var i = 0; i < elements.length; i++) {
412
- if (!(i in elements)) {
413
- continue;
414
- }
415
- tracked_elements[start_index + i] = tracked(elements[i], block);
416
- }
417
- var length = this.length;
418
- set(this.#tracked_index, this.length, block);
419
- return length;
420
- }
421
-
422
- pop() {
423
- var block = safe_scope();
424
- var tracked_elements = this.#tracked_elements;
425
- var length = tracked_elements.length;
426
- var result = super.pop();
427
-
428
- if (length > 0 && tracked_elements[length - 1] !== undefined) {
429
- set(tracked_elements[length - 1], undefined, block);
430
- }
431
- tracked_elements.pop();
432
-
433
- set(this.#tracked_index, this.length, block);
434
- return result;
435
- }
436
-
437
- /**
438
- * Assigns value at index
439
- * Same as bracket [] assignment
440
- * Supports negative index to count back from the end
441
- * @param {number} index
442
- * @param {T} value
443
- * @returns {T}
444
- */
445
- [ARRAY_SET_INDEX_AT](index, value) {
446
- var block = safe_scope();
447
- var tracked_elements = this.#tracked_elements;
448
- var length = this.length;
449
- var init_index = index;
450
-
451
- if (!Number.isInteger(index)) {
452
- throw new TypeError('Provided index must be a valid integer');
453
- }
454
-
455
- if (init_index < -length) {
456
- throw new RangeError('Provided negative index out of bounds');
457
- }
458
-
459
- index = index < 0 ? index + length : index;
460
-
461
- super[index] = value;
462
-
463
- if (tracked_elements[index] === undefined) {
464
- tracked_elements[index] = tracked(value, block);
465
- } else {
466
- set(tracked_elements[index], value, block);
467
- }
468
-
469
- if (this.length > length) {
470
- set(this.#tracked_index, this.length, block);
471
- }
472
-
473
- return value;
474
- }
475
-
476
- /**
477
- * @param {number} index
478
- * @returns {T | undefined}
479
- */
480
- at(index) {
481
- var tracked_elements = this.#tracked_elements;
482
- var normalized = index < 0 ? index + this.length : index;
483
-
484
- if (tracked_elements[normalized] !== undefined) {
485
- get(tracked_elements[normalized]);
486
- }
487
- this.$length;
488
-
489
- return super.at(index);
490
- }
491
-
492
- /**
493
- * @param {number} start
494
- * @param {number} [delete_count]
495
- * @param {...T} elements
496
- * @returns {Array<T>}
497
- */
498
- splice(start, delete_count, ...elements) {
499
- var block = safe_scope();
500
- var tracked_elements = this.#tracked_elements;
501
- var tracked_len = tracked_elements.length;
502
- var before_len = this.length;
503
- var el_len = elements.length;
504
- var result;
505
-
506
- if (start !== undefined && delete_count === undefined && !el_len) {
507
- // we can't just call super.splice(start, delete_count, ...elements)
508
- // delete_count if undefined will be converted to 0 and nothing will be removed
509
- result = super.splice(start);
510
- } else if (start === undefined && delete_count === undefined && !el_len) {
511
- // If start is undefined the native code will get converted to 0
512
- // which would cause to remove all elements.
513
- // Typically no sense to `.splice()` with no args as it does nothing
514
- // since we get args as undefined, we need to handle this case
515
-
516
- // @ts-ignore
517
- result = super.splice();
518
- } else {
519
- // @ts-ignore
520
- result = super.splice(start, delete_count, ...elements);
53
+ /** @type {Array<Tracked>} */
54
+ #tracked_elements = [];
55
+ /** @type {Tracked} */
56
+ // @ts-expect-error
57
+ #tracked_index;
58
+
59
+ /**
60
+ * @template U
61
+ * @param {ArrayLike<U> | Iterable<U>} arrayLike
62
+ * @param {(v: U, k: number) => any | undefined} [mapFn]
63
+ * @param {any} [thisArg]
64
+ * @returns {RippleArray<U>}
65
+ */
66
+ static from(arrayLike, mapFn, thisArg) {
67
+ var arr = mapFn ? Array.from(arrayLike, mapFn, thisArg) : Array.from(arrayLike);
68
+
69
+ return get_instance_from_static(arr);
70
+ }
71
+
72
+ /**
73
+ * @template U
74
+ * @param {ArrayLike<U> | Iterable<U>} arrayLike
75
+ * @param {(v: U, k: number) => any | undefined} [mapFn]
76
+ * @param {any} [thisArg]
77
+ * @returns {Promise<RippleArray<U>>}
78
+ */
79
+ static async fromAsync(arrayLike, mapFn, thisArg) {
80
+ var block = safe_scope();
81
+ // create empty array to get the right scope
82
+ var result = new RippleArray();
83
+
84
+ var arr = mapFn
85
+ ? await Array.fromAsync(arrayLike, mapFn, thisArg)
86
+ : await Array.fromAsync(arrayLike);
87
+
88
+ var first = get_first_if_length(arr);
89
+
90
+ if (first) {
91
+ result[0] = first;
92
+ } else {
93
+ result.length = arr.length;
94
+ for (let i = 0; i < arr.length; i++) {
95
+ if (i in arr) {
96
+ result[i] = arr[i];
521
97
  }
98
+ }
99
+ }
100
+
101
+ result[INIT_AFTER_NEW](block);
102
+
103
+ return result;
104
+ }
105
+
106
+ /**
107
+ * @template U
108
+ * @param {...U} elements
109
+ * @returns {RippleArray<U>}
110
+ */
111
+ static of(...elements) {
112
+ var arr = Array.of(...elements);
113
+
114
+ return get_instance_from_static(arr);
115
+ }
522
116
 
523
- var after_len = this.length;
524
- delete_count = result.length;
117
+ /**
118
+ * @param {...T} elements
119
+ */
120
+ constructor(...elements) {
121
+ super(...elements);
525
122
 
526
- if (delete_count === 0 && !el_len) {
527
- return result;
123
+ this[INIT_AFTER_NEW]();
124
+
125
+ if (!is_proto_set) {
126
+ is_proto_set = true;
127
+ this.#set_proto();
128
+ }
129
+ }
130
+
131
+ [INIT_AFTER_NEW](block = safe_scope()) {
132
+ if (this.length !== 0) {
133
+ var tracked_elements = this.#tracked_elements;
134
+ for (var i = 0; i < this.length; i++) {
135
+ if (!(i in this)) {
136
+ continue;
528
137
  }
138
+ tracked_elements[i] = tracked(this[i], block);
139
+ }
140
+ }
529
141
 
530
- if (start < 0) {
531
- start = Math.max(before_len + start, 0);
532
- } else {
533
- start = Math.min(start, before_len);
142
+ if (!this.#tracked_index) {
143
+ this.#tracked_index = tracked(this.length, block);
144
+ } else if (this.#tracked_index.v !== this.length) {
145
+ set(this.#tracked_index, this.length, block);
146
+ }
147
+ }
148
+
149
+ #set_proto() {
150
+ var proto = RippleArray.prototype;
151
+ var array_proto = Array.prototype;
152
+
153
+ for (const method of introspect_methods) {
154
+ if (!(method in array_proto)) {
155
+ continue;
156
+ }
157
+
158
+ /** @param {...any} v */
159
+ proto[method] = function (...v) {
160
+ var result = array_proto[method].apply(this, v);
161
+
162
+ if (is_ripple_array(result) && this !== result) {
163
+ var tracked_elements = result[TRACKED_OBJECT];
164
+ var block = safe_scope();
165
+
166
+ if (tracked_elements.length != result.length) {
167
+ for (var i = 0; i < result.length; i++) {
168
+ if (i in result && tracked_elements[i] === undefined) {
169
+ tracked_elements[i] = tracked(result[i], block);
170
+ }
171
+ }
172
+ }
534
173
  }
535
174
 
536
- var range_end = el_len - delete_count === 0
537
- ? start + el_len
538
- : Math.max(after_len, tracked_len);
175
+ // the caller reruns on length changes
176
+ this.$length;
177
+ // the caller reruns on element changes
178
+ establish_trackable_deps(this);
179
+ return result;
180
+ };
181
+ }
182
+ }
183
+
184
+ /**
185
+ * @param {number} target
186
+ * @param {number} start
187
+ * @param {number} [end]
188
+ * @returns {this}
189
+ */
190
+ copyWithin(target, start, end) {
191
+ var block = safe_scope();
192
+ var tracked_elements = this.#tracked_elements;
193
+ var length = this.length;
194
+
195
+ super.copyWithin(target, start, end);
196
+
197
+ if (!target && !start) {
198
+ return this;
199
+ } else if (!target && start) {
200
+ target = 0;
201
+ } else if (target && !start) {
202
+ start = 0;
203
+ }
204
+
205
+ if (target < 0) {
206
+ target = Math.max(length + target, 0);
207
+ } else {
208
+ target = Math.min(target, length);
209
+ }
210
+
211
+ if (start < 0) {
212
+ start = Math.max(length + start, 0);
213
+ } else {
214
+ start = Math.min(start, length);
215
+ }
539
216
 
540
- for (let i = start; i < range_end; i++) {
541
- this.#update_tracked_at_index({array: this, i, block, tracked_elements});
542
- }
217
+ if (end === undefined) {
218
+ end = length;
219
+ } else if (end < 0) {
220
+ end = Math.max(length + end, 0);
221
+ } else {
222
+ end = Math.min(end, length);
223
+ }
543
224
 
544
- tracked_elements.length = after_len;
225
+ if (target >= length) {
226
+ return this;
227
+ }
545
228
 
546
- set(this.#tracked_index, this.length, block);
547
- return result;
229
+ const copyCount = Math.min(end - start, length - target);
230
+
231
+ // If no elements are copied (start >= end or copyCount <= 0), return early
232
+ if (start >= end || copyCount <= 0) {
233
+ return this;
234
+ }
235
+
236
+ for (let i = 0; i < copyCount; i++) {
237
+ const index = target + i;
238
+ // Only update if source and target are different positions
239
+ // to avoid unnecessary updates when copying onto itself
240
+ if (index !== start + i) {
241
+ this.#update_tracked_at_index({ array: this, i: index, block, tracked_elements });
242
+ }
243
+ }
244
+
245
+ return this;
246
+ }
247
+
248
+ /**
249
+ * @param {T} value
250
+ * @param {number} [start]
251
+ * @param {number} [end]
252
+ * @returns {this}
253
+ */
254
+ fill(value, start, end) {
255
+ var block = safe_scope();
256
+ var tracked_elements = this.#tracked_elements;
257
+ var length = this.length;
258
+
259
+ // avoid unexpected behavior with method args being undefined
260
+ if (value === undefined && start === undefined && end === undefined) {
261
+ // @ts-ignore
262
+ super.fill();
263
+ } else if (start === undefined && end === undefined) {
264
+ super.fill(value);
265
+ } else if (end === undefined) {
266
+ super.fill(value, start);
267
+ } else {
268
+ super.fill(value, start, end);
269
+ }
270
+
271
+ let actual_start = 0;
272
+ if (start !== undefined) {
273
+ if (start < 0) {
274
+ actual_start = Math.max(length + start, 0);
275
+ } else {
276
+ actual_start = Math.min(start, length);
277
+ }
278
+ }
279
+
280
+ let actual_end = length;
281
+ if (end !== undefined) {
282
+ if (end < 0) {
283
+ actual_end = Math.max(length + end, 0);
284
+ } else {
285
+ actual_end = Math.min(end, length);
286
+ }
287
+ }
288
+
289
+ for (let i = actual_start; i < actual_end; i++) {
290
+ if (tracked_elements[i] === undefined) {
291
+ tracked_elements[i] = tracked(this[i], block);
292
+ } else {
293
+ set(tracked_elements[i], this[i], block);
294
+ }
295
+ }
296
+
297
+ return this;
298
+ }
299
+
300
+ reverse() {
301
+ var result = /** @type {RippleArray<T>} */ (super.reverse());
302
+ this.#update_all_tracked_from_array(result);
303
+ return result;
304
+ }
305
+
306
+ /**
307
+ * @param {(a: T, b: T) => number} [fn]
308
+ * @returns {this}
309
+ */
310
+ sort(fn) {
311
+ var result = super.sort(fn);
312
+ this.#update_all_tracked_from_array(result);
313
+ return result;
314
+ }
315
+
316
+ /**
317
+ * @param {RippleArray<T>} array
318
+ * @returns {RippleArray<T>}
319
+ */
320
+ #update_all_tracked_from_array(array) {
321
+ var block = safe_scope();
322
+ var tracked_elements = this.#tracked_elements;
323
+
324
+ for (var i = 0; i < array.length; i++) {
325
+ this.#update_tracked_at_index({ array, i, block, tracked_elements });
326
+ }
327
+ return array;
328
+ }
329
+
330
+ /**
331
+ * @param {Object} param0
332
+ * @param {RippleArray<T>} param0.array
333
+ * @param {number} param0.i
334
+ * @param {Block} param0.block
335
+ * @param {Tracked[]} param0.tracked_elements
336
+ */
337
+ #update_tracked_at_index({
338
+ array,
339
+ i,
340
+ block = safe_scope(),
341
+ tracked_elements = this.#tracked_elements,
342
+ }) {
343
+ if (i in array) {
344
+ if (tracked_elements[i] === undefined) {
345
+ tracked_elements[i] = tracked(array[i], block);
346
+ } else {
347
+ set(tracked_elements[i], array[i], block);
348
+ }
349
+ } else {
350
+ if (tracked_elements[i] !== undefined) {
351
+ set(tracked_elements[i], undefined, block);
352
+ }
353
+ delete tracked_elements[i];
354
+ }
355
+ }
356
+
357
+ /**
358
+ * @param {...T} elements
359
+ * @returns {number}
360
+ */
361
+ unshift(...elements) {
362
+ var block = safe_scope();
363
+ var tracked_elements = this.#tracked_elements;
364
+ var length = this.length;
365
+ var shift_len = elements.length;
366
+ var new_len = length + shift_len;
367
+
368
+ super.unshift(...elements);
369
+
370
+ // extend the array to fit the new elements
371
+ tracked_elements.push(...elements.map(() => tracked(undefined, block)));
372
+
373
+ // copy the existing ones to the end
374
+ for (let i = length - 1; i >= 0; i--) {
375
+ set(tracked_elements[i + shift_len], tracked_elements[i].v, block);
376
+ }
377
+
378
+ // set new values at the start
379
+ for (let i = shift_len - 1; i >= 0; i--) {
380
+ set(tracked_elements[i], elements[i], block);
381
+ }
382
+
383
+ set(this.#tracked_index, new_len, block);
384
+ return new_len;
385
+ }
386
+
387
+ shift() {
388
+ var block = safe_scope();
389
+ var tracked_elements = this.#tracked_elements;
390
+
391
+ var result = super.shift();
392
+ for (var i = 0; i < tracked_elements.length; i++) {
393
+ // the last must be set to undefined
394
+ set(tracked_elements[i], tracked_elements[i + 1]?.v, block);
395
+ }
396
+ tracked_elements.pop();
397
+
398
+ set(this.#tracked_index, this.length, block);
399
+ return result;
400
+ }
401
+
402
+ /**
403
+ * @param {...T} elements
404
+ * @returns {number}
405
+ */
406
+ push(...elements) {
407
+ var block = safe_scope();
408
+ var start_index = this.length;
409
+ var tracked_elements = this.#tracked_elements;
410
+
411
+ super.push(...elements);
412
+
413
+ for (var i = 0; i < elements.length; i++) {
414
+ if (!(i in elements)) {
415
+ continue;
416
+ }
417
+ tracked_elements[start_index + i] = tracked(elements[i], block);
418
+ }
419
+ var length = this.length;
420
+ set(this.#tracked_index, this.length, block);
421
+ return length;
422
+ }
423
+
424
+ pop() {
425
+ var block = safe_scope();
426
+ var tracked_elements = this.#tracked_elements;
427
+ var length = tracked_elements.length;
428
+ var result = super.pop();
429
+
430
+ if (length > 0 && tracked_elements[length - 1] !== undefined) {
431
+ set(tracked_elements[length - 1], undefined, block);
432
+ }
433
+ tracked_elements.pop();
434
+
435
+ set(this.#tracked_index, this.length, block);
436
+ return result;
437
+ }
438
+
439
+ /**
440
+ * Assigns value at index
441
+ * Same as bracket [] assignment
442
+ * Supports negative index to count back from the end
443
+ * @param {number} index
444
+ * @param {T} value
445
+ * @returns {T}
446
+ */
447
+ [ARRAY_SET_INDEX_AT](index, value) {
448
+ var block = safe_scope();
449
+ var tracked_elements = this.#tracked_elements;
450
+ var length = this.length;
451
+ var init_index = index;
452
+
453
+ if (!Number.isInteger(index)) {
454
+ throw new TypeError('Provided index must be a valid integer');
455
+ }
456
+
457
+ if (init_index < -length) {
458
+ throw new RangeError('Provided negative index out of bounds');
459
+ }
460
+
461
+ index = index < 0 ? index + length : index;
462
+
463
+ super[index] = value;
464
+
465
+ if (tracked_elements[index] === undefined) {
466
+ tracked_elements[index] = tracked(value, block);
467
+ } else {
468
+ set(tracked_elements[index], value, block);
469
+ }
470
+
471
+ if (this.length > length) {
472
+ set(this.#tracked_index, this.length, block);
473
+ }
474
+
475
+ return value;
476
+ }
477
+
478
+ /**
479
+ * @param {number} index
480
+ * @returns {T | undefined}
481
+ */
482
+ at(index) {
483
+ var tracked_elements = this.#tracked_elements;
484
+ var normalized = index < 0 ? index + this.length : index;
485
+
486
+ if (tracked_elements[normalized] !== undefined) {
487
+ get(tracked_elements[normalized]);
488
+ }
489
+ this.$length;
490
+
491
+ return super.at(index);
492
+ }
493
+
494
+ /**
495
+ * @param {number} start
496
+ * @param {number} [delete_count]
497
+ * @param {...T} elements
498
+ * @returns {Array<T>}
499
+ */
500
+ splice(start, delete_count, ...elements) {
501
+ var block = safe_scope();
502
+ var tracked_elements = this.#tracked_elements;
503
+ var tracked_len = tracked_elements.length;
504
+ var before_len = this.length;
505
+ var el_len = elements.length;
506
+ var result;
507
+
508
+ if (start !== undefined && delete_count === undefined && !el_len) {
509
+ // we can't just call super.splice(start, delete_count, ...elements)
510
+ // delete_count if undefined will be converted to 0 and nothing will be removed
511
+ result = super.splice(start);
512
+ } else if (start === undefined && delete_count === undefined && !el_len) {
513
+ // If start is undefined the native code will get converted to 0
514
+ // which would cause to remove all elements.
515
+ // Typically no sense to `.splice()` with no args as it does nothing
516
+ // since we get args as undefined, we need to handle this case
517
+
518
+ // @ts-ignore
519
+ result = super.splice();
520
+ } else {
521
+ // @ts-ignore
522
+ result = super.splice(start, delete_count, ...elements);
523
+ }
524
+
525
+ var after_len = this.length;
526
+ delete_count = result.length;
527
+
528
+ if (delete_count === 0 && !el_len) {
529
+ return result;
530
+ }
531
+
532
+ if (start < 0) {
533
+ start = Math.max(before_len + start, 0);
534
+ } else {
535
+ start = Math.min(start, before_len);
536
+ }
537
+
538
+ var range_end = el_len - delete_count === 0 ? start + el_len : Math.max(after_len, before_len);
539
+
540
+ for (let i = start; i < range_end; i++) {
541
+ this.#update_tracked_at_index({ array: this, i, block, tracked_elements });
542
+ }
543
+
544
+ tracked_elements.length = after_len;
545
+
546
+ set(this.#tracked_index, this.length, block);
547
+ return result;
548
+ }
549
+
550
+ get [TRACKED_OBJECT]() {
551
+ return this.#tracked_elements;
552
+ }
553
+
554
+ get $length() {
555
+ return get(this.#tracked_index);
556
+ }
557
+
558
+ /** @param {number} length */
559
+ set $length(length) {
560
+ if (length === this.length) {
561
+ return;
548
562
  }
549
563
 
550
- get [TRACKED_OBJECT]() {
551
- return this.#tracked_elements;
552
- }
553
-
554
- get $length() {
555
- return get(this.#tracked_index);
556
- }
557
-
558
- /** @param {number} length */
559
- set $length(length) {
560
- if (length === this.length) {
561
- return;
562
- }
563
-
564
- var block = safe_scope();
565
- var tracked_elements = this.#tracked_elements;
566
- var tracked_len = tracked_elements.length;
567
-
568
- if (length < tracked_len) {
569
- for (var i = length; i < tracked_len; i++) {
570
- if (tracked_elements[i] !== undefined) {
571
- set(tracked_elements[i], undefined, block);
572
- }
573
- }
574
- }
575
-
576
- this.length = length;
577
- tracked_elements.length = length;
578
- set(this.#tracked_index, length, block);
579
- }
580
-
581
- /** @param {number} _ */
582
- set length(_) {
583
- // This doesn't actually work because length cannot be overridden.
584
- // This error is now moved to runtime to catch direct assignments to length
585
- throw new Error('Cannot set length on RippleArray, use $length instead');
586
- }
587
-
588
- toJSON() {
589
- this.$length;
590
- return get_all_elements(this);
591
- }
564
+ var block = safe_scope();
565
+ var tracked_elements = this.#tracked_elements;
566
+ var tracked_len = tracked_elements.length;
567
+
568
+ if (length < tracked_len) {
569
+ for (var i = length; i < tracked_len; i++) {
570
+ if (tracked_elements[i] !== undefined) {
571
+ set(tracked_elements[i], undefined, block);
572
+ }
573
+ }
574
+ }
575
+
576
+ this.length = length;
577
+ tracked_elements.length = length;
578
+ set(this.#tracked_index, length, block);
579
+ }
580
+
581
+ /** @param {number} _ */
582
+ set length(_) {
583
+ // This doesn't actually work because length cannot be overridden.
584
+ // This error is now moved to runtime to catch direct assignments to length
585
+ throw new Error('Cannot set length on RippleArray, use $length instead');
586
+ }
587
+
588
+ toJSON() {
589
+ this.$length;
590
+ return get_all_elements(this);
591
+ }
592
592
  }
593
593
 
594
594
  /**
@@ -597,22 +597,22 @@ export class RippleArray extends Array {
597
597
  * @returns {T[]}
598
598
  */
599
599
  export function get_all_elements(array) {
600
- /** @type {Tracked[]} */
601
- var tracked_elements = /** @type {Tracked[]} */ (array[TRACKED_OBJECT]);
602
- // pre-allocate to support holey arrays
603
- var result = new Array(array.length);
604
-
605
- for (var i = 0; i < array.length; i++) {
606
- if (tracked_elements[i] !== undefined) {
607
- get(tracked_elements[i]);
608
- }
609
-
610
- if (i in array) {
611
- result[i] = array[i];
612
- }
613
- }
614
-
615
- return result;
600
+ /** @type {Tracked[]} */
601
+ var tracked_elements = /** @type {Tracked[]} */ (array[TRACKED_OBJECT]);
602
+ // pre-allocate to support holey arrays
603
+ var result = new Array(array.length);
604
+
605
+ for (var i = 0; i < array.length; i++) {
606
+ if (tracked_elements[i] !== undefined) {
607
+ get(tracked_elements[i]);
608
+ }
609
+
610
+ if (i in array) {
611
+ result[i] = array[i];
612
+ }
613
+ }
614
+
615
+ return result;
616
616
  }
617
617
 
618
618
  /**
@@ -620,14 +620,14 @@ export function get_all_elements(array) {
620
620
  * @param {RippleArray<T>} array
621
621
  * @returns {void}
622
622
  */
623
- function establish_trackable_deps (array) {
624
- var tracked_elements = array[TRACKED_OBJECT];
625
-
626
- for (var i = 0; i < tracked_elements.length; i++) {
627
- if (tracked_elements[i] !== undefined) {
628
- get(tracked_elements[i]);
629
- }
630
- }
623
+ function establish_trackable_deps(array) {
624
+ var tracked_elements = array[TRACKED_OBJECT];
625
+
626
+ for (var i = 0; i < tracked_elements.length; i++) {
627
+ if (tracked_elements[i] !== undefined) {
628
+ get(tracked_elements[i]);
629
+ }
630
+ }
631
631
  }
632
632
 
633
633
  /**
@@ -636,20 +636,20 @@ function establish_trackable_deps (array) {
636
636
  * @returns {RippleArray<T>}
637
637
  */
638
638
  function get_instance_from_static(array) {
639
- /** @type RippleArray<T> */
640
- var result;
641
- /** @type {T | void} */
642
- var first = get_first_if_length(array);
643
-
644
- if (first) {
645
- result = new RippleArray();
646
- result[0] = first;
647
- result[INIT_AFTER_NEW]();
648
- } else {
649
- result = new RippleArray(...array);
650
- }
651
-
652
- return result;
639
+ /** @type RippleArray<T> */
640
+ var result;
641
+ /** @type {T | void} */
642
+ var first = get_first_if_length(array);
643
+
644
+ if (first) {
645
+ result = new RippleArray();
646
+ result[0] = first;
647
+ result[INIT_AFTER_NEW]();
648
+ } else {
649
+ result = new RippleArray(...array);
650
+ }
651
+
652
+ return result;
653
653
  }
654
654
 
655
655
  /**
@@ -657,14 +657,16 @@ function get_instance_from_static(array) {
657
657
  * @param {T[]} array
658
658
  * @returns {T | void}
659
659
  */
660
- function get_first_if_length (array) {
661
- var first = array[0];
662
-
663
- if (
664
- array.length === 1 && (0 in array) && Number.isInteger(first)
665
- && /** @type {number} */ (first) >= 0
666
- && /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
667
- ) {
668
- return first;
669
- }
660
+ function get_first_if_length(array) {
661
+ var first = array[0];
662
+
663
+ if (
664
+ array.length === 1 &&
665
+ 0 in array &&
666
+ Number.isInteger(first) &&
667
+ /** @type {number} */ (first) >= 0 &&
668
+ /** @type {number} */ (first) <= MAX_ARRAY_LENGTH
669
+ ) {
670
+ return first;
671
+ }
670
672
  }