ripple 0.3.67 → 0.3.69

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 (182) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +2 -2
  4. package/src/runtime/element.js +1 -1
  5. package/src/runtime/index-client.js +11 -11
  6. package/src/runtime/index-server.js +7 -4
  7. package/src/runtime/internal/client/bindings.js +1 -1
  8. package/src/runtime/internal/client/blocks.js +13 -4
  9. package/src/runtime/internal/client/component.js +55 -0
  10. package/src/runtime/internal/client/composite.js +4 -2
  11. package/src/runtime/internal/client/expression.js +65 -7
  12. package/src/runtime/internal/client/hmr.js +54 -43
  13. package/src/runtime/internal/client/index.js +5 -1
  14. package/src/runtime/internal/client/portal.js +70 -69
  15. package/src/runtime/internal/client/render.js +3 -0
  16. package/src/runtime/internal/server/index.js +92 -8
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
  18. package/tests/client/array/array.copy-within.test.tsrx +33 -31
  19. package/tests/client/array/array.derived.test.tsrx +186 -169
  20. package/tests/client/array/array.iteration.test.tsrx +40 -37
  21. package/tests/client/array/array.mutations.test.tsrx +113 -101
  22. package/tests/client/array/array.static.test.tsrx +119 -101
  23. package/tests/client/array/array.to-methods.test.tsrx +24 -21
  24. package/tests/client/async-suspend.test.tsrx +247 -246
  25. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
  26. package/tests/client/basic/basic.attributes.test.tsrx +428 -423
  27. package/tests/client/basic/basic.collections.test.tsrx +109 -102
  28. package/tests/client/basic/basic.components.test.tsrx +323 -205
  29. package/tests/client/basic/basic.errors.test.tsrx +91 -91
  30. package/tests/client/basic/basic.events.test.tsrx +114 -115
  31. package/tests/client/basic/basic.get-set.test.tsrx +97 -87
  32. package/tests/client/basic/basic.hmr.test.tsrx +19 -16
  33. package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
  34. package/tests/client/basic/basic.rendering.test.tsrx +272 -182
  35. package/tests/client/basic/basic.styling.test.tsrx +23 -22
  36. package/tests/client/basic/basic.utilities.test.tsrx +10 -8
  37. package/tests/client/boundaries.test.tsrx +26 -26
  38. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
  41. package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
  42. package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
  43. package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
  44. package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
  45. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  46. package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
  47. package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
  48. package/tests/client/composite/composite.generics.test.tsrx +168 -192
  49. package/tests/client/composite/composite.props.test.tsrx +97 -81
  50. package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
  51. package/tests/client/composite/composite.render.test.tsrx +122 -105
  52. package/tests/client/computed-properties.test.tsrx +28 -28
  53. package/tests/client/context.test.tsrx +21 -21
  54. package/tests/client/css/global-additional-cases.test.tsrx +58 -58
  55. package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
  56. package/tests/client/css/global-at-rules.test.tsrx +10 -10
  57. package/tests/client/css/global-basic.test.tsrx +14 -14
  58. package/tests/client/css/global-classes-ids.test.tsrx +14 -14
  59. package/tests/client/css/global-combinators.test.tsrx +10 -10
  60. package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
  61. package/tests/client/css/global-edge-cases.test.tsrx +18 -18
  62. package/tests/client/css/global-keyframes.test.tsrx +12 -12
  63. package/tests/client/css/global-nested.test.tsrx +10 -10
  64. package/tests/client/css/global-pseudo.test.tsrx +12 -12
  65. package/tests/client/css/global-scoping.test.tsrx +20 -20
  66. package/tests/client/css/style-identifier.test.tsrx +143 -291
  67. package/tests/client/date.test.tsrx +146 -133
  68. package/tests/client/dynamic-elements.test.tsrx +398 -365
  69. package/tests/client/events.test.tsrx +292 -290
  70. package/tests/client/for.test.tsrx +156 -153
  71. package/tests/client/head.test.tsrx +105 -96
  72. package/tests/client/html.test.tsrx +122 -26
  73. package/tests/client/input-value.test.tsrx +1361 -1314
  74. package/tests/client/lazy-array.test.tsrx +16 -13
  75. package/tests/client/lazy-destructuring.test.tsrx +257 -213
  76. package/tests/client/map.test.tsrx +65 -60
  77. package/tests/client/media-query.test.tsrx +22 -20
  78. package/tests/client/object.test.tsrx +87 -81
  79. package/tests/client/portal.test.tsrx +57 -51
  80. package/tests/client/ref.test.tsrx +233 -202
  81. package/tests/client/return.test.tsrx +71 -2560
  82. package/tests/client/set.test.tsrx +54 -45
  83. package/tests/client/svg.test.tsrx +216 -186
  84. package/tests/client/switch.test.tsrx +194 -193
  85. package/tests/client/track-async-hydration.test.tsrx +18 -14
  86. package/tests/client/tracked-index-access.test.tsrx +28 -18
  87. package/tests/client/try.test.tsrx +675 -548
  88. package/tests/client/tsx.test.tsrx +373 -311
  89. package/tests/client/typescript-generics.test.tsrx +145 -145
  90. package/tests/client/url/url.derived.test.tsrx +33 -28
  91. package/tests/client/url/url.parsing.test.tsrx +61 -51
  92. package/tests/client/url/url.partial-removal.test.tsrx +56 -48
  93. package/tests/client/url/url.reactivity.test.tsrx +142 -125
  94. package/tests/client/url/url.serialization.test.tsrx +13 -11
  95. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
  96. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
  97. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
  98. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
  99. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
  100. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
  101. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
  102. package/tests/hydration/basic.test.js +3 -3
  103. package/tests/hydration/compiled/client/basic.js +586 -651
  104. package/tests/hydration/compiled/client/composite.js +79 -104
  105. package/tests/hydration/compiled/client/events.js +140 -148
  106. package/tests/hydration/compiled/client/for.js +1005 -1018
  107. package/tests/hydration/compiled/client/head.js +124 -134
  108. package/tests/hydration/compiled/client/hmr.js +41 -48
  109. package/tests/hydration/compiled/client/html-in-template.js +38 -41
  110. package/tests/hydration/compiled/client/html.js +970 -1314
  111. package/tests/hydration/compiled/client/if-children.js +234 -249
  112. package/tests/hydration/compiled/client/if.js +182 -189
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
  115. package/tests/hydration/compiled/client/portal.js +65 -85
  116. package/tests/hydration/compiled/client/reactivity.js +84 -90
  117. package/tests/hydration/compiled/client/return.js +38 -1939
  118. package/tests/hydration/compiled/client/switch.js +218 -224
  119. package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
  120. package/tests/hydration/compiled/client/try.js +123 -132
  121. package/tests/hydration/compiled/server/basic.js +773 -831
  122. package/tests/hydration/compiled/server/composite.js +166 -191
  123. package/tests/hydration/compiled/server/events.js +170 -184
  124. package/tests/hydration/compiled/server/for.js +851 -909
  125. package/tests/hydration/compiled/server/head.js +206 -216
  126. package/tests/hydration/compiled/server/hmr.js +64 -72
  127. package/tests/hydration/compiled/server/html-in-template.js +42 -76
  128. package/tests/hydration/compiled/server/html.js +1362 -1667
  129. package/tests/hydration/compiled/server/if-children.js +419 -445
  130. package/tests/hydration/compiled/server/if.js +194 -208
  131. package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
  132. package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
  133. package/tests/hydration/compiled/server/portal.js +152 -160
  134. package/tests/hydration/compiled/server/reactivity.js +94 -106
  135. package/tests/hydration/compiled/server/return.js +28 -2172
  136. package/tests/hydration/compiled/server/switch.js +274 -286
  137. package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
  138. package/tests/hydration/compiled/server/try.js +167 -185
  139. package/tests/hydration/components/basic.tsrx +320 -272
  140. package/tests/hydration/components/composite.tsrx +44 -32
  141. package/tests/hydration/components/events.tsrx +101 -91
  142. package/tests/hydration/components/for.tsrx +510 -452
  143. package/tests/hydration/components/head.tsrx +87 -80
  144. package/tests/hydration/components/hmr.tsrx +22 -17
  145. package/tests/hydration/components/html-in-template.tsrx +22 -17
  146. package/tests/hydration/components/html.tsrx +525 -443
  147. package/tests/hydration/components/if-children.tsrx +158 -148
  148. package/tests/hydration/components/if.tsrx +109 -95
  149. package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
  150. package/tests/hydration/components/nested-control-flow.tsrx +215 -203
  151. package/tests/hydration/components/portal.tsrx +41 -34
  152. package/tests/hydration/components/reactivity.tsrx +37 -27
  153. package/tests/hydration/components/return.tsrx +12 -556
  154. package/tests/hydration/components/switch.tsrx +120 -114
  155. package/tests/hydration/components/track-async-serialization.tsrx +107 -91
  156. package/tests/hydration/components/try.tsrx +55 -40
  157. package/tests/hydration/html.test.js +4 -4
  158. package/tests/hydration/return.test.js +13 -532
  159. package/tests/server/await.test.tsrx +3 -3
  160. package/tests/server/basic.attributes.test.tsrx +264 -195
  161. package/tests/server/basic.components.test.tsrx +296 -169
  162. package/tests/server/basic.test.tsrx +300 -198
  163. package/tests/server/compiler.test.tsrx +62 -60
  164. package/tests/server/composite.props.test.tsrx +77 -63
  165. package/tests/server/composite.test.tsrx +168 -192
  166. package/tests/server/context.test.tsrx +18 -12
  167. package/tests/server/dynamic-elements.test.tsrx +197 -180
  168. package/tests/server/for.test.tsrx +85 -78
  169. package/tests/server/head.test.tsrx +50 -43
  170. package/tests/server/html-nesting-validation.test.tsrx +8 -8
  171. package/tests/server/if.test.tsrx +57 -51
  172. package/tests/server/lazy-destructuring.test.tsrx +366 -294
  173. package/tests/server/return.test.tsrx +76 -1355
  174. package/tests/server/streaming-ssr.test.tsrx +4 -75
  175. package/tests/server/style-identifier.test.tsrx +169 -148
  176. package/tests/server/switch.test.tsrx +91 -85
  177. package/tests/server/track-async-serialization.test.tsrx +105 -85
  178. package/tests/server/try.test.tsrx +374 -280
  179. package/tests/utils/compiler-compat-config.test.js +2 -2
  180. package/tests/utils/runtime-imports.test.js +10 -0
  181. package/types/index.d.ts +8 -0
  182. package/tests/client/__snapshots__/html.test.rsrx.snap +0 -40
@@ -3,8 +3,8 @@ import { flushSync, track } from 'ripple';
3
3
 
4
4
  describe('basic client > rendering & text', () => {
5
5
  it('renders static text', () => {
6
- component Basic() {
7
- <div>{'Hello World'}</div>
6
+ function Basic() {
7
+ return <><div>{'Hello World'}</div></>;
8
8
  }
9
9
 
10
10
  expect(container).toBeDefined();
@@ -14,10 +14,11 @@ describe('basic client > rendering & text', () => {
14
14
  });
15
15
 
16
16
  it('renders semi-dynamic text', () => {
17
- component Basic() {
18
- let message = 'Hello World';
19
-
20
- <div>{message}</div>
17
+ function Basic() {
18
+ return <>
19
+ let message = 'Hello World';
20
+ <div>{message}</div>
21
+ </>;
21
22
  }
22
23
 
23
24
  render(Basic);
@@ -25,11 +26,12 @@ describe('basic client > rendering & text', () => {
25
26
  expect(container).toMatchSnapshot();
26
27
  });
27
28
 
28
- it('renders explicit text interpolation without creating HTML', () => {
29
- component Basic() {
30
- let markup = '<span>Not HTML</span>';
31
-
32
- <div>{text markup}</div>
29
+ it('renders string interpolation without creating HTML', () => {
30
+ function Basic() {
31
+ return <>
32
+ let markup = '<span>Not HTML</span>';
33
+ <div>{markup}</div>
34
+ </>;
33
35
  }
34
36
 
35
37
  render(Basic);
@@ -41,11 +43,13 @@ describe('basic client > rendering & text', () => {
41
43
  });
42
44
 
43
45
  it('renders direct double-quoted text children as text', () => {
44
- component Basic() {
45
- <div class="entities">"Rock &amp; &quot;Roll&quot;"</div>
46
- <div class="backslash">"line\nbreak"</div>
47
- <pre class="multiline">"first
46
+ function Basic() {
47
+ return <>
48
+ <div class="entities">"Rock &amp; &quot;Roll&quot;"</div>
49
+ <div class="backslash">"line\nbreak"</div>
50
+ <pre class="multiline">"first
48
51
  second"</pre>
52
+ </>;
49
53
  }
50
54
 
51
55
  render(Basic);
@@ -55,13 +59,88 @@ second"</pre>
55
59
  expect(container.querySelector('.multiline').textContent).toBe('first\nsecond');
56
60
  });
57
61
 
58
- it('renders bare double-quoted text in if-else branches', () => {
59
- component App() {
60
- let condition = false;
62
+ it('does not render TSRX statements outside returned component templates', () => {
63
+ function Basic() {
64
+ const ready = true;
65
+
66
+ if (ready) {
67
+ <div class="leaked">"should not render"</div>
68
+ }
69
+
70
+ return <>"Hello world"</>;
71
+ }
72
+
73
+ render(Basic);
74
+
75
+ expect(container.textContent).toBe('Hello world');
76
+ expect(container.querySelector('.leaked')).toBeNull();
77
+ });
78
+
79
+ it('renders primitive component return branches', () => {
80
+ function Basic() {
81
+ const ready = false;
82
+
83
+ if (!ready) {
84
+ return 'Waiting';
85
+ }
86
+
87
+ return <>"Ready"</>;
88
+ }
89
+
90
+ render(Basic);
91
+
92
+ expect(container.textContent).toBe('Waiting');
93
+ });
94
+
95
+ it('renders a root plain function that returns only a string', () => {
96
+ function Basic() {
97
+ return 'Only text';
98
+ }
99
+
100
+ render(Basic);
101
+
102
+ expect(container.textContent).toBe('Only text');
103
+ });
104
+
105
+ it('renders a root plain function that returns only null', () => {
106
+ function Basic() {
107
+ return null;
108
+ }
109
+
110
+ render(Basic);
111
+
112
+ expect(container.textContent).toBe('');
113
+ });
114
+
115
+ it('renders a root plain function that returns only undefined', () => {
116
+ function Basic() {
117
+ return undefined;
118
+ }
119
+
120
+ render(Basic);
121
+
122
+ expect(container.textContent).toBe('');
123
+ });
61
124
 
62
- if (condition) {
63
- "Hello Ripple"
64
- } else "Hello React"
125
+ it('does not render elements after an ASI return', () => {
126
+ function Basic() {
127
+ return;
128
+ <div>"should not render"</div>
129
+ }
130
+
131
+ render(Basic);
132
+
133
+ expect(container.textContent).toBe('');
134
+ });
135
+
136
+ it('renders bare double-quoted text in if-else branches', () => {
137
+ function App() {
138
+ return <>
139
+ let condition = false;
140
+ if (condition) {
141
+ "Hello Ripple"
142
+ } else "Hello React"
143
+ </>;
65
144
  }
66
145
 
67
146
  render(App);
@@ -70,17 +149,18 @@ second"</pre>
70
149
  });
71
150
 
72
151
  it('renders dynamic text', () => {
73
- component Basic() {
74
- let &[message] = track('Hello World');
75
-
76
- <button
77
- onClick={() => {
78
- message = 'Hello Ripple';
79
- }}
80
- >
81
- {'Change Text'}
82
- </button>
83
- <div>{message}</div>
152
+ function Basic() {
153
+ return <>
154
+ let &[message] = track('Hello World');
155
+ <button
156
+ onClick={() => {
157
+ message = 'Hello Ripple';
158
+ }}
159
+ >
160
+ {'Change Text'}
161
+ </button>
162
+ <div>{message}</div>
163
+ </>;
84
164
  }
85
165
 
86
166
  render(Basic);
@@ -94,8 +174,8 @@ second"</pre>
94
174
  });
95
175
 
96
176
  it('renders empty string literal', () => {
97
- component Basic() {
98
- <div>{''}</div>
177
+ function Basic() {
178
+ return <><div>{''}</div></>;
99
179
  }
100
180
 
101
181
  render(Basic);
@@ -103,8 +183,8 @@ second"</pre>
103
183
  });
104
184
 
105
185
  it('renders empty template literal', () => {
106
- component Basic() {
107
- <div>{``}</div>
186
+ function Basic() {
187
+ return <><div>{``}</div></>;
108
188
  }
109
189
 
110
190
  render(Basic);
@@ -112,20 +192,22 @@ second"</pre>
112
192
  });
113
193
 
114
194
  it('renders tick template literal for nested children', () => {
115
- component Child({ level, children }: { level: number; children: any }) {
116
- if (level == 1) {
117
- <h1>{children}</h1>
118
- }
119
- if (level == 2) {
120
- <h2>{children}</h2>
121
- }
122
- if (level == 3) {
123
- <h3>{children}</h3>
124
- }
195
+ function Child({ level, children }: { level: number; children: any }) {
196
+ return <>
197
+ if (level == 1) {
198
+ <h1>{children}</h1>
199
+ }
200
+ if (level == 2) {
201
+ <h2>{children}</h2>
202
+ }
203
+ if (level == 3) {
204
+ <h3>{children}</h3>
205
+ }
206
+ </>;
125
207
  }
126
208
 
127
- component App() {
128
- <Child level={1}>{`Heading 1`}</Child>
209
+ function App() {
210
+ return <><Child level={1}>{`Heading 1`}</Child></>;
129
211
  }
130
212
 
131
213
  render(App);
@@ -133,13 +215,14 @@ second"</pre>
133
215
  });
134
216
 
135
217
  it('renders simple JS expression logic correctly', () => {
136
- component Example() {
137
- let test: Record<number, string> = {};
138
- let counter = 0;
139
- test[counter++] = 'Test';
140
-
141
- <div>{JSON.stringify(test)}</div>
142
- <div>{JSON.stringify(counter)}</div>
218
+ function Example() {
219
+ return <>
220
+ let test: Record<number, string> = {};
221
+ let counter = 0;
222
+ test[counter++] = 'Test';
223
+ <div>{JSON.stringify(test)}</div>
224
+ <div>{JSON.stringify(counter)}</div>
225
+ </>;
143
226
  }
144
227
  render(Example);
145
228
 
@@ -147,30 +230,31 @@ second"</pre>
147
230
  });
148
231
 
149
232
  it('renders with mixed static and dynamic content', () => {
150
- component Basic() {
151
- let &[name] = track('World');
152
- let &[count] = track(0);
153
- const staticMessage = 'Welcome to Ripple!';
154
-
155
- <div class="mixed-content">
156
- <h1>{staticMessage}</h1>
157
- <p class="greeting">{'Hello, ' + name + '!'}</p>
158
- <p class="notifications">{'You have ' + count + ' notifications'}</p>
159
- <button
160
- onClick={() => {
161
- count++;
162
- }}
163
- >
164
- {'Add Notification'}
165
- </button>
166
- <button
167
- onClick={() => {
168
- name = name === 'World' ? 'User' : 'World';
169
- }}
170
- >
171
- {'Toggle Name'}
172
- </button>
173
- </div>
233
+ function Basic() {
234
+ return <>
235
+ let &[name] = track('World');
236
+ let &[count] = track(0);
237
+ const staticMessage = 'Welcome to Ripple!';
238
+ <div class="mixed-content">
239
+ <h1>{staticMessage}</h1>
240
+ <p class="greeting">{'Hello, ' + name + '!'}</p>
241
+ <p class="notifications">{'You have ' + count + ' notifications'}</p>
242
+ <button
243
+ onClick={() => {
244
+ count++;
245
+ }}
246
+ >
247
+ {'Add Notification'}
248
+ </button>
249
+ <button
250
+ onClick={() => {
251
+ name = name === 'World' ? 'User' : 'World';
252
+ }}
253
+ >
254
+ {'Toggle Name'}
255
+ </button>
256
+ </div>
257
+ </>;
174
258
  }
175
259
 
176
260
  render(Basic);
@@ -194,14 +278,16 @@ second"</pre>
194
278
  });
195
279
 
196
280
  it('basic operations', () => {
197
- component App() {
198
- let &[count] = track(0);
199
- const a = count++;
200
- const b = ++count;
201
- <div>{a}</div>
202
- <div>{b}</div>
203
- <div>{5}</div>
204
- <div>{count}</div>
281
+ function App() {
282
+ return <>
283
+ let &[count] = track(0);
284
+ const a = count++;
285
+ const b = ++count;
286
+ <div>{a}</div>
287
+ <div>{b}</div>
288
+ <div>{5}</div>
289
+ <div>{count}</div>
290
+ </>;
205
291
  }
206
292
 
207
293
  render(App);
@@ -209,36 +295,36 @@ second"</pre>
209
295
  });
210
296
 
211
297
  it('renders with conditional rendering using if statements', () => {
212
- component Basic() {
213
- let &[showContent] = track(false);
214
- let &[userRole] = track('guest');
215
-
216
- <button
217
- onClick={() => {
218
- showContent = !showContent;
219
- }}
220
- >
221
- {'Toggle Content'}
222
- </button>
223
- <button
224
- onClick={() => {
225
- userRole = userRole === 'guest' ? 'admin' : 'guest';
226
- }}
227
- >
228
- {'Toggle Role'}
229
- </button>
230
-
231
- <div class="content">
232
- if (showContent) {
233
- if (userRole === 'admin') {
234
- <div class="admin-content">{'Admin content'}</div>
298
+ function Basic() {
299
+ return <>
300
+ let &[showContent] = track(false);
301
+ let &[userRole] = track('guest');
302
+ <button
303
+ onClick={() => {
304
+ showContent = !showContent;
305
+ }}
306
+ >
307
+ {'Toggle Content'}
308
+ </button>
309
+ <button
310
+ onClick={() => {
311
+ userRole = userRole === 'guest' ? 'admin' : 'guest';
312
+ }}
313
+ >
314
+ {'Toggle Role'}
315
+ </button>
316
+ <div class="content">
317
+ if (showContent) {
318
+ if (userRole === 'admin') {
319
+ <div class="admin-content">{'Admin content'}</div>
320
+ } else {
321
+ <div class="user-content">{'User content'}</div>
322
+ }
235
323
  } else {
236
- <div class="user-content">{'User content'}</div>
324
+ <div class="no-content">{'No content'}</div>
237
325
  }
238
- } else {
239
- <div class="no-content">{'No content'}</div>
240
- }
241
- </div>
326
+ </div>
327
+ </>;
242
328
  }
243
329
 
244
330
  render(Basic);
@@ -266,12 +352,14 @@ second"</pre>
266
352
  });
267
353
 
268
354
  it('should handle lexical scopes correctly', () => {
269
- component App() {
270
- <section>
271
- let sectionData = 'Nested scope variable';
272
-
273
- {sectionData}
274
- </section>
355
+ function App() {
356
+ return <>
357
+ <section>
358
+ let sectionData = 'Nested scope variable';
359
+
360
+ {sectionData}
361
+ </section>
362
+ </>;
275
363
  }
276
364
 
277
365
  render(App);
@@ -279,50 +367,48 @@ second"</pre>
279
367
  });
280
368
 
281
369
  it('runs nested JavaScript blocks inside component-local callables', () => {
282
- component App() {
283
- function readFunction() {
284
- const label = 'function outer';
285
- let result = '';
286
-
287
- {
288
- const label = 'function inner';
289
- result = label;
290
- }
291
-
292
- return `${result} / ${label}`;
293
- }
294
-
295
- const readArrow = () => {
296
- const offset = 5;
297
- let value = offset;
298
-
299
- {
300
- const offset = 17;
301
- value += offset;
302
- }
303
-
304
- return value;
305
- };
306
-
307
- class Reader {
308
- read() {
309
- const label = 'method outer';
370
+ function App() {
371
+ return <>
372
+ function readFunction() {
373
+ const label = 'function outer';
310
374
  let result = '';
311
375
 
312
376
  {
313
- const label = 'method inner';
377
+ const label = 'function inner';
314
378
  result = label;
315
379
  }
316
380
 
317
381
  return `${result} / ${label}`;
318
382
  }
319
- }
383
+ const readArrow = () => {
384
+ const offset = 5;
385
+ let value = offset;
386
+
387
+ {
388
+ const offset = 17;
389
+ value += offset;
390
+ }
391
+
392
+ return value;
393
+ };
394
+ class Reader {
395
+ read() {
396
+ const label = 'method outer';
397
+ let result = '';
320
398
 
321
- const reader = new Reader();
399
+ {
400
+ const label = 'method inner';
401
+ result = label;
402
+ }
322
403
 
323
- <div class="block-function">{readFunction()}</div>
324
- <div class="block-arrow">{readArrow()}</div>
325
- <div class="block-method">{reader.read()}</div>
404
+ return `${result} / ${label}`;
405
+ }
406
+ }
407
+ const reader = new Reader();
408
+ <div class="block-function">{readFunction()}</div>
409
+ <div class="block-arrow">{readArrow()}</div>
410
+ <div class="block-method">{reader.read()}</div>
411
+ </>;
326
412
  }
327
413
 
328
414
  render(App);
@@ -337,25 +423,27 @@ second"</pre>
337
423
  });
338
424
 
339
425
  it('should handle consecutive text nodes without duplication', () => {
340
- component App() {
341
- const Something = conditional('a');
342
-
343
- <Something />
344
-
345
- function conditional(item: 'a') {
346
- let hello = 'Hello';
347
- const obj = {
348
- a: component() {
349
- <div>
350
- {'a'}
351
- {' '}
352
- {hello}
353
- </div>
354
- },
355
- };
356
-
357
- return obj[item];
358
- }
426
+ function App() {
427
+ return <>
428
+ const Something = conditional('a');
429
+ <Something />
430
+ function conditional(item: 'a') {
431
+ let hello = 'Hello';
432
+ const obj = {
433
+ a: function A() {
434
+ return <>
435
+ <div>
436
+ {'a'}
437
+ {' '}
438
+ {hello}
439
+ </div>
440
+ </>;
441
+ },
442
+ };
443
+
444
+ return obj[item];
445
+ }
446
+ </>;
359
447
  }
360
448
 
361
449
  render(App);
@@ -364,14 +452,16 @@ second"</pre>
364
452
  });
365
453
 
366
454
  it('should handle multiple consecutive text expressions', () => {
367
- component App() {
368
- let name = 'World';
369
- <div>
370
- {'Hello'}
371
- {' '}
372
- {name}
373
- {'!'}
374
- </div>
455
+ function App() {
456
+ return <>
457
+ let name = 'World';
458
+ <div>
459
+ {'Hello'}
460
+ {' '}
461
+ {name}
462
+ {'!'}
463
+ </div>
464
+ </>;
375
465
  }
376
466
 
377
467
  render(App);
@@ -2,28 +2,29 @@ import { compile } from '@tsrx/ripple';
2
2
 
3
3
  describe('basic client > styling', () => {
4
4
  it('renders with styling scoped to component', () => {
5
- component Basic() {
6
- <div class="styled-container">
7
- <h1>{'Styled heading'}</h1>
8
- <p class="text">{'Styled paragraph'}</p>
9
- </div>
5
+ function Basic() {
6
+ return <>
7
+ <div class="styled-container">
8
+ <h1>{'Styled heading'}</h1>
9
+ <p class="text">{'Styled paragraph'}</p>
10
+ </div>
11
+ <style>
12
+ .styled-container {
13
+ background-color: rgb(0, 0, 255);
14
+ padding: 16px;
15
+ }
10
16
 
11
- <style>
12
- .styled-container {
13
- background-color: rgb(0, 0, 255);
14
- padding: 16px;
15
- }
17
+ h1 {
18
+ color: rgb(255, 255, 255);
19
+ font-size: 32px;
20
+ }
16
21
 
17
- h1 {
18
- color: rgb(255, 255, 255);
19
- font-size: 32px;
20
- }
21
-
22
- .text {
23
- color: rgb(200, 200, 200);
24
- font-size: 14px;
25
- }
26
- </style>
22
+ .text {
23
+ color: rgb(200, 200, 200);
24
+ font-size: 14px;
25
+ }
26
+ </style>
27
+ </>;
27
28
  }
28
29
 
29
30
  render(Basic);
@@ -38,7 +39,7 @@ describe('basic client > styling', () => {
38
39
  });
39
40
 
40
41
  it('renders with keyframes in styling scoped to component', () => {
41
- const source = `export component Basic() {
42
+ const source = `export function Basic() { return <>
42
43
  <div>
43
44
  <p>{'Styled paragraph'}</p>
44
45
  </div>
@@ -54,7 +55,7 @@ describe('basic client > styling', () => {
54
55
  animation-name: anim;
55
56
  }
56
57
  </style>
57
- }`;
58
+ </>; }`;
58
59
 
59
60
  const { css } = compile(source, 'test.tsrx');
60
61
  const name = css.match(/@keyframes\s+([a-zA-Z0-9_-]+)\s*\{/)[1];
@@ -5,15 +5,17 @@ describe('basic client > utilities', () => {
5
5
  let resolve: () => void;
6
6
  const promise = new Promise<void>((res) => (resolve = res));
7
7
 
8
- component Basic() {
9
- let &[value] = track(0);
10
- effect(() => {
11
- untrack(() => {
12
- value++;
13
- tick().then(() => resolve());
8
+ function Basic() {
9
+ return <>
10
+ let &[value] = track(0);
11
+ effect(() => {
12
+ untrack(() => {
13
+ value++;
14
+ tick().then(() => resolve());
15
+ });
14
16
  });
15
- });
16
- <p>{value}</p>
17
+ <p>{value}</p>
18
+ </>;
17
19
  }
18
20
  render(Basic);
19
21