ripple 0.3.72 → 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 +66 -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 +55 -61
  23. package/tests/client/basic/basic.components.test.tsrx +196 -218
  24. package/tests/client/basic/basic.errors.test.tsrx +70 -76
  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 +357 -288
  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 +286 -292
  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 +390 -319
  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 -258
  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 +117 -162
  143. package/tests/server/basic.components.test.tsrx +163 -193
  144. package/tests/server/basic.test.tsrx +298 -198
  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 type { Tracked, PropsNoChildren } from 'ripple';
3
3
 
4
4
  describe('basic client', () => {
5
5
  it('render static text', async () => {
6
- function Basic() {
7
- return <><div>{'Hello World'}</div></>;
6
+ function Basic() @{
7
+ <div>{'Hello World'}</div>
8
8
  }
9
9
 
10
10
  const { head, body } = await render(Basic);
@@ -14,11 +14,9 @@ describe('basic client', () => {
14
14
  });
15
15
 
16
16
  it('renders string interpolation as escaped text', async () => {
17
- function Basic() {
18
- return <>
19
- let markup = '<span>Not HTML</span>';
20
- <div>{markup}</div>
21
- </>;
17
+ function Basic() @{
18
+ let markup = '<span>Not HTML</span>';
19
+ <div>{markup}</div>
22
20
  }
23
21
 
24
22
  const { body } = await render(Basic);
@@ -26,32 +24,35 @@ describe('basic client', () => {
26
24
  expect(body).toBeHtml('<div>&lt;span>Not HTML&lt;/span></div>');
27
25
  });
28
26
 
29
- it('renders direct double-quoted text children as text', async () => {
30
- function Basic() {
31
- return <>
32
- <div>"Rock &amp; &quot;Roll&quot;"</div>
33
- <div>"line\nbreak"</div>
34
- <pre>"first
35
- second"</pre>
36
- </>;
27
+ it('renders direct JSX text children as text', async () => {
28
+ function Basic() @{
29
+ <>
30
+ <div>Rock &amp; &quot;Roll&quot;</div>
31
+ <div>line break</div>
32
+ <pre>
33
+ first
34
+ second
35
+ </pre>
36
+ </>
37
37
  }
38
38
 
39
39
  const { body } = await render(Basic);
40
40
 
41
41
  expect(body).toBeHtml(
42
- '<div>Rock &amp; "Roll"</div><div>line\\nbreak</div><pre>first\nsecond</pre>',
42
+ '<div>Rock &amp; "Roll"</div><div>line break</div><pre>first\nsecond</pre>',
43
43
  );
44
44
  });
45
45
 
46
- it('does not render TSRX statements outside returned component templates', async () => {
47
- function Basic() {
46
+ it('does not render JavaScript statements outside returned component templates', async () => {
47
+ function Basic() @{
48
48
  const ready = true;
49
49
 
50
50
  if (ready) {
51
- <div class="leaked">"should not render"</div>
51
+ const leaked = 'should not render';
52
+ void leaked;
52
53
  }
53
54
 
54
- return <>"Hello world"</>;
55
+ <>Hello world</>
55
56
  }
56
57
 
57
58
  const { body } = await render(Basic);
@@ -60,14 +61,12 @@ second"</pre>
60
61
  });
61
62
 
62
63
  it('renders primitive component return branches', async () => {
63
- function Basic() {
64
+ function Basic() @{
64
65
  const ready = false;
65
-
66
66
  if (!ready) {
67
67
  return 'Waiting';
68
68
  }
69
-
70
- return <>"Ready"</>;
69
+ <>Ready</>
71
70
  }
72
71
 
73
72
  const { body } = await render(Basic);
@@ -105,10 +104,115 @@ second"</pre>
105
104
  expect(body).toBeHtml('');
106
105
  });
107
106
 
108
- it('does not render elements after an ASI return', async () => {
107
+ it('does not stringify adjacent call-containing expression children', async () => {
108
+ function child(label: string) @{
109
+ <span>{label}</span>
110
+ }
111
+
112
+ function empty() {
113
+ return null;
114
+ }
115
+
116
+ const Constructed = function Constructed(label: string) {
117
+ return child(label);
118
+ } as unknown as {
119
+ new (label: string): ReturnType<typeof child>;
120
+ };
121
+
122
+ const factory = child;
123
+
124
+ function tag(_strings: TemplateStringsArray) {
125
+ return child('tagged');
126
+ }
127
+
128
+ const lookup = {
129
+ member: child('member'),
130
+ };
131
+
132
+ function getKey(): keyof typeof lookup {
133
+ return 'member';
134
+ }
135
+
136
+ function touch() {
137
+ return null;
138
+ }
139
+
140
+ let assigned;
141
+
142
+ function Basic() @{
143
+ <>
144
+ <div>
145
+ {'call:'}
146
+ {child('call')}
147
+ </div>
148
+ <div>
149
+ {'new:'}
150
+ {new Constructed('new')}
151
+ </div>
152
+ <div>
153
+ {'chain:'}
154
+ {factory?.('chain')}
155
+ </div>
156
+ <div>
157
+ {'ts-wrapper:'}
158
+ {child('ts-wrapper')!}
159
+ </div>
160
+ <div>
161
+ {'array:'}
162
+ {[child('array')]}
163
+ </div>
164
+ <div>
165
+ {'assignment:'}
166
+ {assigned = child('assignment')}
167
+ </div>
168
+ <div>
169
+ {'logical:'}
170
+ {true && child('logical')}
171
+ </div>
172
+ <div>
173
+ {'conditional:'}
174
+ {true ? child('conditional') : ''}
175
+ </div>
176
+ <div>
177
+ {'member:'}
178
+ {lookup[getKey()]}
179
+ </div>
180
+ <div>
181
+ {'object:'}
182
+ {{
183
+ [Symbol.for('ripple.element')]: true,
184
+ render() {
185
+ return child('object');
186
+ },
187
+ }}
188
+ </div>
189
+ <div>
190
+ {'sequence:'}
191
+ {(touch(), child('sequence'))}
192
+ </div>
193
+ <div>
194
+ {'tagged:'}
195
+ {tag`tagged`}
196
+ </div>
197
+ <div>
198
+ {'unary:'}
199
+ {void empty()}
200
+ </div>
201
+ </>
202
+ }
203
+
204
+ const { body } = await render(Basic);
205
+
206
+ expect(body).toBeHtml(
207
+ '<div>call:<span>call</span></div><div>new:<span>new</span></div><div>chain:<span>chain</span></div><div>ts-wrapper:<span>ts-wrapper</span></div><div>array:<span>array</span></div><div>assignment:<span>assignment</span></div><div>logical:<span>logical</span></div><div>conditional:<span>conditional</span></div><div>member:<span>member</span></div><div>object:<span>object</span></div><div>sequence:<span>sequence</span></div><div>tagged:<span>tagged</span></div><div>unary:</div>',
208
+ );
209
+ });
210
+
211
+ it('does not render unreachable statements after an ASI return', async () => {
109
212
  function Basic() {
110
213
  return;
111
- <div>"should not render"</div>
214
+ const leaked = 'should not render';
215
+ void leaked;
112
216
  }
113
217
 
114
218
  const { body } = await render(Basic);
@@ -117,14 +221,10 @@ second"</pre>
117
221
  });
118
222
 
119
223
  it('renders inline tsrx fragments', async () => {
120
- function Basic() {
121
- return <>
122
- const content = <>
123
- const label = 'Server';
124
- <span>{label}</span>
125
- </>;
126
- <div>{content}</div>
127
- </>;
224
+ function Basic() @{
225
+ const label = 'Server';
226
+ const content = <span>{label}</span>;
227
+ <div>{content}</div>
128
228
  }
129
229
 
130
230
  const { body } = await render(Basic);
@@ -133,11 +233,9 @@ second"</pre>
133
233
  });
134
234
 
135
235
  it('renders Fragment innerHTML', async () => {
136
- function Basic() {
137
- return <>
138
- const html = '<strong>Server Fragment HTML</strong>';
139
- <Fragment innerHTML={html} />
140
- </>;
236
+ function Basic() @{
237
+ const html = '<strong>Server Fragment HTML</strong>';
238
+ <Fragment innerHTML={html} />
141
239
  }
142
240
 
143
241
  const { body } = await render(Basic);
@@ -148,20 +246,20 @@ second"</pre>
148
246
 
149
247
  it('renders deeply nested tsx and tsrx expression values', async () => {
150
248
  function makeFragment(label: string) {
249
+ const test = <>
250
+ {[1, 2, 3, 4].map((item) => <div class="helper-item">{item}</div>)}
251
+ </>;
151
252
  return <>
152
253
  <span class="label">{label}</span>
153
- const test = <>
154
- {[1, 2, 3, 4].map((item) => <>{<><div class="helper-item">{item}</div></>}</>)}
155
- </>;
156
254
  {test}
157
255
  </>;
158
256
  }
159
257
 
160
- function Basic() {
161
- return <>
162
- {<>{[1, 2, 3].map((item) => <div class="app-item">{item}</div>)}</>}
258
+ function Basic() @{
259
+ <>
260
+ {[1, 2, 3].map((item) => <div class="app-item">{item}</div>)}
163
261
  {makeFragment('from helper')}
164
- </>;
262
+ </>
165
263
  }
166
264
 
167
265
  const { body } = await render(Basic);
@@ -172,15 +270,15 @@ second"</pre>
172
270
  });
173
271
 
174
272
  it('renders tsrx nested directly inside a top-level tsx expression value', async () => {
175
- function Basic() {
176
- return <>
177
- const content = <>
178
- <section class="outer">
179
- {<><div class="inner">{'from tsrx'}</div></>}
180
- </section>
181
- </>;
182
- {content}
273
+ function Basic() @{
274
+ const content = <>
275
+ <section class="outer">
276
+ <div class="inner">{'from tsrx'}</div>
277
+ </section>
183
278
  </>;
279
+ <>
280
+ {content}
281
+ </>
184
282
  }
185
283
 
186
284
  const { body } = await render(Basic);
@@ -189,19 +287,17 @@ second"</pre>
189
287
  });
190
288
 
191
289
  it('renders nested elements from tsrx inside a top-level tsx value', async () => {
192
- function Basic() {
193
- return <>
194
- const content = <>
195
- <div class="wrapper">
196
- {<>
197
- <section class="native">
198
- <span class="nested-tsrx">{'inside nested tsrx'}</span>
199
- </section>
200
- </>}
201
- </div>
202
- </>;
203
- {content}
290
+ function Basic() @{
291
+ const content = <>
292
+ <div class="wrapper">
293
+ <section class="native">
294
+ <span class="nested-tsrx">{'inside nested tsrx'}</span>
295
+ </section>
296
+ </div>
204
297
  </>;
298
+ <>
299
+ {content}
300
+ </>
205
301
  }
206
302
 
207
303
  const { body } = await render(Basic);
@@ -212,12 +308,12 @@ second"</pre>
212
308
  });
213
309
 
214
310
  it('renders tsx declared before a top-level tsx value', async () => {
215
- function Basic() {
216
- return <>
217
- const nested = <><span class="nested-tsx">{'inside nested tsx'}</span></>;
218
- const content = <><div class="native">{nested}</div></>;
311
+ function Basic() @{
312
+ const nested = <span class="nested-tsx">inside nested tsx</span>;
313
+ const content = <><div class="native">{nested}</div></>;
314
+ <>
219
315
  {content}
220
- </>;
316
+ </>
221
317
  }
222
318
 
223
319
  const { body } = await render(Basic);
@@ -228,19 +324,17 @@ second"</pre>
228
324
  });
229
325
 
230
326
  it('flattens nested primitive arrays inside mixed tsrx collections', async () => {
231
- function Basic() {
232
- return <>
233
- <div>
234
- {<>
235
- {[
236
- 'start:',
237
- ['one', 2, true, null, false],
238
- <strong>{'!'}</strong>,
239
- ':end',
240
- ]}
241
- </>}
242
- </div>
243
- </>;
327
+ function Basic() @{
328
+ <div>
329
+ {<>
330
+ {[
331
+ 'start:',
332
+ ['one', 2, true, null, false],
333
+ <strong>{'!'}</strong>,
334
+ ':end',
335
+ ]}
336
+ </>}
337
+ </div>
244
338
  }
245
339
 
246
340
  const { body } = await render(Basic);
@@ -249,12 +343,14 @@ second"</pre>
249
343
  });
250
344
 
251
345
  it('flattens direct primitive array expressions', async () => {
252
- function Basic() {
253
- return <>
254
- const items = ['start:', ['one', 2], null, true, false, ':end'];
255
- <div>{['start:', ['one', 2], null, true, false, ':end']}</div>
346
+ function Basic() @{
347
+ const items = ['start:', ['one', 2], null, true, false, ':end'];
348
+ <>
349
+ <div>
350
+ {['start:', ['one', 2], null, true, false, ':end']}
351
+ </div>
256
352
  <div>{items}</div>
257
- </>;
353
+ </>
258
354
  }
259
355
 
260
356
  const { body } = await render(Basic);
@@ -263,16 +359,16 @@ second"</pre>
263
359
  });
264
360
 
265
361
  it('flattens conditional primitive array expressions', async () => {
266
- function Basic() {
267
- return <>
268
- const condition = true;
269
- const ternary_items = condition
270
- ? ['start:', ['one', 2], null, true, false, ':end']
271
- : ['fallback'];
272
- const logical_items = condition && ['start:', ['one', 2], null, true, false, ':end'];
362
+ function Basic() @{
363
+ const condition = true;
364
+ const ternary_items = condition
365
+ ? ['start:', ['one', 2], null, true, false, ':end']
366
+ : ['fallback'];
367
+ const logical_items = condition && ['start:', ['one', 2], null, true, false, ':end'];
368
+ <>
273
369
  <div>{ternary_items}</div>
274
370
  <div>{logical_items}</div>
275
- </>;
371
+ </>
276
372
  }
277
373
 
278
374
  const { body } = await render(Basic);
@@ -281,13 +377,11 @@ second"</pre>
281
377
  });
282
378
 
283
379
  it('renders tracked state updates', async () => {
284
- function Counter() {
285
- return <>
286
- let &[count] = track(0);
287
- count++;
288
- count = count + 5;
289
- <div>{count}</div>
290
- </>;
380
+ function Counter() @{
381
+ let &[count] = track(0);
382
+ count++;
383
+ count = count + 5;
384
+ <div>{count}</div>
291
385
  }
292
386
 
293
387
  const { body } = await render(Counter);
@@ -299,19 +393,17 @@ second"</pre>
299
393
  function Child({ count, ...rest }: PropsNoChildren<{
300
394
  count: Tracked<number>;
301
395
  class: { test: boolean };
302
- }>) {
303
- return <>
396
+ }>) @{
397
+ <>
304
398
  <div {...rest}>{'Child Component'}</div>
305
399
  <div>{count.value}</div>
306
- </>;
400
+ </>
307
401
  }
308
402
 
309
- function Parent() {
310
- return <>
311
- const count = track(10);
312
- let Dynamic = track(() => Child);
313
- <@Dynamic {count} class={{ test: true }} />
314
- </>;
403
+ function Parent() @{
404
+ const count = track(10);
405
+ let Dynamic = track(() => Child);
406
+ <@Dynamic {count} class={{ test: true }} />
315
407
  }
316
408
 
317
409
  const { body } = await render(Parent);
@@ -320,13 +412,11 @@ second"</pre>
320
412
  });
321
413
 
322
414
  it('renders tracked object properties', async () => {
323
- function ObjCounter() {
324
- return <>
325
- const obj = { count: track(2) };
326
- obj.count.value += 3;
327
- obj.count.value = obj.count.value + 1;
328
- <div>{obj.count.value}</div>
329
- </>;
415
+ function ObjCounter() @{
416
+ const obj = { count: track(2) };
417
+ obj.count.value += 3;
418
+ obj.count.value = obj.count.value + 1;
419
+ <div>{obj.count.value}</div>
330
420
  }
331
421
 
332
422
  const { body } = await render(ObjCounter);
@@ -335,13 +425,11 @@ second"</pre>
335
425
  });
336
426
 
337
427
  it('renders spread props with tracked values', async () => {
338
- function SpreadProps() {
339
- return <>
340
- let &[id] = track('unique-id');
341
- let &[isActive] = track(true);
342
- let &[styles] = track({ color: 'red', fontSize: '16px' });
343
- <div {...{ id: id, class: { active: isActive }, style: styles }}>{'Spread Props'}</div>
344
- </>;
428
+ function SpreadProps() @{
429
+ let &[id] = track('unique-id');
430
+ let &[isActive] = track(true);
431
+ let &[styles] = track({ color: 'red', fontSize: '16px' });
432
+ <div {...{ id: id, class: { active: isActive }, style: styles }}>{'Spread Props'}</div>
345
433
  }
346
434
 
347
435
  const { body } = await render(SpreadProps);
@@ -352,15 +440,15 @@ second"</pre>
352
440
  });
353
441
 
354
442
  it('handles AssignExpressions with tracked values or properties correctly', async () => {
355
- function Assignments() {
356
- return <>
357
- let &[count] = track(0);
358
- const obj = { value: track(5) };
359
- count += 10;
360
- obj.value.value *= 2;
443
+ function Assignments() @{
444
+ let &[count] = track(0);
445
+ const obj = { value: track(5) };
446
+ count += 10;
447
+ obj.value.value *= 2;
448
+ <>
361
449
  <div>{count}</div>
362
450
  <div>{obj.value.value}</div>
363
- </>;
451
+ </>
364
452
  }
365
453
 
366
454
  const { body } = await render(Assignments);
@@ -369,15 +457,17 @@ second"</pre>
369
457
  });
370
458
 
371
459
  it(`handles derived changes via tracked dependencies' changes`, async () => {
372
- function Derived() {
373
- return <>
374
- let &[base] = track(5);
375
- let &[multiplier] = track(3);
376
- let &[derived] = track(() => base * multiplier);
377
- <div>{derived}</div>
378
- base += 2;
379
- <div>{derived}</div>
380
- </>;
460
+ function Derived() @{
461
+ let &[base] = track(5);
462
+ let &[multiplier] = track(3);
463
+ let &[derived] = track(() => base * multiplier);
464
+ const before = derived;
465
+ base += 2;
466
+ const after = derived;
467
+ <>
468
+ <div>{before}</div>
469
+ <div>{after}</div>
470
+ </>
381
471
  }
382
472
 
383
473
  const { body } = await render(Derived);
@@ -386,16 +476,18 @@ second"</pre>
386
476
  });
387
477
 
388
478
  it(`handles derived changes based on another derived's dependencies' changes`, async () => {
389
- function NestedDerived() {
390
- return <>
391
- let &[a] = track(2);
392
- let &[b] = track(3);
393
- let &[sum] = track(() => a + b);
394
- let &[product] = track(() => sum * 2);
395
- <div>{product}</div>
396
- a = 4;
397
- <div>{product}</div>
398
- </>;
479
+ function NestedDerived() @{
480
+ let &[a] = track(2);
481
+ let &[b] = track(3);
482
+ let &[sum] = track(() => a + b);
483
+ let &[product] = track(() => sum * 2);
484
+ const before = product;
485
+ a = 4;
486
+ const after = product;
487
+ <>
488
+ <div>{before}</div>
489
+ <div>{after}</div>
490
+ </>
399
491
  }
400
492
 
401
493
  const { body } = await render(NestedDerived);
@@ -404,23 +496,31 @@ second"</pre>
404
496
  });
405
497
 
406
498
  it('handles lexical scopes correctly', async () => {
407
- function LexicalScopes() {
408
- return <>
409
- let &[x] = track(1);
499
+ function LexicalScopes() @{
500
+ let &[x] = track(1);
501
+ let nested = '';
502
+ let deep = '';
503
+ let deeper = '';
504
+ {
505
+ let &[x] = track(10);
506
+ nested = String(x);
507
+ }
508
+ {
509
+ let &[x] = track(12);
510
+ deep = String(x);
511
+ {
512
+ let &[x] = track(15);
513
+ deeper = String(x);
514
+ }
515
+ }
516
+ <>
410
517
  <div>{x}</div>
518
+ <div>{nested}</div>
411
519
  <div>
412
- let &[x] = track(10);
413
- {x}
414
- </div>
415
- <div>
416
- let &[x] = track(12);
417
- {x}
418
- <span>
419
- let &[x] = track(15);
420
- {x}
421
- </span>
520
+ {deep}
521
+ <span>{deeper}</span>
422
522
  </div>
423
- </>;
523
+ </>
424
524
  }
425
525
 
426
526
  const { body } = await render(LexicalScopes);
@@ -429,48 +529,48 @@ second"</pre>
429
529
  });
430
530
 
431
531
  it('runs nested JavaScript blocks inside component-local callables', async () => {
432
- function App() {
433
- return <>
434
- function readFunction() {
435
- const label = 'function outer';
436
- let result = '';
532
+ function App() @{
533
+ function readFunction() {
534
+ const label = 'function outer';
535
+ let result = '';
536
+
537
+ {
538
+ const label = 'function inner';
539
+ result = label;
540
+ }
437
541
 
438
- {
439
- const label = 'function inner';
440
- result = label;
441
- }
542
+ return `${result} / ${label}`;
543
+ }
544
+ const readArrow = () => {
545
+ const offset = 5;
546
+ let value = offset;
442
547
 
443
- return `${result} / ${label}`;
548
+ {
549
+ const offset = 17;
550
+ value += offset;
444
551
  }
445
- const readArrow = () => {
446
- const offset = 5;
447
- let value = offset;
552
+
553
+ return value;
554
+ };
555
+ class Reader {
556
+ read() {
557
+ const label = 'method outer';
558
+ let result = '';
448
559
 
449
560
  {
450
- const offset = 17;
451
- value += offset;
561
+ const label = 'method inner';
562
+ result = label;
452
563
  }
453
564
 
454
- return value;
455
- };
456
- class Reader {
457
- read() {
458
- const label = 'method outer';
459
- let result = '';
460
-
461
- {
462
- const label = 'method inner';
463
- result = label;
464
- }
465
-
466
- return `${result} / ${label}`;
467
- }
565
+ return `${result} / ${label}`;
468
566
  }
469
- const reader = new Reader();
567
+ }
568
+ const reader = new Reader();
569
+ <>
470
570
  <div class="block-function">{readFunction()}</div>
471
571
  <div class="block-arrow">{readArrow()}</div>
472
572
  <div class="block-method">{reader.read()}</div>
473
- </>;
573
+ </>
474
574
  }
475
575
 
476
576
  const { body } = await render(App);