ripple 0.3.71 → 0.3.74

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 (165) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +2 -8
  4. package/src/runtime/index-client.js +3 -13
  5. package/src/runtime/internal/client/blocks.js +3 -25
  6. package/src/runtime/internal/client/for.js +80 -5
  7. package/src/runtime/internal/client/index.js +0 -2
  8. package/src/runtime/internal/client/types.d.ts +0 -10
  9. package/tests/client/__snapshots__/computed-properties.test.tsrx.snap +8 -0
  10. package/tests/client/__snapshots__/for.test.tsrx.snap +22 -0
  11. package/tests/client/__snapshots__/html.test.tsrx.snap +4 -0
  12. package/tests/client/array/array.copy-within.test.tsrx +19 -19
  13. package/tests/client/array/array.derived.test.tsrx +97 -109
  14. package/tests/client/array/array.iteration.test.tsrx +28 -28
  15. package/tests/client/array/array.mutations.test.tsrx +68 -68
  16. package/tests/client/array/array.static.test.tsrx +82 -92
  17. package/tests/client/array/array.to-methods.test.tsrx +15 -15
  18. package/tests/client/async-suspend.test.tsrx +180 -179
  19. package/tests/client/basic/__snapshots__/basic.attributes.test.tsrx.snap +2 -0
  20. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +4 -0
  21. package/tests/client/basic/basic.attributes.test.tsrx +273 -317
  22. package/tests/client/basic/basic.collections.test.tsrx +59 -71
  23. package/tests/client/basic/basic.components.test.tsrx +196 -222
  24. package/tests/client/basic/basic.errors.test.tsrx +72 -78
  25. package/tests/client/basic/basic.events.test.tsrx +80 -85
  26. package/tests/client/basic/basic.get-set.test.tsrx +54 -64
  27. package/tests/client/basic/basic.hmr.test.tsrx +15 -19
  28. package/tests/client/basic/basic.reactivity.test.tsrx +121 -135
  29. package/tests/client/basic/basic.rendering.test.tsrx +273 -178
  30. package/tests/client/basic/basic.utilities.test.tsrx +8 -10
  31. package/tests/client/boundaries.test.tsrx +18 -18
  32. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -76
  33. package/tests/client/compiler/compiler.attributes.test.tsrx +18 -14
  34. package/tests/client/compiler/compiler.basic.test.tsrx +364 -296
  35. package/tests/client/compiler/compiler.regex.test.tsrx +40 -44
  36. package/tests/client/compiler/compiler.tracked-access.test.tsrx +57 -38
  37. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  38. package/tests/client/compiler/compiler.typescript.test.tsrx +4 -3
  39. package/tests/client/composite/composite.dynamic-components.test.tsrx +41 -44
  40. package/tests/client/composite/composite.generics.test.tsrx +165 -167
  41. package/tests/client/composite/composite.props.test.tsrx +66 -74
  42. package/tests/client/composite/composite.reactivity.test.tsrx +132 -166
  43. package/tests/client/composite/composite.render.test.tsrx +92 -101
  44. package/tests/client/computed-properties.test.tsrx +14 -18
  45. package/tests/client/context.test.tsrx +14 -18
  46. package/tests/client/css/global-additional-cases.test.tsrx +491 -437
  47. package/tests/client/css/global-advanced-selectors.test.tsrx +169 -153
  48. package/tests/client/css/global-at-rules.test.tsrx +71 -66
  49. package/tests/client/css/global-basic.test.tsrx +105 -98
  50. package/tests/client/css/global-classes-ids.test.tsrx +128 -114
  51. package/tests/client/css/global-combinators.test.tsrx +83 -78
  52. package/tests/client/css/global-complex-nesting.test.tsrx +134 -120
  53. package/tests/client/css/global-edge-cases.test.tsrx +138 -120
  54. package/tests/client/css/global-keyframes.test.tsrx +108 -96
  55. package/tests/client/css/global-nested.test.tsrx +88 -78
  56. package/tests/client/css/global-pseudo.test.tsrx +104 -98
  57. package/tests/client/css/global-scoping.test.tsrx +145 -125
  58. package/tests/client/css/style-identifier.test.tsrx +62 -69
  59. package/tests/client/date.test.tsrx +83 -83
  60. package/tests/client/dynamic-elements.test.tsrx +227 -283
  61. package/tests/client/events.test.tsrx +252 -266
  62. package/tests/client/for.test.tsrx +120 -127
  63. package/tests/client/head.test.tsrx +40 -48
  64. package/tests/client/html.test.tsrx +37 -49
  65. package/tests/client/input-value.test.tsrx +1125 -1354
  66. package/tests/client/lazy-array.test.tsrx +10 -16
  67. package/tests/client/lazy-destructuring.test.tsrx +169 -221
  68. package/tests/client/map.test.tsrx +39 -41
  69. package/tests/client/media-query.test.tsrx +15 -19
  70. package/tests/client/object.test.tsrx +46 -56
  71. package/tests/client/portal.test.tsrx +31 -37
  72. package/tests/client/ref.test.tsrx +173 -193
  73. package/tests/client/return.test.tsrx +62 -37
  74. package/tests/client/set.test.tsrx +33 -33
  75. package/tests/client/svg.test.tsrx +195 -215
  76. package/tests/client/switch.test.tsrx +201 -191
  77. package/tests/client/track-async-hydration.test.tsrx +14 -18
  78. package/tests/client/tracked-index-access.test.tsrx +18 -28
  79. package/tests/client/try.test.tsrx +494 -619
  80. package/tests/client/tsx.test.tsrx +290 -371
  81. package/tests/client/typescript-generics.test.tsrx +121 -129
  82. package/tests/client/url/url.derived.test.tsrx +21 -25
  83. package/tests/client/url/url.parsing.test.tsrx +35 -35
  84. package/tests/client/url/url.partial-removal.test.tsrx +32 -32
  85. package/tests/client/url/url.reactivity.test.tsrx +68 -72
  86. package/tests/client/url/url.serialization.test.tsrx +8 -8
  87. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +21 -27
  88. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +16 -16
  89. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +37 -37
  90. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +56 -60
  91. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +32 -34
  92. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +9 -9
  93. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +10 -10
  94. package/tests/hydration/compiled/client/basic.js +396 -325
  95. package/tests/hydration/compiled/client/composite.js +52 -44
  96. package/tests/hydration/compiled/client/for.js +734 -604
  97. package/tests/hydration/compiled/client/head.js +183 -103
  98. package/tests/hydration/compiled/client/html.js +93 -86
  99. package/tests/hydration/compiled/client/if-children.js +95 -71
  100. package/tests/hydration/compiled/client/if.js +113 -89
  101. package/tests/hydration/compiled/client/mixed-control-flow.js +225 -209
  102. package/tests/hydration/compiled/client/nested-control-flow.js +94 -98
  103. package/tests/hydration/compiled/client/reactivity.js +26 -24
  104. package/tests/hydration/compiled/client/return.js +8 -42
  105. package/tests/hydration/compiled/client/switch.js +208 -173
  106. package/tests/hydration/compiled/client/track-async-serialization.js +176 -128
  107. package/tests/hydration/compiled/client/try.js +29 -21
  108. package/tests/hydration/compiled/server/basic.js +210 -221
  109. package/tests/hydration/compiled/server/composite.js +13 -14
  110. package/tests/hydration/compiled/server/for.js +427 -444
  111. package/tests/hydration/compiled/server/head.js +199 -189
  112. package/tests/hydration/compiled/server/html.js +33 -41
  113. package/tests/hydration/compiled/server/if-children.js +114 -117
  114. package/tests/hydration/compiled/server/if.js +77 -83
  115. package/tests/hydration/compiled/server/mixed-control-flow.js +145 -150
  116. package/tests/hydration/compiled/server/nested-control-flow.js +10 -0
  117. package/tests/hydration/compiled/server/reactivity.js +24 -22
  118. package/tests/hydration/compiled/server/return.js +6 -18
  119. package/tests/hydration/compiled/server/switch.js +179 -176
  120. package/tests/hydration/compiled/server/track-async-serialization.js +88 -70
  121. package/tests/hydration/compiled/server/try.js +31 -35
  122. package/tests/hydration/components/basic.tsrx +216 -286
  123. package/tests/hydration/components/composite.tsrx +32 -42
  124. package/tests/hydration/components/events.tsrx +81 -101
  125. package/tests/hydration/components/for.tsrx +270 -336
  126. package/tests/hydration/components/head.tsrx +43 -39
  127. package/tests/hydration/components/hmr.tsrx +16 -22
  128. package/tests/hydration/components/html-in-template.tsrx +15 -21
  129. package/tests/hydration/components/html.tsrx +442 -526
  130. package/tests/hydration/components/if-children.tsrx +107 -125
  131. package/tests/hydration/components/if.tsrx +68 -90
  132. package/tests/hydration/components/mixed-control-flow.tsrx +65 -72
  133. package/tests/hydration/components/nested-control-flow.tsrx +202 -216
  134. package/tests/hydration/components/portal.tsrx +33 -41
  135. package/tests/hydration/components/reactivity.tsrx +26 -34
  136. package/tests/hydration/components/return.tsrx +4 -6
  137. package/tests/hydration/components/switch.tsrx +73 -78
  138. package/tests/hydration/components/track-async-serialization.tsrx +83 -93
  139. package/tests/hydration/components/try.tsrx +37 -51
  140. package/tests/hydration/switch.test.js +8 -8
  141. package/tests/server/await.test.tsrx +3 -3
  142. package/tests/server/basic.attributes.test.tsrx +120 -167
  143. package/tests/server/basic.components.test.tsrx +163 -197
  144. package/tests/server/basic.test.tsrx +298 -220
  145. package/tests/server/compiler.test.tsrx +142 -72
  146. package/tests/server/composite.props.test.tsrx +54 -58
  147. package/tests/server/composite.test.tsrx +165 -167
  148. package/tests/server/context.test.tsrx +13 -17
  149. package/tests/server/dynamic-elements.test.tsrx +103 -135
  150. package/tests/server/for.test.tsrx +115 -84
  151. package/tests/server/head.test.tsrx +31 -31
  152. package/tests/server/html-nesting-validation.test.tsrx +16 -8
  153. package/tests/server/if.test.tsrx +49 -59
  154. package/tests/server/lazy-destructuring.test.tsrx +288 -366
  155. package/tests/server/return.test.tsrx +58 -36
  156. package/tests/server/streaming-ssr.test.tsrx +4 -4
  157. package/tests/server/style-identifier.test.tsrx +58 -66
  158. package/tests/server/switch.test.tsrx +89 -97
  159. package/tests/server/track-async-serialization.test.tsrx +85 -103
  160. package/tests/server/try.test.tsrx +275 -360
  161. package/tests/utils/ref-types.test.js +72 -0
  162. package/tests/utils/vite-plugin-config.test.js +41 -74
  163. package/types/index.d.ts +1 -0
  164. package/src/runtime/internal/client/compat.js +0 -40
  165. package/tests/utils/compiler-compat-config.test.js +0 -38
@@ -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
- function Basic() {
7
- return <><div>{'Hello World'}</div></>;
6
+ function Basic() @{
7
+ <div>{'Hello World'}</div>
8
8
  }
9
9
 
10
10
  expect(container).toBeDefined();
@@ -14,11 +14,9 @@ describe('basic client > rendering & text', () => {
14
14
  });
15
15
 
16
16
  it('renders semi-dynamic text', () => {
17
- function Basic() {
18
- return <>
19
- let message = 'Hello World';
20
- <div>{message}</div>
21
- </>;
17
+ function Basic() @{
18
+ let message = 'Hello World';
19
+ <div>{message}</div>
22
20
  }
23
21
 
24
22
  render(Basic);
@@ -27,11 +25,9 @@ describe('basic client > rendering & text', () => {
27
25
  });
28
26
 
29
27
  it('renders string interpolation without creating HTML', () => {
30
- function Basic() {
31
- return <>
32
- let markup = '<span>Not HTML</span>';
33
- <div>{markup}</div>
34
- </>;
28
+ function Basic() @{
29
+ let markup = '<span>Not HTML</span>';
30
+ <div>{markup}</div>
35
31
  }
36
32
 
37
33
  render(Basic);
@@ -42,32 +38,35 @@ describe('basic client > rendering & text', () => {
42
38
  expect(div.querySelector('span')).toBeNull();
43
39
  });
44
40
 
45
- it('renders direct double-quoted text children as text', () => {
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
51
- second"</pre>
52
- </>;
41
+ it('renders direct JSX text children as text', () => {
42
+ function Basic() @{
43
+ <>
44
+ <div class="entities">Rock &amp; &quot;Roll&quot;</div>
45
+ <div class="backslash">line break</div>
46
+ <pre class="multiline">
47
+ first
48
+ second
49
+ </pre>
50
+ </>
53
51
  }
54
52
 
55
53
  render(Basic);
56
54
 
57
55
  expect(container.querySelector('.entities').textContent).toBe('Rock & "Roll"');
58
- expect(container.querySelector('.backslash').textContent).toBe('line\\nbreak');
56
+ expect(container.querySelector('.backslash').textContent).toBe('line break');
59
57
  expect(container.querySelector('.multiline').textContent).toBe('first\nsecond');
60
58
  });
61
59
 
62
- it('does not render TSRX statements outside returned component templates', () => {
60
+ it('does not render JavaScript statements outside returned component templates', () => {
63
61
  function Basic() {
64
62
  const ready = true;
65
63
 
66
64
  if (ready) {
67
- <div class="leaked">"should not render"</div>
65
+ const leaked = 'should not render';
66
+ void leaked;
68
67
  }
69
68
 
70
- return <>"Hello world"</>;
69
+ return <>Hello world</>;
71
70
  }
72
71
 
73
72
  render(Basic);
@@ -77,14 +76,12 @@ second"</pre>
77
76
  });
78
77
 
79
78
  it('renders primitive component return branches', () => {
80
- function Basic() {
79
+ function Basic() @{
81
80
  const ready = false;
82
-
83
81
  if (!ready) {
84
82
  return 'Waiting';
85
83
  }
86
-
87
- return <>"Ready"</>;
84
+ <>Ready</>
88
85
  }
89
86
 
90
87
  render(Basic);
@@ -122,10 +119,127 @@ second"</pre>
122
119
  expect(container.textContent).toBe('');
123
120
  });
124
121
 
125
- it('does not render elements after an ASI return', () => {
122
+ it('does not stringify adjacent call-containing expression children', () => {
123
+ function child(label: string) @{
124
+ <span>{label}</span>
125
+ }
126
+
127
+ function empty() {
128
+ return null;
129
+ }
130
+
131
+ const Constructed = function Constructed(label: string) {
132
+ return child(label);
133
+ } as unknown as {
134
+ new (label: string): ReturnType<typeof child>;
135
+ };
136
+
137
+ const factory = child;
138
+
139
+ function tag(_strings: TemplateStringsArray) {
140
+ return child('tagged');
141
+ }
142
+
143
+ const lookup = {
144
+ member: child('member'),
145
+ };
146
+
147
+ function getKey(): keyof typeof lookup {
148
+ return 'member';
149
+ }
150
+
151
+ function touch() {
152
+ return null;
153
+ }
154
+
155
+ let assigned;
156
+
157
+ function Basic() @{
158
+ <>
159
+ <div>
160
+ {'call:'}
161
+ {child('call')}
162
+ </div>
163
+ <div>
164
+ {'new:'}
165
+ {new Constructed('new')}
166
+ </div>
167
+ <div>
168
+ {'chain:'}
169
+ {factory?.('chain')}
170
+ </div>
171
+ <div>
172
+ {'ts-wrapper:'}
173
+ {child('ts-wrapper')!}
174
+ </div>
175
+ <div>
176
+ {'array:'}
177
+ {[child('array')]}
178
+ </div>
179
+ <div>
180
+ {'assignment:'}
181
+ {assigned = child('assignment')}
182
+ </div>
183
+ <div>
184
+ {'logical:'}
185
+ {true && child('logical')}
186
+ </div>
187
+ <div>
188
+ {'conditional:'}
189
+ {true ? child('conditional') : ''}
190
+ </div>
191
+ <div>
192
+ {'member:'}
193
+ {lookup[getKey()]}
194
+ </div>
195
+ <div>
196
+ {'object:'}
197
+ {{
198
+ [Symbol.for('ripple.element')]: true,
199
+ render() {
200
+ return child('object');
201
+ },
202
+ }}
203
+ </div>
204
+ <div>
205
+ {'sequence:'}
206
+ {(touch(), child('sequence'))}
207
+ </div>
208
+ <div>
209
+ {'tagged:'}
210
+ {tag`tagged`}
211
+ </div>
212
+ <div>
213
+ {'unary:'}
214
+ {void empty()}
215
+ </div>
216
+ </>
217
+ }
218
+
219
+ render(Basic);
220
+
221
+ expect([...container.querySelectorAll('div')].map((div) => div.textContent)).toEqual([
222
+ 'call:call',
223
+ 'new:new',
224
+ 'chain:chain',
225
+ 'ts-wrapper:ts-wrapper',
226
+ 'array:array',
227
+ 'assignment:assignment',
228
+ 'logical:logical',
229
+ 'conditional:conditional',
230
+ 'member:member',
231
+ 'object:object',
232
+ 'sequence:sequence',
233
+ 'tagged:tagged',
234
+ 'unary:',
235
+ ]);
236
+ });
237
+
238
+ it('does not render unreachable statements after an ASI return', () => {
126
239
  function Basic() {
127
240
  return;
128
- <div>"should not render"</div>
241
+ const leaked = 'should not render';
242
+ void leaked;
129
243
  }
130
244
 
131
245
  render(Basic);
@@ -133,14 +247,14 @@ second"</pre>
133
247
  expect(container.textContent).toBe('');
134
248
  });
135
249
 
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
- </>;
250
+ it('renders bare JSX text in if-else branches', () => {
251
+ function App() @{
252
+ let condition = false;
253
+ @if (condition) {
254
+ <>Hello Ripple</>
255
+ } @else {
256
+ <>Hello React</>
257
+ }
144
258
  }
145
259
 
146
260
  render(App);
@@ -149,18 +263,16 @@ second"</pre>
149
263
  });
150
264
 
151
265
  it('renders dynamic text', () => {
152
- function Basic() {
153
- return <>
154
- let &[message] = track('Hello World');
266
+ function Basic() @{
267
+ let &[message] = track('Hello World');
268
+ <>
155
269
  <button
156
270
  onClick={() => {
157
271
  message = 'Hello Ripple';
158
272
  }}
159
- >
160
- {'Change Text'}
161
- </button>
273
+ >{'Change Text'}</button>
162
274
  <div>{message}</div>
163
- </>;
275
+ </>
164
276
  }
165
277
 
166
278
  render(Basic);
@@ -174,8 +286,8 @@ second"</pre>
174
286
  });
175
287
 
176
288
  it('renders empty string literal', () => {
177
- function Basic() {
178
- return <><div>{''}</div></>;
289
+ function Basic() @{
290
+ <div>{''}</div>
179
291
  }
180
292
 
181
293
  render(Basic);
@@ -183,8 +295,8 @@ second"</pre>
183
295
  });
184
296
 
185
297
  it('renders empty template literal', () => {
186
- function Basic() {
187
- return <><div>{``}</div></>;
298
+ function Basic() @{
299
+ <div>{``}</div>
188
300
  }
189
301
 
190
302
  render(Basic);
@@ -192,22 +304,22 @@ second"</pre>
192
304
  });
193
305
 
194
306
  it('renders tick template literal for nested children', () => {
195
- function Child({ level, children }: { level: number; children: any }) {
196
- return <>
197
- if (level == 1) {
307
+ function Child({ level, children }: { level: number; children: any }) @{
308
+ <>
309
+ @if (level == 1) {
198
310
  <h1>{children}</h1>
199
311
  }
200
- if (level == 2) {
312
+ @if (level == 2) {
201
313
  <h2>{children}</h2>
202
314
  }
203
- if (level == 3) {
315
+ @if (level == 3) {
204
316
  <h3>{children}</h3>
205
317
  }
206
- </>;
318
+ </>
207
319
  }
208
320
 
209
- function App() {
210
- return <><Child level={1}>{`Heading 1`}</Child></>;
321
+ function App() @{
322
+ <Child level={1}>{`Heading 1`}</Child>
211
323
  }
212
324
 
213
325
  render(App);
@@ -215,14 +327,14 @@ second"</pre>
215
327
  });
216
328
 
217
329
  it('renders simple JS expression logic correctly', () => {
218
- function Example() {
219
- return <>
220
- let test: Record<number, string> = {};
221
- let counter = 0;
222
- test[counter++] = 'Test';
330
+ function Example() @{
331
+ let test: Record<number, string> = {};
332
+ let counter = 0;
333
+ test[counter++] = 'Test';
334
+ <>
223
335
  <div>{JSON.stringify(test)}</div>
224
336
  <div>{JSON.stringify(counter)}</div>
225
- </>;
337
+ </>
226
338
  }
227
339
  render(Example);
228
340
 
@@ -230,31 +342,29 @@ second"</pre>
230
342
  });
231
343
 
232
344
  it('renders with mixed static and dynamic content', () => {
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
- </>;
345
+ function Basic() @{
346
+ let &[name] = track('World');
347
+ let &[count] = track(0);
348
+ const staticMessage = 'Welcome to Ripple!';
349
+ <div class="mixed-content">
350
+ <h1>{staticMessage}</h1>
351
+ <p class="greeting">
352
+ {'Hello, ' + name + '!'}
353
+ </p>
354
+ <p class="notifications">
355
+ {'You have ' + count + ' notifications'}
356
+ </p>
357
+ <button
358
+ onClick={() => {
359
+ count++;
360
+ }}
361
+ >{'Add Notification'}</button>
362
+ <button
363
+ onClick={() => {
364
+ name = name === 'World' ? 'User' : 'World';
365
+ }}
366
+ >{'Toggle Name'}</button>
367
+ </div>
258
368
  }
259
369
 
260
370
  render(Basic);
@@ -278,16 +388,16 @@ second"</pre>
278
388
  });
279
389
 
280
390
  it('basic operations', () => {
281
- function App() {
282
- return <>
283
- let &[count] = track(0);
284
- const a = count++;
285
- const b = ++count;
391
+ function App() @{
392
+ let &[count] = track(0);
393
+ const a = count++;
394
+ const b = ++count;
395
+ <>
286
396
  <div>{a}</div>
287
397
  <div>{b}</div>
288
398
  <div>{5}</div>
289
399
  <div>{count}</div>
290
- </>;
400
+ </>
291
401
  }
292
402
 
293
403
  render(App);
@@ -295,36 +405,32 @@ second"</pre>
295
405
  });
296
406
 
297
407
  it('renders with conditional rendering using if statements', () => {
298
- function Basic() {
299
- return <>
300
- let &[showContent] = track(false);
301
- let &[userRole] = track('guest');
408
+ function Basic() @{
409
+ let &[showContent] = track(false);
410
+ let &[userRole] = track('guest');
411
+ <>
302
412
  <button
303
413
  onClick={() => {
304
414
  showContent = !showContent;
305
415
  }}
306
- >
307
- {'Toggle Content'}
308
- </button>
416
+ >{'Toggle Content'}</button>
309
417
  <button
310
418
  onClick={() => {
311
419
  userRole = userRole === 'guest' ? 'admin' : 'guest';
312
420
  }}
313
- >
314
- {'Toggle Role'}
315
- </button>
421
+ >{'Toggle Role'}</button>
316
422
  <div class="content">
317
- if (showContent) {
318
- if (userRole === 'admin') {
423
+ @if (showContent) {
424
+ @if (userRole === 'admin') {
319
425
  <div class="admin-content">{'Admin content'}</div>
320
- } else {
426
+ } @else {
321
427
  <div class="user-content">{'User content'}</div>
322
428
  }
323
- } else {
429
+ } @else {
324
430
  <div class="no-content">{'No content'}</div>
325
431
  }
326
432
  </div>
327
- </>;
433
+ </>
328
434
  }
329
435
 
330
436
  render(Basic);
@@ -352,14 +458,9 @@ second"</pre>
352
458
  });
353
459
 
354
460
  it('should handle lexical scopes correctly', () => {
355
- function App() {
356
- return <>
357
- <section>
358
- let sectionData = 'Nested scope variable';
359
-
360
- {sectionData}
361
- </section>
362
- </>;
461
+ function App() @{
462
+ let sectionData = 'Nested scope variable';
463
+ <section>{sectionData}</section>
363
464
  }
364
465
 
365
466
  render(App);
@@ -367,48 +468,48 @@ second"</pre>
367
468
  });
368
469
 
369
470
  it('runs nested JavaScript blocks inside component-local callables', () => {
370
- function App() {
371
- return <>
372
- function readFunction() {
373
- const label = 'function outer';
374
- let result = '';
471
+ function App() @{
472
+ function readFunction() {
473
+ const label = 'function outer';
474
+ let result = '';
475
+
476
+ {
477
+ const label = 'function inner';
478
+ result = label;
479
+ }
375
480
 
376
- {
377
- const label = 'function inner';
378
- result = label;
379
- }
481
+ return `${result} / ${label}`;
482
+ }
483
+ const readArrow = () => {
484
+ const offset = 5;
485
+ let value = offset;
380
486
 
381
- return `${result} / ${label}`;
487
+ {
488
+ const offset = 17;
489
+ value += offset;
382
490
  }
383
- const readArrow = () => {
384
- const offset = 5;
385
- let value = offset;
491
+
492
+ return value;
493
+ };
494
+ class Reader {
495
+ read() {
496
+ const label = 'method outer';
497
+ let result = '';
386
498
 
387
499
  {
388
- const offset = 17;
389
- value += offset;
500
+ const label = 'method inner';
501
+ result = label;
390
502
  }
391
503
 
392
- return value;
393
- };
394
- class Reader {
395
- read() {
396
- const label = 'method outer';
397
- let result = '';
398
-
399
- {
400
- const label = 'method inner';
401
- result = label;
402
- }
403
-
404
- return `${result} / ${label}`;
405
- }
504
+ return `${result} / ${label}`;
406
505
  }
407
- const reader = new Reader();
506
+ }
507
+ const reader = new Reader();
508
+ <>
408
509
  <div class="block-function">{readFunction()}</div>
409
510
  <div class="block-arrow">{readArrow()}</div>
410
511
  <div class="block-method">{reader.read()}</div>
411
- </>;
512
+ </>
412
513
  }
413
514
 
414
515
  render(App);
@@ -423,27 +524,23 @@ second"</pre>
423
524
  });
424
525
 
425
526
  it('should handle consecutive text nodes without duplication', () => {
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
- };
527
+ function App() @{
528
+ const Something = conditional('a');
529
+ function conditional(item: 'a') {
530
+ let hello = 'Hello';
531
+ const obj = {
532
+ a: function A() @{
533
+ <div>
534
+ {'a'}
535
+ {' '}
536
+ {hello}
537
+ </div>
538
+ },
539
+ };
443
540
 
444
- return obj[item];
445
- }
446
- </>;
541
+ return obj[item];
542
+ }
543
+ <Something />
447
544
  }
448
545
 
449
546
  render(App);
@@ -452,16 +549,14 @@ second"</pre>
452
549
  });
453
550
 
454
551
  it('should handle multiple consecutive text expressions', () => {
455
- function App() {
456
- return <>
457
- let name = 'World';
458
- <div>
459
- {'Hello'}
460
- {' '}
461
- {name}
462
- {'!'}
463
- </div>
464
- </>;
552
+ function App() @{
553
+ let name = 'World';
554
+ <div>
555
+ {'Hello'}
556
+ {' '}
557
+ {name}
558
+ {'!'}
559
+ </div>
465
560
  }
466
561
 
467
562
  render(App);
@@ -5,17 +5,15 @@ describe('basic client > utilities', () => {
5
5
  let resolve: () => void;
6
6
  const promise = new Promise<void>((res) => (resolve = res));
7
7
 
8
- function Basic() {
9
- return <>
10
- let &[value] = track(0);
11
- effect(() => {
12
- untrack(() => {
13
- value++;
14
- tick().then(() => resolve());
15
- });
8
+ function Basic() @{
9
+ let &[value] = track(0);
10
+ effect(() => {
11
+ untrack(() => {
12
+ value++;
13
+ tick().then(() => resolve());
16
14
  });
17
- <p>{value}</p>
18
- </>;
15
+ });
16
+ <p>{value}</p>
19
17
  }
20
18
  render(Basic);
21
19