ripple 0.3.68 → 0.3.70

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 +126 -259
  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 -131
  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
@@ -2,8 +2,8 @@ import { RippleObject, track } from 'ripple';
2
2
 
3
3
  describe('basic server > attribute rendering', () => {
4
4
  it('render static attributes', async () => {
5
- component Basic() {
6
- <div class="foo" id="bar" style="color: red;">{'Hello World'}</div>
5
+ function Basic() {
6
+ return <><div class="foo" id="bar" style="color: red;">{'Hello World'}</div></>;
7
7
  }
8
8
 
9
9
  const { body } = await render(Basic);
@@ -18,16 +18,16 @@ describe('basic server > attribute rendering', () => {
18
18
  });
19
19
 
20
20
  it('render dynamic class attribute', async () => {
21
- component Basic() {
22
- let &[active] = track(false);
23
-
24
- <div class={active ? 'active' : 'inactive'}>{'Dynamic Class'}</div>
25
-
26
- <style>
27
- .active {
28
- color: green;
29
- }
30
- </style>
21
+ function Basic() {
22
+ return <>
23
+ let &[active] = track(false);
24
+ <div class={active ? 'active' : 'inactive'}>{'Dynamic Class'}</div>
25
+ <style>
26
+ .active {
27
+ color: green;
28
+ }
29
+ </style>
30
+ </>;
31
31
  }
32
32
 
33
33
  const { body } = await render(Basic);
@@ -42,29 +42,30 @@ describe('basic server > attribute rendering', () => {
42
42
  });
43
43
 
44
44
  it('render class attribute with array, nested array, nested object', async () => {
45
- component Basic() {
46
- <div
47
- class={[
48
- 'foo',
49
- 'bar',
50
- true && 'baz',
51
- false && 'aaa',
52
- null && 'bbb',
53
- [
54
- 'ccc',
55
- 'ddd',
56
- { eee: true, fff: false },
57
- ],
58
- ]}
59
- >
60
- {'Class Array'}
61
- </div>
62
-
63
- <style>
64
- .foo {
65
- color: red;
66
- }
67
- </style>
45
+ function Basic() {
46
+ return <>
47
+ <div
48
+ class={[
49
+ 'foo',
50
+ 'bar',
51
+ true && 'baz',
52
+ false && 'aaa',
53
+ null && 'bbb',
54
+ [
55
+ 'ccc',
56
+ 'ddd',
57
+ { eee: true, fff: false },
58
+ ],
59
+ ]}
60
+ >
61
+ {'Class Array'}
62
+ </div>
63
+ <style>
64
+ .foo {
65
+ color: red;
66
+ }
67
+ </style>
68
+ </>;
68
69
  }
69
70
 
70
71
  const { body } = await render(Basic);
@@ -88,32 +89,30 @@ describe('basic server > attribute rendering', () => {
88
89
  });
89
90
 
90
91
  it('applies scoped classes inside tsx blocks and fragment shorthand', async () => {
91
- component App() {
92
- <tsx>
93
- <div class="card">
94
- <h2>
95
- {'tsx block'}
96
- </h2>
97
- </div>
98
- </tsx>
99
-
100
- <>
101
- <div class="card">
102
- <h2>
103
- {'fragment shorthand'}
104
- </h2>
105
- </div>
106
- </>
107
-
108
- <style>
109
- .card {
110
- padding: 1rem;
111
- }
92
+ function App() {
93
+ return <>
94
+ <tsx>
95
+ <div class="card">
96
+ <h2>
97
+ {'tsx block'}
98
+ </h2>
99
+ </div>
100
+ </tsx>
101
+ <>
102
+ <div class="card">
103
+ <h2>{'fragment shorthand'}</h2>
104
+ </div>
105
+ </>
106
+ <style>
107
+ .card {
108
+ padding: 1rem;
109
+ }
112
110
 
113
- h2 {
114
- color: red;
115
- }
116
- </style>
111
+ h2 {
112
+ color: red;
113
+ }
114
+ </style>
115
+ </>;
117
116
  }
118
117
 
119
118
  const { body } = await render(App);
@@ -139,16 +138,16 @@ describe('basic server > attribute rendering', () => {
139
138
  });
140
139
 
141
140
  it('render dynamic class object', async () => {
142
- component Basic() {
143
- let &[active] = track(false);
144
-
145
- <div class={{ active: active, inactive: !active }}>{'Dynamic Class'}</div>
146
-
147
- <style>
148
- .active {
149
- color: green;
150
- }
151
- </style>
141
+ function Basic() {
142
+ return <>
143
+ let &[active] = track(false);
144
+ <div class={{ active: active, inactive: !active }}>{'Dynamic Class'}</div>
145
+ <style>
146
+ .active {
147
+ color: green;
148
+ }
149
+ </style>
150
+ </>;
152
151
  }
153
152
 
154
153
  const { body } = await render(Basic);
@@ -166,21 +165,21 @@ describe('basic server > attribute rendering', () => {
166
165
  it(
167
166
  'applies scoped ripple class to multiple elements with dynamic class expressions',
168
167
  async () => {
169
- component Basic() {
170
- let &[selected] = track(1);
171
-
172
- <div class={selected === 0 ? 'selected' : ''}>{`div 1`}</div>
173
- <div class={selected === 0 ? 'selected' : ''}>{`div 2`}</div>
174
-
175
- <style>
176
- div {
177
- background: green;
178
- color: white;
179
- }
180
- div.selected {
181
- background: indigo;
182
- }
183
- </style>
168
+ function Basic() {
169
+ return <>
170
+ let &[selected] = track(1);
171
+ <div class={selected === 0 ? 'selected' : ''}>{`div 1`}</div>
172
+ <div class={selected === 0 ? 'selected' : ''}>{`div 2`}</div>
173
+ <style>
174
+ div {
175
+ background: green;
176
+ color: white;
177
+ }
178
+ div.selected {
179
+ background: indigo;
180
+ }
181
+ </style>
182
+ </>;
184
183
  }
185
184
 
186
185
  const { body } = await render(Basic);
@@ -197,10 +196,11 @@ describe('basic server > attribute rendering', () => {
197
196
  );
198
197
 
199
198
  it('render dynamic id attribute', async () => {
200
- component Basic() {
201
- let &[count] = track(0);
202
-
203
- <div id={`item-${count}`}>{'Dynamic ID'}</div>
199
+ function Basic() {
200
+ return <>
201
+ let &[count] = track(0);
202
+ <div id={`item-${count}`}>{'Dynamic ID'}</div>
203
+ </>;
204
204
  }
205
205
 
206
206
  const { body } = await render(Basic);
@@ -212,10 +212,11 @@ describe('basic server > attribute rendering', () => {
212
212
  });
213
213
 
214
214
  it('render dynamic style attribute', async () => {
215
- component Basic() {
216
- let &[color] = track('red');
217
-
218
- <div style={`color: ${color}; font-weight: bold;`}>{'Dynamic Style'}</div>
215
+ function Basic() {
216
+ return <>
217
+ let &[color] = track('red');
218
+ <div style={`color: ${color}; font-weight: bold;`}>{'Dynamic Style'}</div>
219
+ </>;
219
220
  }
220
221
 
221
222
  const { body } = await render(Basic);
@@ -228,10 +229,11 @@ describe('basic server > attribute rendering', () => {
228
229
  });
229
230
 
230
231
  it('render style attribute as dynamic object', async () => {
231
- component Basic() {
232
- let &[color] = track('red');
233
-
234
- <div style={{ color: color, fontWeight: 'bold' }}>{'Dynamic Style'}</div>
232
+ function Basic() {
233
+ return <>
234
+ let &[color] = track('red');
235
+ <div style={{ color: color, fontWeight: 'bold' }}>{'Dynamic Style'}</div>
236
+ </>;
235
237
  }
236
238
 
237
239
  const { body } = await render(Basic);
@@ -244,10 +246,11 @@ describe('basic server > attribute rendering', () => {
244
246
  });
245
247
 
246
248
  it('render tracked variable as style attribute', async () => {
247
- component Basic() {
248
- let &[style] = track({ color: 'red', fontWeight: 'bold' });
249
-
250
- <div {style}>{'Dynamic Style'}</div>
249
+ function Basic() {
250
+ return <>
251
+ let &[style] = track({ color: 'red', fontWeight: 'bold' });
252
+ <div {style}>{'Dynamic Style'}</div>
253
+ </>;
251
254
  }
252
255
 
253
256
  const { body } = await render(Basic);
@@ -260,13 +263,14 @@ describe('basic server > attribute rendering', () => {
260
263
  });
261
264
 
262
265
  it('render tracked object as style attribute', async () => {
263
- component Basic() {
264
- let style = new RippleObject({
265
- color: 'red',
266
- fontWeight: 'bold',
267
- });
268
-
269
- <div style={{ color: style.color, fontWeight: style.fontWeight }}>{'Dynamic Style'}</div>
266
+ function Basic() {
267
+ return <>
268
+ let style = new RippleObject({
269
+ color: 'red',
270
+ fontWeight: 'bold',
271
+ });
272
+ <div style={{ color: style.color, fontWeight: style.fontWeight }}>{'Dynamic Style'}</div>
273
+ </>;
270
274
  }
271
275
 
272
276
  const { body } = await render(Basic);
@@ -279,13 +283,14 @@ describe('basic server > attribute rendering', () => {
279
283
  });
280
284
 
281
285
  it('render spread attributes with style and class', async () => {
282
- component Basic() {
283
- const attributes = {
284
- style: { color: 'red', fontWeight: 'bold' },
285
- class: ['foo', false && 'bar'],
286
- };
287
-
288
- <div {...attributes}>{'Attributes with style and class'}</div>
286
+ function Basic() {
287
+ return <>
288
+ const attributes = {
289
+ style: { color: 'red', fontWeight: 'bold' },
290
+ class: ['foo', false && 'bar'],
291
+ };
292
+ <div {...attributes}>{'Attributes with style and class'}</div>
293
+ </>;
289
294
  }
290
295
 
291
296
  const { body } = await render(Basic);
@@ -300,13 +305,64 @@ describe('basic server > attribute rendering', () => {
300
305
  expect(div.classList.contains('bar')).toBe(false);
301
306
  });
302
307
 
303
- it('render spread props without duplication', async () => {
304
- component Basic() {
305
- const checkBoxProp = { name: 'car' };
308
+ it('renders host innerHTML as content instead of an attribute', async () => {
309
+ function Basic() {
310
+ return <>
311
+ const html = '<span>Direct HTML</span>';
312
+ <code innerHTML={html} />
313
+ </>;
314
+ }
315
+
316
+ const { body } = await render(Basic);
317
+ const { document } = parseHtml(body);
318
+ const code = document.querySelector('code');
319
+
320
+ expect(code.innerHTML).toBe('<span>Direct HTML</span>');
321
+ expect(code.hasAttribute('innerhtml')).toBe(false);
322
+ expect(body).toBeHtml('<code><span>Direct HTML</span></code>');
323
+ });
324
+
325
+ it('renders spread innerHTML as content instead of an attribute', async () => {
326
+ function Basic() {
327
+ return <>
328
+ const attributes = { innerHTML: '<span>Spread HTML</span>' };
329
+ <code {...attributes} />
330
+ </>;
331
+ }
306
332
 
307
- <div>
308
- <input {...checkBoxProp} type="checkbox" id="vehicle1" value="Bike" />
309
- </div>
333
+ const { body } = await render(Basic);
334
+ const { document } = parseHtml(body);
335
+ const code = document.querySelector('code');
336
+
337
+ expect(code.innerHTML).toBe('<span>Spread HTML</span>');
338
+ expect(code.hasAttribute('innerhtml')).toBe(false);
339
+ expect(body).toBeHtml('<code><span>Spread HTML</span></code>');
340
+ });
341
+
342
+ it('uses the last innerHTML value when spreads and direct props are mixed', async () => {
343
+ function Basic() {
344
+ return <>
345
+ const attributes = { innerHTML: '<span>Spread HTML</span>' };
346
+ <code {...attributes} innerHTML="<em>Direct HTML</em>" />
347
+ </>;
348
+ }
349
+
350
+ const { body } = await render(Basic);
351
+ const { document } = parseHtml(body);
352
+ const code = document.querySelector('code');
353
+
354
+ expect(code.innerHTML).toBe('<em>Direct HTML</em>');
355
+ expect(code.hasAttribute('innerhtml')).toBe(false);
356
+ });
357
+
358
+ it('render spread props without duplication', async () => {
359
+ function Basic() {
360
+ return <>
361
+ const checkBoxProp = { name: 'car' };
362
+ <div>
363
+ <input {...checkBoxProp} type="checkbox" id="vehicle1" value="Bike" />
364
+ </div>
365
+ </>;
310
366
  }
311
367
 
312
368
  const { body } = await render(Basic);
@@ -330,11 +386,12 @@ describe('basic server > attribute rendering', () => {
330
386
  });
331
387
 
332
388
  it('render dynamic boolean attributes as false', async () => {
333
- component Basic() {
334
- let &[disabled] = track(false);
335
- let &[checked] = track(false);
336
-
337
- <input type="checkbox" {disabled} {checked} />
389
+ function Basic() {
390
+ return <>
391
+ let &[disabled] = track(false);
392
+ let &[checked] = track(false);
393
+ <input type="checkbox" {disabled} {checked} />
394
+ </>;
338
395
  }
339
396
 
340
397
  const { body } = await render(Basic);
@@ -348,10 +405,11 @@ describe('basic server > attribute rendering', () => {
348
405
  });
349
406
 
350
407
  it('render dynamic boolean attributes as false via spread', async () => {
351
- component Basic() {
352
- const spread = { disabled: false, checked: false };
353
-
354
- <input type="checkbox" {...spread} />
408
+ function Basic() {
409
+ return <>
410
+ const spread = { disabled: false, checked: false };
411
+ <input type="checkbox" {...spread} />
412
+ </>;
355
413
  }
356
414
 
357
415
  const { body } = await render(Basic);
@@ -365,11 +423,12 @@ describe('basic server > attribute rendering', () => {
365
423
  });
366
424
 
367
425
  it('render dynamic boolean attributes as true', async () => {
368
- component Basic() {
369
- let &[disabled] = track(true);
370
- let &[checked] = track(true);
371
-
372
- <input type="checkbox" {disabled} {checked} />
426
+ function Basic() {
427
+ return <>
428
+ let &[disabled] = track(true);
429
+ let &[checked] = track(true);
430
+ <input type="checkbox" {disabled} {checked} />
431
+ </>;
373
432
  }
374
433
 
375
434
  const { body } = await render(Basic);
@@ -383,10 +442,11 @@ describe('basic server > attribute rendering', () => {
383
442
  });
384
443
 
385
444
  it('render dynamic boolean attributes as true via spread', async () => {
386
- component Basic() {
387
- const spread = { disabled: true, checked: true };
388
-
389
- <input type="checkbox" {...spread} />
445
+ function Basic() {
446
+ return <>
447
+ const spread = { disabled: true, checked: true };
448
+ <input type="checkbox" {...spread} />
449
+ </>;
390
450
  }
391
451
 
392
452
  const { body } = await render(Basic);
@@ -400,10 +460,11 @@ describe('basic server > attribute rendering', () => {
400
460
  });
401
461
 
402
462
  it('renders formnovalidate as a boolean attribute', async () => {
403
- component Basic() {
404
- let &[formnovalidate] = track(true);
405
-
406
- <button {formnovalidate}>{'Submit'}</button>
463
+ function Basic() {
464
+ return <>
465
+ let &[formnovalidate] = track(true);
466
+ <button {formnovalidate}>{'Submit'}</button>
467
+ </>;
407
468
  }
408
469
 
409
470
  const { body } = await render(Basic);
@@ -417,10 +478,11 @@ describe('basic server > attribute rendering', () => {
417
478
  });
418
479
 
419
480
  it('renders hidden as a boolean attribute when true', async () => {
420
- component Basic() {
421
- let &[hidden] = track(true);
422
-
423
- <div {hidden}>{'Hidden content'}</div>
481
+ function Basic() {
482
+ return <>
483
+ let &[hidden] = track(true);
484
+ <div {hidden}>{'Hidden content'}</div>
485
+ </>;
424
486
  }
425
487
 
426
488
  const { body } = await render(Basic);
@@ -434,10 +496,11 @@ describe('basic server > attribute rendering', () => {
434
496
  });
435
497
 
436
498
  it('does not render hidden when false', async () => {
437
- component Basic() {
438
- let &[hidden] = track(false);
439
-
440
- <div {hidden}>{'Visible content'}</div>
499
+ function Basic() {
500
+ return <>
501
+ let &[hidden] = track(false);
502
+ <div {hidden}>{'Visible content'}</div>
503
+ </>;
441
504
  }
442
505
 
443
506
  const { body } = await render(Basic);
@@ -450,13 +513,14 @@ describe('basic server > attribute rendering', () => {
450
513
  });
451
514
 
452
515
  it('render multiple dynamic attributes', async () => {
453
- component Basic() {
454
- let &[theme] = track('light');
455
- let &[size] = track('medium');
456
-
457
- <div class={`theme-${theme} size-${size}`} data-theme={theme} data-size={size}>
458
- {'Multiple Dynamic Attributes'}
459
- </div>
516
+ function Basic() {
517
+ return <>
518
+ let &[theme] = track('light');
519
+ let &[size] = track('medium');
520
+ <div class={`theme-${theme} size-${size}`} data-theme={theme} data-size={size}>
521
+ {'Multiple Dynamic Attributes'}
522
+ </div>
523
+ </>;
460
524
  }
461
525
 
462
526
  const { body } = await render(Basic);
@@ -470,16 +534,17 @@ describe('basic server > attribute rendering', () => {
470
534
  });
471
535
 
472
536
  it('render conditional attributes', async () => {
473
- component Basic() {
474
- let &[showTitle] = track(false);
475
- let &[showAria] = track(false);
476
-
477
- <div
478
- title={showTitle ? 'This is a title' : undefined}
479
- aria-label={showAria ? 'Accessible label' : undefined}
480
- >
481
- {'Conditional Attributes'}
482
- </div>
537
+ function Basic() {
538
+ return <>
539
+ let &[showTitle] = track(false);
540
+ let &[showAria] = track(false);
541
+ <div
542
+ title={showTitle ? 'This is a title' : undefined}
543
+ aria-label={showAria ? 'Accessible label' : undefined}
544
+ >
545
+ {'Conditional Attributes'}
546
+ </div>
547
+ </>;
483
548
  }
484
549
 
485
550
  const { body } = await render(Basic);
@@ -492,13 +557,14 @@ describe('basic server > attribute rendering', () => {
492
557
  });
493
558
 
494
559
  it('render spread attributes', async () => {
495
- component Basic() {
496
- let &[attrs] = track<TestAttributes>({
497
- class: 'initial',
498
- id: 'test-1',
499
- });
500
-
501
- <div {...attrs}>{'Spread Attributes'}</div>
560
+ function Basic() {
561
+ return <>
562
+ let &[attrs] = track<TestAttributes>({
563
+ class: 'initial',
564
+ id: 'test-1',
565
+ });
566
+ <div {...attrs}>{'Spread Attributes'}</div>
567
+ </>;
502
568
  }
503
569
 
504
570
  const { body } = await render(Basic);
@@ -512,16 +578,16 @@ describe('basic server > attribute rendering', () => {
512
578
  });
513
579
 
514
580
  it('renders with reactive attributes with nested reactive attributes', async () => {
515
- component Basic() {
516
- let &[value] = track('parent-class');
517
-
518
- <p class={value}>{'Colored parent value'}</p>
519
-
520
- <div>
521
- let &[nested] = track('nested-class');
522
-
523
- <p class={nested}>{'Colored nested value'}</p>
524
- </div>
581
+ function Basic() {
582
+ return <>
583
+ let &[value] = track('parent-class');
584
+ <p class={value}>{'Colored parent value'}</p>
585
+ <div>
586
+ let &[nested] = track('nested-class');
587
+
588
+ <p class={nested}>{'Colored nested value'}</p>
589
+ </div>
590
+ </>;
525
591
  }
526
592
 
527
593
  const { body } = await render(Basic);
@@ -534,10 +600,12 @@ describe('basic server > attribute rendering', () => {
534
600
  });
535
601
 
536
602
  it('handles boolean attributes with no prop value provides', async () => {
537
- component Basic() {
538
- <div class="container">
539
- <input type="checkbox" checked />
540
- </div>
603
+ function Basic() {
604
+ return <>
605
+ <div class="container">
606
+ <input type="checkbox" checked />
607
+ </div>
608
+ </>;
541
609
  }
542
610
 
543
611
  const { body } = await render(Basic);
@@ -545,14 +613,15 @@ describe('basic server > attribute rendering', () => {
545
613
  });
546
614
 
547
615
  it('handles boolean props correctly', async () => {
548
- component Basic() {
549
- <div data-disabled />
550
-
551
- <Child isDisabled />
616
+ function Basic() {
617
+ return <>
618
+ <div data-disabled />
619
+ <Child isDisabled />
620
+ </>;
552
621
  }
553
622
 
554
- component Child({ isDisabled }: { isDisabled: boolean }) {
555
- <input disabled={isDisabled} />
623
+ function Child({ isDisabled }: { isDisabled: boolean }) {
624
+ return <><input disabled={isDisabled} /></>;
556
625
  }
557
626
 
558
627
  const { body } = await render(Basic);