ripple 0.2.53 → 0.2.54
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/package.json +1 -1
- package/src/compiler/phases/2-analyze/index.js +1 -32
- package/src/compiler/phases/3-transform/index.js +13 -84
- package/src/compiler/phases/3-transform/stylesheet.js +16 -3
- package/src/compiler/utils.js +0 -20
- package/src/runtime/array.js +158 -610
- package/src/runtime/index.js +3 -3
- package/src/runtime/internal/client/constants.js +1 -1
- package/src/runtime/internal/client/for.js +5 -5
- package/src/runtime/internal/client/index.js +1 -11
- package/src/runtime/internal/client/operations.js +0 -3
- package/src/runtime/internal/client/portal.js +5 -6
- package/src/runtime/internal/client/runtime.js +48 -315
- package/src/runtime/internal/client/utils.js +0 -10
- package/src/runtime/map.js +28 -15
- package/src/runtime/set.js +47 -21
- package/tests/array.test.ripple +75 -187
- package/tests/basic.test.ripple +33 -9
- package/tests/compiler.test.ripple +5 -5
- package/tests/composite.test.ripple +253 -3
- package/tests/for.test.ripple +3 -3
- package/tests/map.test.ripple +10 -10
- package/tests/ref.test.ripple +3 -3
- package/tests/set.test.ripple +7 -7
- package/types/index.d.ts +18 -31
package/tests/basic.test.ripple
CHANGED
|
@@ -130,7 +130,7 @@ describe('basic', () => {
|
|
|
130
130
|
|
|
131
131
|
expect(Array.from(div.classList).some(className => className.startsWith('ripple-'))).toBe(true);
|
|
132
132
|
expect(div.classList.contains('inactive')).toBe(true);
|
|
133
|
-
|
|
133
|
+
|
|
134
134
|
button.click();
|
|
135
135
|
flushSync();
|
|
136
136
|
expect(div.classList.contains('active')).toBe(true);
|
|
@@ -203,15 +203,15 @@ describe('basic', () => {
|
|
|
203
203
|
|
|
204
204
|
const input = container.querySelector('input');
|
|
205
205
|
const html = container.innerHTML;
|
|
206
|
-
|
|
206
|
+
|
|
207
207
|
expect(input.getAttribute('name')).toBe('car');
|
|
208
208
|
expect(input.getAttribute('type')).toBe('checkbox');
|
|
209
209
|
expect(input.getAttribute('id')).toBe('vehicle1');
|
|
210
210
|
expect(input.getAttribute('value')).toBe('Bike');
|
|
211
|
-
|
|
211
|
+
|
|
212
212
|
expect(html).not.toContain('type="checkbox"type="checkbox"');
|
|
213
213
|
expect(html).not.toContain('value="Bike"value="Bike"');
|
|
214
|
-
|
|
214
|
+
|
|
215
215
|
expect(container).toMatchSnapshot();
|
|
216
216
|
});
|
|
217
217
|
|
|
@@ -381,7 +381,7 @@ describe('basic', () => {
|
|
|
381
381
|
|
|
382
382
|
// TODO
|
|
383
383
|
it.skip('renders multiple reactive lexical blocks with complexity', () => {
|
|
384
|
-
|
|
384
|
+
|
|
385
385
|
component Basic() {
|
|
386
386
|
const count = 'count';
|
|
387
387
|
|
|
@@ -390,16 +390,16 @@ describe('basic', () => {
|
|
|
390
390
|
count: track(0)
|
|
391
391
|
};
|
|
392
392
|
|
|
393
|
-
<span>{obj[count]}</span>
|
|
393
|
+
<span>{obj[@count]}</span>
|
|
394
394
|
</div>
|
|
395
395
|
<div>
|
|
396
396
|
let b = {
|
|
397
397
|
count: track(0)
|
|
398
398
|
};
|
|
399
399
|
|
|
400
|
-
<button onClick={() => { b[count]-- }}>{'-'}</button>
|
|
401
|
-
<span class='count'>{b[count]}</span>
|
|
402
|
-
<button onClick={() => { b[count]++ }}>{'+'}</button>
|
|
400
|
+
<button onClick={() => { b[@count]-- }}>{'-'}</button>
|
|
401
|
+
<span class='count'>{b[@count]}</span>
|
|
402
|
+
<button onClick={() => { b[@count]++ }}>{'+'}</button>
|
|
403
403
|
</div>
|
|
404
404
|
}
|
|
405
405
|
render(Basic);
|
|
@@ -880,6 +880,30 @@ describe('basic', () => {
|
|
|
880
880
|
expect(paragraph.textContent).toBe('Styled paragraph');
|
|
881
881
|
});
|
|
882
882
|
|
|
883
|
+
it('renders with keyframes in styling scoped to component', () => {
|
|
884
|
+
const source = `export component Basic() {
|
|
885
|
+
<div>
|
|
886
|
+
<p>{'Styled paragraph'}</p>
|
|
887
|
+
</div>
|
|
888
|
+
|
|
889
|
+
<style>
|
|
890
|
+
div {
|
|
891
|
+
animation-name: anim;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
@keyframes anim {}
|
|
895
|
+
|
|
896
|
+
p {
|
|
897
|
+
animation-name: anim;
|
|
898
|
+
}
|
|
899
|
+
</style>
|
|
900
|
+
}`;
|
|
901
|
+
|
|
902
|
+
const { css } = compile(source, 'test.ripple');
|
|
903
|
+
const name = css.match(/@keyframes\s+([a-zA-Z0-9_-]+)\s*\{/)[1];
|
|
904
|
+
expect(css.match(new RegExp(name, 'g'))?.length).toEqual(3);
|
|
905
|
+
});
|
|
906
|
+
|
|
883
907
|
it('renders with mixed static and dynamic content', () => {
|
|
884
908
|
component Basic() {
|
|
885
909
|
let name = track('World');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount,
|
|
2
|
+
import { mount, TrackedArray, track } from 'ripple';
|
|
3
3
|
import { parse } from 'ripple/compiler'
|
|
4
4
|
|
|
5
5
|
describe('compiler success tests', () => {
|
|
@@ -21,17 +21,17 @@ describe('compiler success tests', () => {
|
|
|
21
21
|
container = null;
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
it('Parses style content correctly', () => {
|
|
26
26
|
const source = `export component App() {
|
|
27
27
|
<div id="myid" class="myclass">{"Hello World"}</div>
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
<style>#style</style>
|
|
30
30
|
}`;
|
|
31
31
|
const style1 = '.myid {color: green }';
|
|
32
32
|
const style2 = '#myid {color: green }';
|
|
33
33
|
const style3 = 'div {color: green }';
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
let input = source.replace('#style', style1);
|
|
36
36
|
let ast = parse(input);
|
|
37
37
|
expect(ast.body[0].declaration.css.source).toEqual(style1);
|
|
@@ -111,7 +111,7 @@ describe('compiler success tests', () => {
|
|
|
111
111
|
let items = [];
|
|
112
112
|
let tracked_items = track([]);
|
|
113
113
|
let items2 = new Array();
|
|
114
|
-
let items3 = new
|
|
114
|
+
let items3 = new TrackedArray();
|
|
115
115
|
let i = 0;
|
|
116
116
|
|
|
117
117
|
logs.push(items[0]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync, track,
|
|
2
|
+
import { mount, flushSync, track, TrackedArray, TrackedMap, effect } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('composite components', () => {
|
|
5
5
|
let container;
|
|
@@ -287,7 +287,7 @@ describe('composite components', () => {
|
|
|
287
287
|
|
|
288
288
|
it('handlers generic', () => {
|
|
289
289
|
component ArrayTest() {
|
|
290
|
-
let items = new
|
|
290
|
+
let items = new TrackedArray<number>();
|
|
291
291
|
items.push.apply(items, [1, 2, 3, 4, 5]);
|
|
292
292
|
|
|
293
293
|
<pre>{items ? JSON.stringify(items) : 'Loading...'}</pre>
|
|
@@ -336,4 +336,254 @@ describe('composite components', () => {
|
|
|
336
336
|
expect(container.querySelector('div').textContent).toBe('2 3 4');
|
|
337
337
|
expect(logs).toEqual(['Child effect: 1, 2, 3', 'Child effect: 2, 3, 4']);
|
|
338
338
|
});
|
|
339
|
-
|
|
339
|
+
|
|
340
|
+
it('handles advanced generic ambiguity and edge cases', () => {
|
|
341
|
+
component App() {
|
|
342
|
+
// Ambiguous generics vs JSX / less-than parsing scenarios
|
|
343
|
+
|
|
344
|
+
// 1. Simple "new" with generic (should NOT become an <Element>)
|
|
345
|
+
const a = new TrackedArray<number>();
|
|
346
|
+
|
|
347
|
+
// 2. Multi-line generic with newline after '<'
|
|
348
|
+
const b = new TrackedArray<
|
|
349
|
+
string
|
|
350
|
+
>();
|
|
351
|
+
|
|
352
|
+
// // 3. Member expression + generic
|
|
353
|
+
// class List<T> {
|
|
354
|
+
// items: T[];
|
|
355
|
+
// constructor() {
|
|
356
|
+
// this.items = [];
|
|
357
|
+
// }
|
|
358
|
+
// }
|
|
359
|
+
// class Containers {
|
|
360
|
+
// List<T>() {
|
|
361
|
+
// return new List<T>();
|
|
362
|
+
// }
|
|
363
|
+
// }
|
|
364
|
+
|
|
365
|
+
// const c = new Containers.List<string>();
|
|
366
|
+
|
|
367
|
+
// 4. Chained call with generic method
|
|
368
|
+
const someSource = new Array<number>(1, 2, 3);
|
|
369
|
+
const d = someSource.map<number>((x) => x * 2).filter<boolean>((x) => !!x);
|
|
370
|
+
|
|
371
|
+
// 5. Nested generics
|
|
372
|
+
const e = new Map<string, Promise<number>>();
|
|
373
|
+
|
|
374
|
+
// // 6. Generic after a call expression result
|
|
375
|
+
// function getBuilder<T>() {
|
|
376
|
+
// return {
|
|
377
|
+
// build<U>() {
|
|
378
|
+
// return {
|
|
379
|
+
// build<V>() {
|
|
380
|
+
// return 42;
|
|
381
|
+
// }
|
|
382
|
+
// };
|
|
383
|
+
// }
|
|
384
|
+
// };
|
|
385
|
+
// }
|
|
386
|
+
// const f = getBuilder()<ResultType>().build<number>();
|
|
387
|
+
|
|
388
|
+
// // 7. Generic following optional chaining
|
|
389
|
+
// const maybe = {};
|
|
390
|
+
// const g = maybe?.factory<number>()?.make<boolean>();
|
|
391
|
+
|
|
392
|
+
// // 8. Comparison operator (ensure '<' here NOT misparsed as generics)
|
|
393
|
+
// let x = 10, y = 20;
|
|
394
|
+
// const h = x < y ? 'lt' : 'ge';
|
|
395
|
+
|
|
396
|
+
// // 9. Chained comparisons with intervening generics
|
|
397
|
+
// class Box<T> {
|
|
398
|
+
// value: T;
|
|
399
|
+
// constructor(value?: T) {
|
|
400
|
+
// this.value = value;
|
|
401
|
+
// }
|
|
402
|
+
// open<U>() {
|
|
403
|
+
// return new Box<U>();
|
|
404
|
+
// }
|
|
405
|
+
// }
|
|
406
|
+
// const limit = 100;
|
|
407
|
+
// const i = new Box<number>().value < limit ? 'ok' : 'no';
|
|
408
|
+
|
|
409
|
+
// 10. JSX / Element should still work
|
|
410
|
+
<div class="still-works">
|
|
411
|
+
<span>{a.length}</span>
|
|
412
|
+
</div>
|
|
413
|
+
|
|
414
|
+
// // 11. Generic function call vs Element: Identifier followed by generic args
|
|
415
|
+
// function identity<T>(value: T): T {
|
|
416
|
+
// return value;
|
|
417
|
+
// }
|
|
418
|
+
// const j = identity<number>(42);
|
|
419
|
+
|
|
420
|
+
// 12. Member + generic call immediately followed by another call
|
|
421
|
+
class Factory {
|
|
422
|
+
create<T>() {
|
|
423
|
+
return (value: T) => value;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const factory = new Factory();
|
|
427
|
+
const k = factory.create<number>()(123);
|
|
428
|
+
|
|
429
|
+
// // 13. Multiple generic segments in chain
|
|
430
|
+
// function foo<T>() {
|
|
431
|
+
// return {
|
|
432
|
+
// bar<U>() {
|
|
433
|
+
// return {
|
|
434
|
+
// baz<V>() {
|
|
435
|
+
// return true;
|
|
436
|
+
// }
|
|
437
|
+
// };
|
|
438
|
+
// }
|
|
439
|
+
// };
|
|
440
|
+
// }
|
|
441
|
+
// const l = foo<number>().bar<string>().baz<boolean>();
|
|
442
|
+
|
|
443
|
+
// 14. Generic with constraint + default
|
|
444
|
+
type Extractor<T extends { id: number } = { id: number }> = (v: T) => number;
|
|
445
|
+
const m: Extractor = (v) => v.id;
|
|
446
|
+
|
|
447
|
+
// // 15. Generic in angle after "new" + trailing call
|
|
448
|
+
// class Wrapper<T> {
|
|
449
|
+
// value: T;
|
|
450
|
+
// constructor() {
|
|
451
|
+
// this.value = null as unknown as T;
|
|
452
|
+
// }
|
|
453
|
+
// unwrap<U>() {
|
|
454
|
+
// return null as unknown as U;
|
|
455
|
+
// }
|
|
456
|
+
// }
|
|
457
|
+
// const n = new Wrapper<number>().unwrap<string>();
|
|
458
|
+
|
|
459
|
+
// // 16. Angle brackets inside type assertion vs generic call
|
|
460
|
+
// function getUnknown(): unknown {
|
|
461
|
+
// return { a: 1 };
|
|
462
|
+
// }
|
|
463
|
+
// getUnknown.factory = function<T>() {
|
|
464
|
+
// return {
|
|
465
|
+
// make<U>() {
|
|
466
|
+
// return 2;
|
|
467
|
+
// }
|
|
468
|
+
// };
|
|
469
|
+
// };
|
|
470
|
+
// const raw = getUnknown();
|
|
471
|
+
// const o = (raw as Map<string, number>).get('a');
|
|
472
|
+
|
|
473
|
+
// // 17. Generic with comma + trailing less-than comparison on next token
|
|
474
|
+
// class Pair<T1, T2> {
|
|
475
|
+
// first: T1;
|
|
476
|
+
// second: T2;
|
|
477
|
+
// constructor() {
|
|
478
|
+
// this.first = null as unknown as T1;
|
|
479
|
+
// this.second = null as unknown as T2;
|
|
480
|
+
// }
|
|
481
|
+
// }
|
|
482
|
+
// const p = new Pair<number, string>();
|
|
483
|
+
// const q = 1 < 2 ? p : null;
|
|
484
|
+
|
|
485
|
+
// // 18. Nested generics with line breaks resembling JSX indentation
|
|
486
|
+
// interface Node<T> {
|
|
487
|
+
// value: T;
|
|
488
|
+
// }
|
|
489
|
+
// interface Edge<W> {
|
|
490
|
+
// weight: W;
|
|
491
|
+
// }
|
|
492
|
+
// class Graph<N, E> {
|
|
493
|
+
// nodes: N[];
|
|
494
|
+
// edges: E[];
|
|
495
|
+
// constructor() {
|
|
496
|
+
// this.nodes = [];
|
|
497
|
+
// this.edges = [];
|
|
498
|
+
// }
|
|
499
|
+
// }
|
|
500
|
+
// const r = new Graph<
|
|
501
|
+
// Node<string>,
|
|
502
|
+
// Edge<number>
|
|
503
|
+
// >();
|
|
504
|
+
|
|
505
|
+
// // 19. Ternary containing generics in both branches
|
|
506
|
+
// let flag = true;
|
|
507
|
+
// const s = flag ? new Box<number>() : new Box<string>();
|
|
508
|
+
|
|
509
|
+
// 20. Generic inside template expression
|
|
510
|
+
const t = `length=${new TrackedArray<number>().length}`;
|
|
511
|
+
|
|
512
|
+
// 21. Optional chaining + generic + property access
|
|
513
|
+
const registry = new TrackedMap<string, number>();
|
|
514
|
+
const u = registry.get<number>('id')?.toString();
|
|
515
|
+
|
|
516
|
+
// // 22. Generic call used as callee for another call
|
|
517
|
+
// function make<T>() {
|
|
518
|
+
// return (value: T) => value;
|
|
519
|
+
// }
|
|
520
|
+
// const v = make<number>()('done');
|
|
521
|
+
|
|
522
|
+
// // 23. Generic followed by tagged template (ensure not confused with JSX)
|
|
523
|
+
// function tagFn<T>(strings: TemplateStringsArray, ...values: T[]) {
|
|
524
|
+
// return values[0];
|
|
525
|
+
// }
|
|
526
|
+
// const tagResult = tagFn<number>`value`;
|
|
527
|
+
|
|
528
|
+
// // 24. Sequence mixing: (a < b) + generic call in same statement
|
|
529
|
+
// function compute<T>(x: T, y: T): T {
|
|
530
|
+
// return y;
|
|
531
|
+
// }
|
|
532
|
+
|
|
533
|
+
// const w = (x < y) && compute<number>(x, y);
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
// Additional component focusing on edge crankers
|
|
538
|
+
|
|
539
|
+
// // 28. Generic after parenthesized new expression
|
|
540
|
+
// const aa = (new Box<number>()).open<string>();
|
|
541
|
+
|
|
542
|
+
// // 29. Generic chain right after closing paren of IIFE
|
|
543
|
+
// class Builder<Kind> {
|
|
544
|
+
// finalize<Result>() {
|
|
545
|
+
// return {
|
|
546
|
+
// result: null as unknown as Result
|
|
547
|
+
// };
|
|
548
|
+
// }
|
|
549
|
+
// }
|
|
550
|
+
// const builder = new Builder<Number>();
|
|
551
|
+
// const result = (function(){ return builder; })()<Number>().finalize<boolean>();
|
|
552
|
+
|
|
553
|
+
// // 30. Angle bracket start of conditional expression line
|
|
554
|
+
// function adjust<T>(value: T): T {
|
|
555
|
+
// return value;
|
|
556
|
+
// }
|
|
557
|
+
// const val =
|
|
558
|
+
// new Wrapper<number>()
|
|
559
|
+
// .value < 100
|
|
560
|
+
// ? adjust<number>(10)
|
|
561
|
+
// : adjust<number>(20);
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
// // 32. Generic with comments inside angle list
|
|
565
|
+
// class Mapper<Key, Value> {
|
|
566
|
+
// map: Map<Key, Value>;
|
|
567
|
+
// constructor() {
|
|
568
|
+
// this.map = new Map<Key, Value>();
|
|
569
|
+
// }
|
|
570
|
+
// }
|
|
571
|
+
// const gg = new Mapper<
|
|
572
|
+
// // key type
|
|
573
|
+
// string,
|
|
574
|
+
// /* value type */
|
|
575
|
+
// number
|
|
576
|
+
// >();
|
|
577
|
+
|
|
578
|
+
// // 33. Map of generic instance as key
|
|
579
|
+
// const mm = new Map<TrackedArray<number>, TrackedArray<string>>();
|
|
580
|
+
|
|
581
|
+
// // 34. Inline assertion then generic call
|
|
582
|
+
|
|
583
|
+
// const asserted = (getUnknown() as Factory).create<number>();
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
render(App);
|
|
588
|
+
});
|
|
589
|
+
});
|
package/tests/for.test.ripple
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
2
|
|
|
3
|
-
import { mount, flushSync,
|
|
3
|
+
import { mount, flushSync, TrackedArray } from 'ripple';
|
|
4
4
|
|
|
5
5
|
describe('for statements', () => {
|
|
6
6
|
let container;
|
|
@@ -37,13 +37,13 @@ describe('for statements', () => {
|
|
|
37
37
|
|
|
38
38
|
it('render a simple dynamic array', () => {
|
|
39
39
|
component App() {
|
|
40
|
-
const items = new
|
|
40
|
+
const items = new TrackedArray('Item 1', 'Item 2', 'Item 3');
|
|
41
41
|
|
|
42
42
|
for (const item of items) {
|
|
43
43
|
<div class={item}>{item}</div>
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
<button onClick={() => items.push(`Item ${items
|
|
46
|
+
<button onClick={() => items.push(`Item ${items.length + 1}`)}>{'Add Item'}</button>
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
render(App);
|
package/tests/map.test.ripple
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync,
|
|
2
|
+
import { mount, flushSync, TrackedMap, track } from 'ripple';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
4
|
+
describe('TrackedMap', () => {
|
|
5
5
|
let container;
|
|
6
6
|
|
|
7
7
|
function render(component) {
|
|
@@ -20,11 +20,11 @@ describe('RippleMap', () => {
|
|
|
20
20
|
container = null;
|
|
21
21
|
});
|
|
22
22
|
|
|
23
|
-
it('handles set with update and delete operations with a reactive
|
|
23
|
+
it('handles set with update and delete operations with a reactive size var', () => {
|
|
24
24
|
component MapTest() {
|
|
25
|
-
let map = new
|
|
25
|
+
let map = new TrackedMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
26
26
|
let value = track(() => map.get('a'));
|
|
27
|
-
let size = track(() => map
|
|
27
|
+
let size = track(() => map.size);
|
|
28
28
|
|
|
29
29
|
<button onClick={() => map.set('d', 4)}>{'set'}</button>
|
|
30
30
|
<button onClick={() => map.delete('b')}>{'delete'}</button>
|
|
@@ -61,10 +61,10 @@ describe('RippleMap', () => {
|
|
|
61
61
|
|
|
62
62
|
it('handles clear operation', () => {
|
|
63
63
|
component MapTest() {
|
|
64
|
-
let map = new
|
|
64
|
+
let map = new TrackedMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
65
65
|
|
|
66
66
|
<button onClick={() => map.clear()}>{'clear'}</button>
|
|
67
|
-
<pre>{map
|
|
67
|
+
<pre>{map.size}</pre>
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
render(MapTest);
|
|
@@ -79,7 +79,7 @@ describe('RippleMap', () => {
|
|
|
79
79
|
|
|
80
80
|
it('handles has operation and tracks reactive $has', () => {
|
|
81
81
|
component MapTest() {
|
|
82
|
-
let map = new
|
|
82
|
+
let map = new TrackedMap([['a', 1], ['b', 2], ['c', 3]]);
|
|
83
83
|
let has = track(() => map.has('b'));
|
|
84
84
|
|
|
85
85
|
<button onClick={() => map.delete('b')}>{'delete'}</button>
|
|
@@ -99,7 +99,7 @@ describe('RippleMap', () => {
|
|
|
99
99
|
|
|
100
100
|
it('handles reactivity of keys, values, and entries', () => {
|
|
101
101
|
component MapTest() {
|
|
102
|
-
let map = new
|
|
102
|
+
let map = new TrackedMap([['x', 10], ['y', 20]]);
|
|
103
103
|
let keys = track(() => Array.from(map.keys()));
|
|
104
104
|
let values = track(() => Array.from(map.values()));
|
|
105
105
|
let entries = track(() => Array.from(map.entries()));
|
|
@@ -129,7 +129,7 @@ describe('RippleMap', () => {
|
|
|
129
129
|
|
|
130
130
|
it('toJSON returns correct object', () => {
|
|
131
131
|
component MapTest() {
|
|
132
|
-
let map = new
|
|
132
|
+
let map = new TrackedMap([['foo', 1], ['bar', 2]]);
|
|
133
133
|
|
|
134
134
|
<pre>{JSON.stringify(map)}</pre>
|
|
135
135
|
}
|
package/tests/ref.test.ripple
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync,
|
|
2
|
+
import { mount, flushSync, TrackedArray } from 'ripple';
|
|
3
3
|
|
|
4
4
|
describe('refs', () => {
|
|
5
5
|
let container;
|
|
@@ -35,7 +35,7 @@ describe('refs', () => {
|
|
|
35
35
|
let _node;
|
|
36
36
|
|
|
37
37
|
component Component() {
|
|
38
|
-
let items =
|
|
38
|
+
let items = TrackedArray.from([1, 2, 3]);
|
|
39
39
|
|
|
40
40
|
function componentRef(node) {
|
|
41
41
|
_node = node;
|
|
@@ -47,7 +47,7 @@ describe('refs', () => {
|
|
|
47
47
|
component Child(props) {
|
|
48
48
|
const { items, ...rest } = props;
|
|
49
49
|
<pre {...rest}>{JSON.stringify(items)}</pre>
|
|
50
|
-
<pre>{items
|
|
50
|
+
<pre>{items.length}</pre>
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
render(Component);
|
package/tests/set.test.ripple
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
-
import { mount, flushSync,
|
|
2
|
+
import { mount, flushSync, TrackedSet, track } from 'ripple';
|
|
3
3
|
|
|
4
|
-
describe('
|
|
4
|
+
describe('TrackedSet', () => {
|
|
5
5
|
let container;
|
|
6
6
|
|
|
7
7
|
function render(component) {
|
|
@@ -22,7 +22,7 @@ describe('RippleSet', () => {
|
|
|
22
22
|
|
|
23
23
|
it('handles add and delete operations', () => {
|
|
24
24
|
component SetTest() {
|
|
25
|
-
let items = new
|
|
25
|
+
let items = new TrackedSet([1, 2, 3]);
|
|
26
26
|
|
|
27
27
|
<button onClick={() => items.add(4)}>{'add'}</button>
|
|
28
28
|
<button onClick={() => items.delete(2)}>{'delete'}</button>
|
|
@@ -31,7 +31,7 @@ describe('RippleSet', () => {
|
|
|
31
31
|
|
|
32
32
|
component Child({ items }) {
|
|
33
33
|
<pre>{JSON.stringify(items)}</pre>
|
|
34
|
-
<pre>{items
|
|
34
|
+
<pre>{items.size}</pre>
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
render(SetTest);
|
|
@@ -54,7 +54,7 @@ describe('RippleSet', () => {
|
|
|
54
54
|
|
|
55
55
|
it('handles clear operation', () => {
|
|
56
56
|
component SetTest() {
|
|
57
|
-
let items = new
|
|
57
|
+
let items = new TrackedSet([1, 2, 3]);
|
|
58
58
|
|
|
59
59
|
<button onClick={() => items.clear()}>{'clear'}</button>
|
|
60
60
|
<Child items={items} />
|
|
@@ -62,7 +62,7 @@ describe('RippleSet', () => {
|
|
|
62
62
|
|
|
63
63
|
component Child({ items }) {
|
|
64
64
|
<pre>{JSON.stringify(items)}</pre>
|
|
65
|
-
<pre>{items
|
|
65
|
+
<pre>{items.size}</pre>
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
render(SetTest);
|
|
@@ -78,7 +78,7 @@ describe('RippleSet', () => {
|
|
|
78
78
|
|
|
79
79
|
it('handles has operation', () => {
|
|
80
80
|
component SetTest() {
|
|
81
|
-
let items = new
|
|
81
|
+
let items = new TrackedSet([1, 2, 3]);
|
|
82
82
|
let hasValue = track(() => items.has(2));
|
|
83
83
|
|
|
84
84
|
<button onClick={() => items.delete(2)}>{'delete'}</button>
|
package/types/index.d.ts
CHANGED
|
@@ -11,27 +11,17 @@ export declare function flushSync<T>(fn: () => T): T;
|
|
|
11
11
|
|
|
12
12
|
export declare function effect(fn: (() => void) | (() => () => void)): void;
|
|
13
13
|
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
thisArg?: any,
|
|
20
|
-
): RippleArray<U>;
|
|
21
|
-
static from<T>(iterable: Iterable<T>): RippleArray<T>;
|
|
22
|
-
static from<T, U>(
|
|
23
|
-
iterable: Iterable<T>,
|
|
24
|
-
mapFn: (v: T, k: number) => U,
|
|
25
|
-
thisArg?: any,
|
|
26
|
-
): RippleArray<U>;
|
|
27
|
-
|
|
28
|
-
static of<T>(...items: T[]): RippleArray<T>;
|
|
29
|
-
|
|
30
|
-
$length: number;
|
|
31
|
-
|
|
32
|
-
toJSON(): T[];
|
|
14
|
+
export interface TrackedArrayConstructor {
|
|
15
|
+
new <T>(...elements: T[]): TrackedArray<T>; // must be used with `new`
|
|
16
|
+
from<T>(arrayLike: ArrayLike<T>): TrackedArray<T>;
|
|
17
|
+
of<T>(...items: T[]): TrackedArray<T>;
|
|
18
|
+
fromAsync<T>(iterable: AsyncIterable<T>): Promise<TrackedArray<T>>;
|
|
33
19
|
}
|
|
34
20
|
|
|
21
|
+
export interface TrackedArray<T> extends Array<T> {}
|
|
22
|
+
|
|
23
|
+
export declare const TrackedArray: TrackedArrayConstructor;
|
|
24
|
+
|
|
35
25
|
export type Context<T> = {
|
|
36
26
|
get(): T;
|
|
37
27
|
set(value: T): void;
|
|
@@ -39,20 +29,18 @@ export type Context<T> = {
|
|
|
39
29
|
|
|
40
30
|
export declare function createContext<T>(initialValue: T): Context<T>;
|
|
41
31
|
|
|
42
|
-
export class
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
union(other: RippleSet<T> | Set<T>): RippleSet<T>;
|
|
32
|
+
export class TrackedSet<T> extends Set<T> {
|
|
33
|
+
isDisjointFrom(other: TrackedSet<T> | Set<T>): boolean;
|
|
34
|
+
isSubsetOf(other: TrackedSet<T> | Set<T>): boolean;
|
|
35
|
+
isSupersetOf(other: TrackedSet<T> | Set<T>): boolean;
|
|
36
|
+
difference(other: TrackedSet<T> | Set<T>): TrackedSet<T>;
|
|
37
|
+
intersection(other: TrackedSet<T> | Set<T>): TrackedSet<T>;
|
|
38
|
+
symmetricDifference(other: TrackedSet<T> | Set<T>): TrackedSet<T>;
|
|
39
|
+
union(other: TrackedSet<T> | Set<T>): TrackedSet<T>;
|
|
51
40
|
toJSON(): T[];
|
|
52
41
|
}
|
|
53
42
|
|
|
54
|
-
export class
|
|
55
|
-
get $size(): number;
|
|
43
|
+
export class TrackedMap<K, V> extends Map<K, V> {
|
|
56
44
|
toJSON(): [K, V][];
|
|
57
45
|
}
|
|
58
46
|
|
|
@@ -70,7 +58,6 @@ declare global {
|
|
|
70
58
|
*/
|
|
71
59
|
var $: {
|
|
72
60
|
tracked<T>(value: T, block?: any): T;
|
|
73
|
-
tracked_object<T extends Record<string, any>>(obj: T, props: string[], block?: any): T;
|
|
74
61
|
computed<T>(fn: () => T, block?: any): T;
|
|
75
62
|
scope(): any;
|
|
76
63
|
get_tracked(node: any): any;
|