ripple 0.3.7 → 0.3.9

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 (119) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/package.json +2 -2
  3. package/src/compiler/phases/1-parse/index.js +48 -349
  4. package/src/compiler/phases/2-analyze/index.js +343 -52
  5. package/src/compiler/phases/3-transform/client/index.js +28 -160
  6. package/src/compiler/phases/3-transform/segments.js +0 -7
  7. package/src/compiler/phases/3-transform/server/index.js +31 -154
  8. package/src/compiler/types/acorn.d.ts +1 -1
  9. package/src/compiler/types/estree.d.ts +1 -1
  10. package/src/compiler/types/import.d.ts +0 -2
  11. package/src/compiler/types/index.d.ts +5 -17
  12. package/src/compiler/types/parse.d.ts +1 -17
  13. package/src/compiler/utils.js +53 -20
  14. package/src/runtime/index-client.js +2 -13
  15. package/src/runtime/index-server.js +2 -2
  16. package/src/runtime/internal/client/bindings.js +3 -1
  17. package/src/runtime/internal/client/composite.js +3 -2
  18. package/src/runtime/internal/client/events.js +1 -1
  19. package/src/runtime/internal/client/head.js +3 -4
  20. package/src/runtime/internal/client/index.js +0 -1
  21. package/src/runtime/internal/client/runtime.js +0 -52
  22. package/src/runtime/internal/server/index.js +31 -55
  23. package/tests/client/array/array.copy-within.test.ripple +12 -12
  24. package/tests/client/array/array.derived.test.ripple +46 -46
  25. package/tests/client/array/array.iteration.test.ripple +10 -10
  26. package/tests/client/array/array.mutations.test.ripple +20 -20
  27. package/tests/client/array/array.to-methods.test.ripple +6 -6
  28. package/tests/client/async-suspend.test.ripple +5 -5
  29. package/tests/client/basic/basic.attributes.test.ripple +81 -81
  30. package/tests/client/basic/basic.collections.test.ripple +9 -9
  31. package/tests/client/basic/basic.components.test.ripple +28 -28
  32. package/tests/client/basic/basic.errors.test.ripple +46 -18
  33. package/tests/client/basic/basic.events.test.ripple +37 -37
  34. package/tests/client/basic/basic.get-set.test.ripple +6 -6
  35. package/tests/client/basic/basic.reactivity.test.ripple +58 -203
  36. package/tests/client/basic/basic.rendering.test.ripple +19 -19
  37. package/tests/client/basic/basic.utilities.test.ripple +3 -3
  38. package/tests/client/boundaries.test.ripple +12 -12
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.ripple.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.ripple +19 -19
  41. package/tests/client/compiler/compiler.basic.test.ripple +46 -27
  42. package/tests/client/compiler/compiler.tracked-access.test.ripple +2 -2
  43. package/tests/client/composite/composite.dynamic-components.test.ripple +9 -9
  44. package/tests/client/composite/composite.props.test.ripple +14 -16
  45. package/tests/client/composite/composite.reactivity.test.ripple +69 -70
  46. package/tests/client/composite/composite.render.test.ripple +3 -3
  47. package/tests/client/computed-properties.test.ripple +4 -4
  48. package/tests/client/date.test.ripple +42 -42
  49. package/tests/client/dynamic-elements.test.ripple +44 -45
  50. package/tests/client/events.test.ripple +70 -70
  51. package/tests/client/for.test.ripple +25 -25
  52. package/tests/client/head.test.ripple +19 -19
  53. package/tests/client/html.test.ripple +3 -3
  54. package/tests/client/input-value.test.ripple +84 -84
  55. package/tests/client/lazy-destructuring.test.ripple +138 -26
  56. package/tests/client/map.test.ripple +16 -16
  57. package/tests/client/media-query.test.ripple +7 -7
  58. package/tests/client/portal.test.ripple +11 -11
  59. package/tests/client/ref.test.ripple +4 -4
  60. package/tests/client/return.test.ripple +52 -52
  61. package/tests/client/set.test.ripple +6 -6
  62. package/tests/client/svg.test.ripple +5 -5
  63. package/tests/client/switch.test.ripple +44 -44
  64. package/tests/client/try.test.ripple +5 -5
  65. package/tests/client/url/url.derived.test.ripple +6 -6
  66. package/tests/client/url-search-params/url-search-params.derived.test.ripple +8 -8
  67. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +10 -10
  68. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +10 -10
  69. package/tests/client/url-search-params/url-search-params.retrieval.test.ripple +18 -18
  70. package/tests/client/url-search-params/url-search-params.serialization.test.ripple +2 -2
  71. package/tests/hydration/compiled/client/events.js +25 -25
  72. package/tests/hydration/compiled/client/for.js +70 -66
  73. package/tests/hydration/compiled/client/head.js +25 -25
  74. package/tests/hydration/compiled/client/hmr.js +2 -2
  75. package/tests/hydration/compiled/client/html.js +3 -3
  76. package/tests/hydration/compiled/client/if-children.js +24 -24
  77. package/tests/hydration/compiled/client/if.js +18 -18
  78. package/tests/hydration/compiled/client/mixed-control-flow.js +9 -9
  79. package/tests/hydration/compiled/client/portal.js +3 -3
  80. package/tests/hydration/compiled/client/reactivity.js +16 -16
  81. package/tests/hydration/compiled/client/return.js +40 -40
  82. package/tests/hydration/compiled/client/switch.js +12 -12
  83. package/tests/hydration/compiled/server/events.js +19 -19
  84. package/tests/hydration/compiled/server/for.js +41 -41
  85. package/tests/hydration/compiled/server/head.js +26 -26
  86. package/tests/hydration/compiled/server/hmr.js +2 -2
  87. package/tests/hydration/compiled/server/html.js +2 -2
  88. package/tests/hydration/compiled/server/if-children.js +16 -16
  89. package/tests/hydration/compiled/server/if.js +11 -11
  90. package/tests/hydration/compiled/server/mixed-control-flow.js +6 -6
  91. package/tests/hydration/compiled/server/portal.js +2 -2
  92. package/tests/hydration/compiled/server/reactivity.js +16 -16
  93. package/tests/hydration/compiled/server/return.js +25 -25
  94. package/tests/hydration/compiled/server/switch.js +8 -8
  95. package/tests/hydration/components/events.ripple +25 -25
  96. package/tests/hydration/components/for.ripple +66 -66
  97. package/tests/hydration/components/head.ripple +16 -16
  98. package/tests/hydration/components/hmr.ripple +2 -2
  99. package/tests/hydration/components/html.ripple +3 -3
  100. package/tests/hydration/components/if-children.ripple +24 -24
  101. package/tests/hydration/components/if.ripple +18 -18
  102. package/tests/hydration/components/mixed-control-flow.ripple +9 -9
  103. package/tests/hydration/components/portal.ripple +3 -3
  104. package/tests/hydration/components/reactivity.ripple +16 -16
  105. package/tests/hydration/components/return.ripple +40 -40
  106. package/tests/hydration/components/switch.ripple +20 -20
  107. package/tests/server/await.test.ripple +3 -3
  108. package/tests/server/basic.attributes.test.ripple +34 -34
  109. package/tests/server/basic.components.test.ripple +10 -10
  110. package/tests/server/basic.test.ripple +38 -40
  111. package/tests/server/compiler.test.ripple +22 -0
  112. package/tests/server/composite.props.test.ripple +12 -14
  113. package/tests/server/dynamic-elements.test.ripple +15 -15
  114. package/tests/server/head.test.ripple +11 -11
  115. package/tests/server/lazy-destructuring.test.ripple +92 -13
  116. package/tsconfig.typecheck.json +4 -0
  117. package/types/index.d.ts +0 -19
  118. package/tests/client/__snapshots__/tracked-expression.test.ripple.snap +0 -34
  119. package/tests/client/tracked-expression.test.ripple +0 -26
@@ -16,8 +16,8 @@ describe('compiler > assignments', () => {
16
16
 
17
17
  logs.push(items[0]);
18
18
  logs.push(items[i]);
19
- logs.push(@tracked_items[0]);
20
- logs.push(@tracked_items[i]);
19
+ logs.push(tracked_items.value[0]);
20
+ logs.push(tracked_items.value[i]);
21
21
  logs.push(items2[0]);
22
22
  logs.push(items2[i]);
23
23
  logs.push(items3[0]);
@@ -25,8 +25,8 @@ describe('compiler > assignments', () => {
25
25
 
26
26
  items[0] = 123;
27
27
  items[i] = 123;
28
- @tracked_items[0] = 123;
29
- @tracked_items[i] = 123;
28
+ tracked_items.value[0] = 123;
29
+ tracked_items.value[i] = 123;
30
30
  items2[0] = 123;
31
31
  items2[i] = 123;
32
32
  items3[0] = 123;
@@ -34,8 +34,8 @@ describe('compiler > assignments', () => {
34
34
 
35
35
  logs.push(items[0]);
36
36
  logs.push(items[i]);
37
- logs.push(@tracked_items[0]);
38
- logs.push(@tracked_items[i]);
37
+ logs.push(tracked_items.value[0]);
38
+ logs.push(tracked_items.value[i]);
39
39
  logs.push(items2[0]);
40
40
  logs.push(items2[i]);
41
41
  logs.push(items3[0]);
@@ -43,8 +43,8 @@ describe('compiler > assignments', () => {
43
43
 
44
44
  items[0]++;
45
45
  items[i]++;
46
- @tracked_items[0]++;
47
- @tracked_items[i]++;
46
+ tracked_items.value[0]++;
47
+ tracked_items.value[i]++;
48
48
  items2[0]++;
49
49
  items2[i]++;
50
50
  items3[0]++;
@@ -52,8 +52,8 @@ describe('compiler > assignments', () => {
52
52
 
53
53
  logs.push(items[0]);
54
54
  logs.push(items[i]);
55
- logs.push(@tracked_items[0]);
56
- logs.push(@tracked_items[i]);
55
+ logs.push(tracked_items.value[0]);
56
+ logs.push(tracked_items.value[i]);
57
57
  logs.push(items2[0]);
58
58
  logs.push(items2[i]);
59
59
  logs.push(items3[0]);
@@ -61,8 +61,8 @@ describe('compiler > assignments', () => {
61
61
 
62
62
  logs.push(--items[0]);
63
63
  logs.push(--items[i]);
64
- logs.push(--@tracked_items[0]);
65
- logs.push(--@tracked_items[i]);
64
+ logs.push(--tracked_items.value[0]);
65
+ logs.push(--tracked_items.value[i]);
66
66
  logs.push(--items2[0]);
67
67
  logs.push(--items2[i]);
68
68
  logs.push(--items3[0]);
@@ -110,10 +110,10 @@ describe('compiler > assignments', () => {
110
110
  it('compiles tracked values in effect with assignment expression', () => {
111
111
  const source = `import { track, effect } from 'ripple';
112
112
  component App() {
113
- let count = track(0);
113
+ let &[count] = track(0);
114
114
 
115
115
  effect(() => {
116
- state.count = @count;
116
+ state.count = count;
117
117
  });
118
118
  }`;
119
119
  const result = compile(source, 'test.ripple');
@@ -124,14 +124,14 @@ component App() {
124
124
  it('compiles tracked values in effect with update expressions', () => {
125
125
  const source = `import { track, effect, untrack } from 'ripple';
126
126
  component App() {
127
- let count = track(5);
127
+ let &[count] = track(5);
128
128
 
129
129
  effect(() => {
130
130
  untrack(() => {
131
- state.preIncrement = ++@count;
132
- state.postIncrement = @count++;
133
- state.preDecrement = --@count;
134
- state.postDecrement = @count--;
131
+ state.preIncrement = ++count;
132
+ state.postIncrement = count++;
133
+ state.preDecrement = --count;
134
+ state.postDecrement = count--;
135
135
  });
136
136
  });
137
137
  }`;
@@ -324,35 +324,23 @@ describe('compiler > basics', () => {
324
324
  const source = `
325
325
  import { RippleArray, RippleMap, RippleObject, RippleSet, createRefKey, effect, track, untrack } from 'ripple';
326
326
  component App() {
327
- let value = track('test');
328
- function inputRef(node) {}
329
-
330
- const props = {
331
- id: 'example',
332
- @value,
333
- [createRefKey()]: inputRef,
334
- };
327
+ let value = track('test');
328
+ function inputRef(node) {}
329
+
330
+ const props = {
331
+ id: 'example',
332
+ value: value.value,
333
+ [createRefKey()]: inputRef,
334
+ };
335
335
  }
336
336
  `;
337
337
 
338
338
  const result = compile_to_volar_mappings(source, 'test.ripple').code;
339
339
 
340
- expect(result).toMatch(/value:\s*value\??\.\['#v'\]/);
340
+ expect(result).toMatch(/value:\s*value\.value/);
341
341
  });
342
342
 
343
343
  it('keeps lazy destructuring as plain destructuring in to_ts output', () => {
344
- const track_split_source = `
345
- import { trackSplit } from 'ripple';
346
- component App() {
347
- const source = { a: 1, b: 2, c: 3 };
348
- let &[a, b, rest] = trackSplit(source, ['a', 'b']);
349
- const sum = @a + @b + @rest.c;
350
- }
351
- `;
352
- const track_split_result = compile_to_volar_mappings(track_split_source, 'test.ripple').code;
353
- expect(track_split_result).toContain('let [a, b, rest] = trackSplit(source, [\'a\', \'b\']);');
354
- expect(track_split_result).not.toContain('let lazy = trackSplit');
355
-
356
344
  const track_source = `
357
345
  import { track } from 'ripple';
358
346
  component App() {
@@ -369,6 +357,37 @@ component App() {
369
357
  expect(track_result).not.toContain('lazy0');
370
358
  });
371
359
 
360
+ it('uses tracked fast path for nested lazy params typed as Tracked', () => {
361
+ const source = `
362
+ import type { Tracked } from 'ripple';
363
+ function use_nested({ value: &[count, tracked] }: { value: Tracked<number> }) {
364
+ count++;
365
+ return tracked;
366
+ }
367
+ `;
368
+ const { js } = compile(source, 'tracked-nested-lazy.ripple', { mode: 'client' });
369
+
370
+ // Nested lazy array should still use tracked tuple fast path from outer annotation.
371
+ expect(js.code).toContain('_$_.update(');
372
+ expect(js.code).not.toContain('[0]');
373
+ expect(js.code).not.toContain('[1]');
374
+ });
375
+
376
+ it('uses tracked fast path for nested lazy params at tuple rest positions', () => {
377
+ const source = `
378
+ import type { Tracked } from 'ripple';
379
+ function use_tuple_rest({ value: [head, &[count, tracked]] }: { value: [number, ...Tracked<number>[]] }) {
380
+ count++;
381
+ return tracked;
382
+ }
383
+ `;
384
+ const { js } = compile(source, 'tracked-nested-lazy-tuple-rest.ripple', { mode: 'client' });
385
+
386
+ // Tuple rest element access should resolve to Tracked<number>, not Tracked<number>[].
387
+ expect(js.code).toContain('_$_.update(');
388
+ expect(js.code).not.toContain('[1]');
389
+ });
390
+
372
391
  it('preserves generic type args in interface extends for Volar mappings', () => {
373
392
  const source = `
374
393
  interface PolymorphicProps<T extends keyof HTMLElementTagNameMap> {
@@ -393,13 +412,13 @@ export component App(props: Props) {
393
412
  const source = `
394
413
  import { track } from 'ripple';
395
414
  export component App() {
396
- let level = track(1);
415
+ let &[level] = track(1);
397
416
 
398
417
  <button
399
418
  onClick={() => {
400
- if (@level === 1) @level = 2;
401
- else if (@level === 2) @level = 3;
402
- else @level = 1;
419
+ if (level === 1) level = 2;
420
+ else if (level === 2) level = 3;
421
+ else level = 1;
403
422
  }}
404
423
  >
405
424
  {'Toggle'}
@@ -496,10 +515,10 @@ export component App() {
496
515
  import { track, effect, untrack } from 'ripple';
497
516
 
498
517
  component App() {
499
- let count = track(0);
518
+ let &[count] = track(0);
500
519
 
501
520
  effect(() => {
502
- const snapshot = untrack(() => @count);
521
+ const snapshot = untrack(() => count);
503
522
  console.log(snapshot);
504
523
  });
505
524
  }
@@ -70,8 +70,8 @@ import { track } from 'ripple';
70
70
  const code = `
71
71
  import { track } from 'ripple';
72
72
  export default component App() {
73
- let count = track(0);
74
- console.log(@count);
73
+ let &[count] = track(0);
74
+ console.log(count);
75
75
  }
76
76
  `;
77
77
  expect(() => compile(code, 'test.ripple')).not.toThrow();
@@ -18,7 +18,7 @@ describe('composite > dynamic components', () => {
18
18
  expect(container.textContent).toBe('Basic Component');
19
19
  });
20
20
 
21
- it('supports rendering composite components using <object.@component> syntax', () => {
21
+ it('supports rendering composite components from object properties', () => {
22
22
  component App() {
23
23
  component basic() {
24
24
  <div>{'Basic Component'}</div>
@@ -30,7 +30,8 @@ describe('composite > dynamic components', () => {
30
30
  tracked_basic,
31
31
  };
32
32
 
33
- <obj.@tracked_basic />
33
+ const comp = obj.tracked_basic;
34
+ <@comp />
34
35
  }
35
36
 
36
37
  render(App);
@@ -39,7 +40,7 @@ describe('composite > dynamic components', () => {
39
40
  expect(container.textContent).toBe('Basic Component');
40
41
  });
41
42
 
42
- it('supports rendering composite components using <@object.@component> syntax', () => {
43
+ it('supports rendering composite components from tracked object properties', () => {
43
44
  component App() {
44
45
  component basic() {
45
46
  <div>{'Basic Component'}</div>
@@ -51,9 +52,10 @@ describe('composite > dynamic components', () => {
51
52
  tracked_basic,
52
53
  };
53
54
 
54
- const ripple_object = track(obj);
55
+ let &[inner] = track(obj);
55
56
 
56
- <@ripple_object.@tracked_basic />
57
+ const comp = inner.tracked_basic;
58
+ <@comp />
57
59
  }
58
60
 
59
61
  render(App);
@@ -72,15 +74,13 @@ describe('composite > dynamic components', () => {
72
74
  }
73
75
 
74
76
  component App() {
75
- let thing = track(() => Child1);
77
+ let &[thing] = track(() => Child1);
76
78
 
77
79
  <div id="container">
78
80
  <@thing />
79
81
  </div>
80
82
 
81
- <button onClick={() => (@thing = @thing === Child1 ? Child2 : Child1)}>
82
- {'Change Child'}
83
- </button>
83
+ <button onClick={() => (thing = thing === Child1 ? Child2 : Child1)}>{'Change Child'}</button>
84
84
  }
85
85
 
86
86
  render(App);
@@ -1,5 +1,5 @@
1
1
  import type { Tracked, Props } from 'ripple';
2
- import { effect, flushSync, track, trackSplit } from 'ripple';
2
+ import { effect, flushSync, track } from 'ripple';
3
3
 
4
4
  describe('composite > props', () => {
5
5
  it('correctly handles default prop values', () => {
@@ -8,10 +8,10 @@ describe('composite > props', () => {
8
8
  }
9
9
 
10
10
  component App() {
11
- let foo = track(123);
11
+ let &[foo] = track(123);
12
12
 
13
13
  <Child />
14
- <Child {@foo} />
14
+ <Child {foo} />
15
15
  }
16
16
 
17
17
  render(App);
@@ -40,7 +40,7 @@ describe('composite > props', () => {
40
40
 
41
41
  it('correctly handles no props', () => {
42
42
  component Child(props: { foo?: Tracked<number> }) {
43
- <div>{props.@foo}</div>
43
+ <div>{props.foo?.value}</div>
44
44
  }
45
45
 
46
46
  component App() {
@@ -62,10 +62,10 @@ describe('composite > props', () => {
62
62
  }
63
63
 
64
64
  component App() {
65
- let foo = track(123);
65
+ let &[foo] = track(123);
66
66
 
67
67
  <Child />
68
- <Child {@foo} />
68
+ <Child {foo} />
69
69
  }
70
70
 
71
71
  render(App);
@@ -79,10 +79,10 @@ describe('composite > props', () => {
79
79
 
80
80
  component Counter({ count }: { count: Tracked<number> }) {
81
81
  effect(() => {
82
- logs.push(@count);
82
+ logs.push(count.value);
83
83
  });
84
84
 
85
- <button onClick={() => (@count = @count + 1)}>{'+'}</button>
85
+ <button onClick={() => (count.value = count.value + 1)}>{'+'}</button>
86
86
  }
87
87
 
88
88
  component App() {
@@ -106,9 +106,8 @@ describe('composite > props', () => {
106
106
  });
107
107
 
108
108
  it('correctly retains prop accessors and reactivity when using rest props', () => {
109
- component Button(props: Props) {
110
- const [children, rest] = trackSplit(props, ['children']);
111
- <button {...@rest}>
109
+ component Button(&{ children, ...rest }: Props) {
110
+ <button {...rest}>
112
111
  <@children />
113
112
  </button>
114
113
  <style>
@@ -121,11 +120,10 @@ describe('composite > props', () => {
121
120
  </style>
122
121
  }
123
122
 
124
- component Toggle(props: { pressed: Tracked<boolean> }) {
125
- const [pressed, rest] = trackSplit(props, ['pressed']);
126
- const onClick = () => (@pressed = !@pressed);
127
- <Button {...@rest} class={@pressed ? 'on' : 'off'} {onClick}>{'button 1'}</Button>
128
- <Button class={@pressed ? 'on' : 'off'} {onClick}>{'button 2'}</Button>
123
+ component Toggle(&{ pressed, ...rest }: { pressed: Tracked<boolean> }) {
124
+ const onClick = () => (pressed.value = !pressed.value);
125
+ <Button {...rest} class={pressed.value ? 'on' : 'off'} {onClick}>{'button 1'}</Button>
126
+ <Button class={pressed.value ? 'on' : 'off'} {onClick}>{'button 2'}</Button>
129
127
  }
130
128
 
131
129
  component App() {
@@ -1,5 +1,5 @@
1
1
  import type { Props, Tracked } from 'ripple';
2
- import { RippleObject, effect, flushSync, track, trackSplit } from 'ripple';
2
+ import { RippleObject, effect, flushSync, track } from 'ripple';
3
3
 
4
4
  describe('composite > reactivity', () => {
5
5
  it('renders composite components with object state', () => {
@@ -7,10 +7,10 @@ describe('composite > reactivity', () => {
7
7
  <button
8
8
  class="count2"
9
9
  onClick={() => {
10
- obj.@count++;
10
+ obj.count.value++;
11
11
  }}
12
12
  >
13
- {obj.@count}
13
+ {obj.count.value}
14
14
  </button>
15
15
  }
16
16
 
@@ -20,7 +20,7 @@ describe('composite > reactivity', () => {
20
20
  count: track(0),
21
21
  };
22
22
 
23
- <span class="count">{obj.@count}</span>
23
+ <span class="count">{obj.count.value}</span>
24
24
  <Button {obj} />
25
25
  </div>
26
26
  }
@@ -41,15 +41,15 @@ describe('composite > reactivity', () => {
41
41
  <button
42
42
  class="count2"
43
43
  onClick={() => {
44
- obj.@count++;
44
+ obj.count.value++;
45
45
  }}
46
46
  >
47
- {obj.@count}
47
+ {obj.count.value}
48
48
  </button>
49
49
  }
50
50
 
51
51
  component OtherComponent({ obj }: { obj: { count: Tracked<number> } }) {
52
- <div class="count3">{obj.@count}</div>
52
+ <div class="count3">{obj.count.value}</div>
53
53
  }
54
54
 
55
55
  component App() {
@@ -58,7 +58,7 @@ describe('composite > reactivity', () => {
58
58
  count: track(0),
59
59
  };
60
60
 
61
- <span class="count">{obj.@count}</span>
61
+ <span class="count">{obj.count.value}</span>
62
62
  <span>{' '}</span>
63
63
  if (obj) {
64
64
  <Button {obj} />
@@ -84,28 +84,28 @@ describe('composite > reactivity', () => {
84
84
 
85
85
  it('parents and children have isolated state', () => {
86
86
  component Button(props: { count: number }) {
87
- let count = track(() => props.count);
87
+ let &[count] = track(() => props.count);
88
88
  <button
89
89
  onClick={() => {
90
- @count++;
90
+ count++;
91
91
  }}
92
92
  >
93
- {'child: ' + @count}
93
+ {'child: ' + count}
94
94
  </button>
95
95
  }
96
96
 
97
97
  component App() {
98
98
  <div>
99
- let count = track(0);
99
+ let &[count] = track(0);
100
100
 
101
101
  <button
102
102
  onClick={() => {
103
- @count++;
103
+ count++;
104
104
  }}
105
105
  >
106
- {'parent: ' + @count}
106
+ {'parent: ' + count}
107
107
  </button>
108
- <Button {@count} />
108
+ <Button {count} />
109
109
  </div>
110
110
  }
111
111
 
@@ -131,28 +131,28 @@ describe('composite > reactivity', () => {
131
131
 
132
132
  it('parents and children have isolated connected state (destructured props)', () => {
133
133
  component Button(&{ count }: { count: number }) {
134
- let local_count = track(() => count);
134
+ let &[local_count] = track(() => count);
135
135
  <button
136
136
  onClick={() => {
137
- @local_count++;
137
+ local_count++;
138
138
  }}
139
139
  >
140
- {'child: ' + @local_count}
140
+ {'child: ' + local_count}
141
141
  </button>
142
142
  }
143
143
 
144
144
  component App() {
145
145
  <div>
146
- let count = track(0);
146
+ let &[count] = track(0);
147
147
 
148
148
  <button
149
149
  onClick={() => {
150
- @count++;
150
+ count++;
151
151
  }}
152
152
  >
153
- {'parent: ' + @count}
153
+ {'parent: ' + count}
154
154
  </button>
155
- <Button {@count} />
155
+ <Button {count} />
156
156
  </div>
157
157
  }
158
158
 
@@ -180,25 +180,25 @@ describe('composite > reactivity', () => {
180
180
  let logs: string[] = [];
181
181
 
182
182
  component App() {
183
- const a = track(1);
184
- const b = track(2);
185
- const c = track(3);
183
+ let &[a] = track(1);
184
+ let &[b] = track(2);
185
+ let &[c] = track(3);
186
186
 
187
- const obj = track(
187
+ let &[obj] = track(
188
188
  () => ({
189
- @a,
190
- @b,
191
- @c,
189
+ a,
190
+ b,
191
+ c,
192
192
  }),
193
193
  );
194
194
 
195
- <Child {...@obj} />
195
+ <Child {...obj} />
196
196
 
197
197
  <button
198
198
  onClick={() => {
199
- @a++;
200
- @b++;
201
- @c++;
199
+ a++;
200
+ b++;
201
+ c++;
202
202
  }}
203
203
  >
204
204
  {'Increment all'}
@@ -227,51 +227,47 @@ describe('composite > reactivity', () => {
227
227
  expect(logs).toEqual(['Child effect: 1, 2, 3', 'Child effect: 2, 3, 4']);
228
228
  });
229
229
 
230
- it(
231
- 'keeps reactivity for spread props via intermediate components and usage of trackSplit()',
232
- () => {
233
- component App() {
234
- const count = track(0);
235
- <CounterWrapper {@count} up={() => @count++} down={() => @count--} />
236
- }
230
+ it('keeps reactivity for spread props via intermediate components and lazy destructuring', () => {
231
+ component App() {
232
+ let &[count] = track(0);
233
+ <CounterWrapper {count} up={() => count++} down={() => count--} />
234
+ }
237
235
 
238
- component CounterWrapper(props: Props) {
239
- <div>
240
- <Counter {...props} />
241
- </div>
242
- }
236
+ component CounterWrapper(props: Props) {
237
+ <div>
238
+ <Counter {...props} />
239
+ </div>
240
+ }
243
241
 
244
- component Counter(props: Props) {
245
- const [count, up, down, rest] = trackSplit(props, ['count', 'up', 'down']);
246
- <button onClick={() => @up()}>{'UP'}</button>
247
- <button onClick={() => @down()}>{'DOWN'}</button>
248
- <span {...@rest}>{`Counter: ${@count}`}</span>
249
- }
242
+ component Counter(&{ count, up, down, ...rest }: Props) {
243
+ <button onClick={() => up()}>{'UP'}</button>
244
+ <button onClick={() => down()}>{'DOWN'}</button>
245
+ <span {...rest}>{`Counter: ${count}`}</span>
246
+ }
250
247
 
251
- render(App);
248
+ render(App);
252
249
 
253
- const buttonIncrement = container.querySelectorAll('button')[0];
254
- const buttonDecrement = container.querySelectorAll('button')[1];
255
- const span = container.querySelector('span');
250
+ const buttonIncrement = container.querySelectorAll('button')[0];
251
+ const buttonDecrement = container.querySelectorAll('button')[1];
252
+ const span = container.querySelector('span');
256
253
 
257
- expect(span.textContent).toBe('Counter: 0');
254
+ expect(span.textContent).toBe('Counter: 0');
258
255
 
259
- buttonIncrement.click();
260
- flushSync();
256
+ buttonIncrement.click();
257
+ flushSync();
261
258
 
262
- expect(span.textContent).toBe('Counter: 1');
259
+ expect(span.textContent).toBe('Counter: 1');
263
260
 
264
- buttonDecrement.click();
265
- flushSync();
261
+ buttonDecrement.click();
262
+ flushSync();
266
263
 
267
- expect(span.textContent).toBe('Counter: 0');
268
- },
269
- );
264
+ expect(span.textContent).toBe('Counter: 0');
265
+ });
270
266
 
271
267
  it('keeps reactivity on elements for element spreads and adds / removes dynamic props', () => {
272
268
  component App() {
273
- const count = track(0);
274
- <CounterWrapper {@count} up={() => @count++} />
269
+ let &[count] = track(0);
270
+ <CounterWrapper {count} up={() => count++} />
275
271
  }
276
272
 
277
273
  component CounterWrapper(props: { count: number; up: () => void }) {
@@ -295,16 +291,19 @@ describe('composite > reactivity', () => {
295
291
  </div>
296
292
  }
297
293
 
298
- component Counter(props: {
294
+ component Counter(&{
295
+ count,
296
+ up,
297
+ ...rest
298
+ }: {
299
299
  count: number;
300
300
  up: () => void;
301
301
  double: Tracked<number>;
302
302
  another?: number;
303
303
  extra: number;
304
304
  }) {
305
- const [count, up, rest] = trackSplit(props, ['count', 'up']);
306
- <div {...@rest}>{`Counter: ${@count} Double: ${props.@double}`}</div>
307
- <button onClick={() => @up()}>{'UP'}</button>
305
+ <div {...rest}>{`Counter: ${count} Double: ${rest.double.value}`}</div>
306
+ <button onClick={() => up()}>{'UP'}</button>
308
307
  }
309
308
 
310
309
  render(App);
@@ -7,10 +7,10 @@ describe('composite > render', () => {
7
7
  }
8
8
 
9
9
  component App() {
10
- let count = track(0);
10
+ let &[count] = track(0);
11
11
 
12
- <button onClick={() => @count++}>{'Increment'}</button>
13
- <Button {@count} />
12
+ <button onClick={() => count++}>{'Increment'}</button>
13
+ <Button {count} />
14
14
  }
15
15
 
16
16
  render(App);
@@ -7,11 +7,11 @@ describe('computed tracked properties', () => {
7
7
  [0]: track(0),
8
8
  };
9
9
 
10
- <div>{obj.@[0]}</div>
10
+ <div>{obj[0].value}</div>
11
11
 
12
12
  <button
13
13
  onClick={() => {
14
- obj.@[0] += 1;
14
+ obj[0].value += 1;
15
15
  }}
16
16
  >
17
17
  {'Increment'}
@@ -34,11 +34,11 @@ describe('computed tracked properties', () => {
34
34
  [0]: track(0),
35
35
  };
36
36
 
37
- <div>{obj.@[0]}</div>
37
+ <div>{obj[0].value}</div>
38
38
 
39
39
  <button
40
40
  onClick={() => {
41
- obj.@[0]++;
41
+ obj[0].value++;
42
42
  }}
43
43
  >
44
44
  {'Increment'}