ripple 0.2.151 → 0.2.153

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 (67) hide show
  1. package/README.md +3 -3
  2. package/package.json +5 -5
  3. package/src/compiler/phases/1-parse/index.js +1 -1
  4. package/src/compiler/phases/3-transform/client/index.js +37 -16
  5. package/src/compiler/phases/3-transform/server/index.js +43 -25
  6. package/src/runtime/internal/client/events.js +5 -1
  7. package/src/runtime/internal/client/index.js +2 -1
  8. package/src/runtime/internal/client/render.js +18 -15
  9. package/src/runtime/internal/client/runtime.js +78 -10
  10. package/src/runtime/internal/server/index.js +51 -11
  11. package/src/server/index.js +1 -1
  12. package/tests/client/array/array.derived.test.ripple +61 -33
  13. package/tests/client/array/array.iteration.test.ripple +3 -1
  14. package/tests/client/array/array.mutations.test.ripple +19 -15
  15. package/tests/client/array/array.static.test.ripple +115 -104
  16. package/tests/client/array/array.to-methods.test.ripple +3 -3
  17. package/tests/client/basic/basic.attributes.test.ripple +110 -57
  18. package/tests/client/basic/basic.collections.test.ripple +41 -22
  19. package/tests/client/basic/basic.errors.test.ripple +12 -6
  20. package/tests/client/basic/basic.events.test.ripple +51 -33
  21. package/tests/client/basic/basic.reactivity.test.ripple +120 -56
  22. package/tests/client/basic/basic.rendering.test.ripple +49 -19
  23. package/tests/client/basic/basic.styling.test.ripple +2 -2
  24. package/tests/client/basic/basic.utilities.test.ripple +1 -1
  25. package/tests/client/boundaries.test.ripple +70 -58
  26. package/tests/client/compiler/compiler.assignments.test.ripple +32 -4
  27. package/tests/client/compiler/compiler.attributes.test.ripple +46 -46
  28. package/tests/client/compiler/compiler.basic.test.ripple +18 -15
  29. package/tests/client/compiler/compiler.tracked-access.test.ripple +53 -42
  30. package/tests/client/compiler/compiler.typescript.test.ripple +1 -2
  31. package/tests/client/composite/composite.dynamic-components.test.ripple +6 -6
  32. package/tests/client/composite/composite.generics.test.ripple +39 -36
  33. package/tests/client/composite/composite.props.test.ripple +4 -3
  34. package/tests/client/composite/composite.reactivity.test.ripple +112 -27
  35. package/tests/client/composite/composite.render.test.ripple +9 -8
  36. package/tests/client/computed-properties.test.ripple +24 -24
  37. package/tests/client/context.test.ripple +11 -9
  38. package/tests/client/date.test.ripple +3 -1
  39. package/tests/client/dynamic-elements.test.ripple +103 -78
  40. package/tests/client/for.test.ripple +27 -17
  41. package/tests/client/head.test.ripple +42 -6
  42. package/tests/client/html.test.ripple +42 -32
  43. package/tests/client/input-value.test.ripple +4 -4
  44. package/tests/client/map.test.ripple +140 -141
  45. package/tests/client/media-query.test.ripple +31 -31
  46. package/tests/client/object.test.ripple +148 -112
  47. package/tests/client/portal.test.ripple +29 -15
  48. package/tests/client/ref.test.ripple +9 -3
  49. package/tests/client/set.test.ripple +111 -111
  50. package/tests/client/tracked-expression.test.ripple +16 -17
  51. package/tests/client/url/url.derived.test.ripple +19 -9
  52. package/tests/client/url/url.parsing.test.ripple +24 -8
  53. package/tests/client/url/url.partial-removal.test.ripple +12 -4
  54. package/tests/client/url/url.reactivity.test.ripple +63 -25
  55. package/tests/client/url/url.serialization.test.ripple +18 -6
  56. package/tests/client/url-search-params/url-search-params.derived.test.ripple +10 -6
  57. package/tests/client/url-search-params/url-search-params.iteration.test.ripple +3 -1
  58. package/tests/client/url-search-params/url-search-params.mutation.test.ripple +26 -14
  59. package/tests/client/url-search-params/url-search-params.tracked-url.test.ripple +3 -1
  60. package/tests/server/await.test.ripple +23 -22
  61. package/tests/server/basic.test.ripple +1 -1
  62. package/tests/server/compiler.test.ripple +3 -7
  63. package/tests/server/composite.test.ripple +38 -36
  64. package/tests/server/for.test.ripple +9 -5
  65. package/tests/server/if.test.ripple +1 -1
  66. package/tests/server/streaming-ssr.test.ripple +67 -0
  67. package/types/server.d.ts +5 -4
@@ -1,21 +1,26 @@
1
- import { track, flushSync, effect } from 'ripple';
1
+ import { track, trackSplit, flushSync, effect } from 'ripple';
2
2
 
3
3
  describe('composite > reactivity', () => {
4
4
  it('renders composite components with object state', () => {
5
5
  component Button({ obj }: { obj: { count: Tracked<number> } }) {
6
- <button class='count2' onClick={() => {
7
- obj.@count++;
8
- }}>{obj.@count}</button>
6
+ <button
7
+ class="count2"
8
+ onClick={() => {
9
+ obj.@count++;
10
+ }}
11
+ >
12
+ {obj.@count}
13
+ </button>
9
14
  }
10
15
 
11
16
  component App() {
12
17
  <div>
13
18
  let obj = {
14
- count: track(0)
19
+ count: track(0),
15
20
  };
16
21
 
17
- <span class='count'>{obj.@count}</span>
18
- <Button obj={obj} />
22
+ <span class="count">{obj.@count}</span>
23
+ <Button {obj} />
19
24
  </div>
20
25
  }
21
26
 
@@ -32,29 +37,34 @@ describe('composite > reactivity', () => {
32
37
 
33
38
  it('renders composite components with object state wrapped in an if statement', () => {
34
39
  component Button({ obj }: { obj: { count: Tracked<number> } }) {
35
- <button class='count2' onClick={() => {
36
- obj.@count++;
37
- }}>{obj.@count}</button>
40
+ <button
41
+ class="count2"
42
+ onClick={() => {
43
+ obj.@count++;
44
+ }}
45
+ >
46
+ {obj.@count}
47
+ </button>
38
48
  }
39
49
 
40
50
  component OtherComponent({ obj }: { obj: { count: Tracked<number> } }) {
41
- <div class='count3'>{obj.@count}</div>
51
+ <div class="count3">{obj.@count}</div>
42
52
  }
43
53
 
44
54
  component App() {
45
55
  <div>
46
56
  let obj = {
47
- count: track(0)
57
+ count: track(0),
48
58
  };
49
59
 
50
- <span class='count'>{obj.@count}</span>
60
+ <span class="count">{obj.@count}</span>
51
61
  <span>{' '}</span>
52
62
  if (obj) {
53
- <Button obj={obj} />
63
+ <Button {obj} />
54
64
  }
55
65
 
56
66
  if (obj) {
57
- <OtherComponent obj={obj} />
67
+ <OtherComponent {obj} />
58
68
  }
59
69
  </div>
60
70
  }
@@ -74,14 +84,26 @@ describe('composite > reactivity', () => {
74
84
  it('parents and children have isolated state', () => {
75
85
  component Button(props: { count: number }) {
76
86
  let count = track(() => props.count);
77
- <button onClick={() => { @count++; } }>{"child: " + @count}</button>
87
+ <button
88
+ onClick={() => {
89
+ @count++;
90
+ }}
91
+ >
92
+ {'child: ' + @count}
93
+ </button>
78
94
  }
79
95
 
80
96
  component App() {
81
97
  <div>
82
98
  let count = track(0);
83
99
 
84
- <button onClick={() => { @count++; } }>{"parent: " + @count}</button>
100
+ <button
101
+ onClick={() => {
102
+ @count++;
103
+ }}
104
+ >
105
+ {'parent: ' + @count}
106
+ </button>
85
107
  <Button {@count} />
86
108
  </div>
87
109
  }
@@ -107,16 +129,28 @@ describe('composite > reactivity', () => {
107
129
  });
108
130
 
109
131
  it('parents and children have isolated connected state (destructured props)', () => {
110
- component Button({count}: { count: number }) {
132
+ component Button({ count }: { count: number }) {
111
133
  let local_count = track(() => count);
112
- <button onClick={() => { @local_count++; } }>{"child: " + @local_count}</button>
134
+ <button
135
+ onClick={() => {
136
+ @local_count++;
137
+ }}
138
+ >
139
+ {'child: ' + @local_count}
140
+ </button>
113
141
  }
114
142
 
115
143
  component App() {
116
144
  <div>
117
145
  let count = track(0);
118
146
 
119
- <button onClick={() => { @count++; } }>{"parent: " + @count}</button>
147
+ <button
148
+ onClick={() => {
149
+ @count++;
150
+ }}
151
+ >
152
+ {'parent: ' + @count}
153
+ </button>
120
154
  <Button {@count} />
121
155
  </div>
122
156
  }
@@ -149,15 +183,25 @@ describe('composite > reactivity', () => {
149
183
  const b = track(2);
150
184
  const c = track(3);
151
185
 
152
- const obj = track(() => ({
153
- @a,
154
- @b,
155
- @c,
156
- }));
186
+ const obj = track(
187
+ () => ({
188
+ @a,
189
+ @b,
190
+ @c,
191
+ }),
192
+ );
157
193
 
158
194
  <Child {...@obj} />
159
195
 
160
- <button onClick={() => { @a++; @b++; @c++; }}>{"Increment all"}</button>
196
+ <button
197
+ onClick={() => {
198
+ @a++;
199
+ @b++;
200
+ @c++;
201
+ }}
202
+ >
203
+ {'Increment all'}
204
+ </button>
161
205
  }
162
206
 
163
207
  component Child({ a, b, c }: { a: number; b: number; c: number }) {
@@ -165,7 +209,7 @@ describe('composite > reactivity', () => {
165
209
  logs.push(`Child effect: ${a}, ${b}, ${c}`);
166
210
  });
167
211
 
168
- <div>{a + ' ' + b + ' ' + c}</div>
212
+ <div>{a + ' ' + b + ' ' + c}</div>
169
213
  }
170
214
 
171
215
  render(App);
@@ -181,4 +225,45 @@ describe('composite > reactivity', () => {
181
225
  expect(container.querySelector('div').textContent).toBe('2 3 4');
182
226
  expect(logs).toEqual(['Child effect: 1, 2, 3', 'Child effect: 2, 3, 4']);
183
227
  });
228
+
229
+ it(
230
+ 'keeps reactivity for spread props via intermediate components and usage of trackSplit()',
231
+ () => {
232
+ component App() {
233
+ const count = track(0);
234
+ <CounterWrapper {@count} up={() => @count++} down={() => @count--} />
235
+ }
236
+
237
+ component CounterWrapper(props) {
238
+ <div>
239
+ <Counter {...props} />
240
+ </div>
241
+ }
242
+
243
+ component Counter(props) {
244
+ const [count, up, down, rest] = trackSplit(props, ['count', 'up', 'down']);
245
+ <button onClick={() => @up()}>{'UP'}</button>
246
+ <button onClick={() => @down()}>{'DOWN'}</button>
247
+ <span {...@rest}>{`Counter: ${@count}`}</span>
248
+ }
249
+
250
+ render(App);
251
+
252
+ const buttonIncrement = container.querySelectorAll('button')[0];
253
+ const buttonDecrement = container.querySelectorAll('button')[1];
254
+ const span = container.querySelector('span');
255
+
256
+ expect(span.textContent).toBe('Counter: 0');
257
+
258
+ buttonIncrement.click();
259
+ flushSync();
260
+
261
+ expect(span.textContent).toBe('Counter: 1');
262
+
263
+ buttonDecrement.click();
264
+ flushSync();
265
+
266
+ expect(span.textContent).toBe('Counter: 0');
267
+ },
268
+ );
184
269
  });
@@ -40,11 +40,11 @@ describe('composite > render', () => {
40
40
  component App() {
41
41
  <Button>
42
42
  component A() {
43
- <div>{"I am A"}</div>
43
+ <div>{'I am A'}</div>
44
44
  }
45
- <div>{"other text"}</div>
45
+ <div>{'other text'}</div>
46
46
  component B() {
47
- <div>{"I am B"}</div>
47
+ <div>{'I am B'}</div>
48
48
  }
49
49
  </Button>
50
50
  }
@@ -58,13 +58,14 @@ describe('composite > render', () => {
58
58
  component App() {
59
59
  let name = 'Click Me';
60
60
 
61
- <Child
62
- class="my-button"
63
- >{name}</Child>;
61
+ <Child class="my-button">{name}</Child>
62
+
64
63
  }
65
64
 
66
- component Child({children, ...rest}: {children: string; class: string}) {
67
- <button {...rest}><children /></button>
65
+ component Child({ children, ...rest }: { children: string; class: string }) {
66
+ <button {...rest}>
67
+ <children />
68
+ </button>
68
69
  }
69
70
 
70
71
  render(App);
@@ -1,45 +1,45 @@
1
1
  import { flushSync, track } from 'ripple';
2
2
 
3
3
  describe('computed tracked properties', () => {
4
- it('should update a property using assignment', () => {
5
- component App() {
6
- let obj = {
7
- [0]: track(0),
8
- };
4
+ it('should update a property using assignment', () => {
5
+ component App() {
6
+ let obj = {
7
+ [0]: track(0),
8
+ };
9
9
 
10
10
  <div>{obj.@[0]}</div>
11
11
 
12
12
  <button onClick={() => { obj.@[0] += 1 }}>{"Increment"}</button>
13
13
  }
14
14
 
15
- render(App);
16
- expect(container).toMatchSnapshot();
15
+ render(App);
16
+ expect(container).toMatchSnapshot();
17
17
 
18
- const button = container.querySelector('button');
19
- button.click();
20
- flushSync();
18
+ const button = container.querySelector('button');
19
+ button.click();
20
+ flushSync();
21
21
 
22
- expect(container).toMatchSnapshot();
23
- });
22
+ expect(container).toMatchSnapshot();
23
+ });
24
24
 
25
- it('should update a property using update expressions', () => {
26
- component App() {
27
- let obj = {
28
- [0]: track(0),
29
- };
25
+ it('should update a property using update expressions', () => {
26
+ component App() {
27
+ let obj = {
28
+ [0]: track(0),
29
+ };
30
30
 
31
31
  <div>{obj.@[0]}</div>
32
32
 
33
33
  <button onClick={() => { obj.@[0]++ }}>{"Increment"}</button>
34
34
  }
35
35
 
36
- render(App);
37
- expect(container).toMatchSnapshot();
36
+ render(App);
37
+ expect(container).toMatchSnapshot();
38
38
 
39
- const button = container.querySelector('button');
40
- button.click();
41
- flushSync();
39
+ const button = container.querySelector('button');
40
+ button.click();
41
+ flushSync();
42
42
 
43
- expect(container).toMatchSnapshot();
44
- });
43
+ expect(container).toMatchSnapshot();
44
+ });
45
45
  });
@@ -14,7 +14,7 @@ describe('context', () => {
14
14
  component TestContext() {
15
15
  const value = MyContext.get();
16
16
 
17
- MyContext.set("Hello from context!");
17
+ MyContext.set('Hello from context!');
18
18
 
19
19
  <Child />
20
20
  }
@@ -25,22 +25,24 @@ describe('context', () => {
25
25
  });
26
26
 
27
27
  it('handles context captured inside a computed tracked', () => {
28
-
29
- const MyContext = new Context<number | null>(null)
28
+ const MyContext = new Context<number | null>(null);
30
29
 
31
30
  const doubleContext = () => {
32
- const value = MyContext.get() as number
33
- return value * 2
34
- }
31
+ const value = MyContext.get() as number;
32
+ return value * 2;
33
+ };
35
34
 
36
35
  component App() {
37
- MyContext.set(4)
36
+ MyContext.set(4);
38
37
 
39
38
  <h3>{MyContext.get()}</h3>
40
39
 
41
- <h4>{'2x:'} {doubleContext()}</h4>
40
+ <h4>
41
+ {'2x:'}
42
+ {doubleContext()}
43
+ </h4>
42
44
 
43
- MyContext.set(8)
45
+ MyContext.set(8);
44
46
  }
45
47
 
46
48
  render(App);
@@ -250,7 +250,9 @@ describe('TrackedDate', () => {
250
250
  let day = track(() => date.getDate());
251
251
  let hours = track(() => date.getHours());
252
252
 
253
- <button onClick={() => date.setTime(new Date(2026, 5, 15, 14, 45, 30).getTime())}>{'Change All'}</button>
253
+ <button onClick={() => date.setTime(new Date(2026, 5, 15, 14, 45, 30).getTime())}>
254
+ {'Change All'}
255
+ </button>
254
256
  <div>
255
257
  {'Year: '}
256
258
  {@year}