ripple 0.3.68 → 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 +48 -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
@@ -10,16 +10,17 @@ import { did_error } from '../capture-error.js';
10
10
 
11
11
  describe('basic client > components & composition', () => {
12
12
  it('renders with component composition and children', () => {
13
- component Card(props: PropsWithChildren<{}>) {
14
- <div class="card">{props.children}</div>
13
+ function Card(props: PropsWithChildren<{}>) {
14
+ return <><div class="card">{props.children}</div></>;
15
15
  }
16
16
 
17
- component Basic() {
18
- component children() {
19
- <p>{'Card content here'}</p>
20
- }
21
-
22
- <Card {children} />
17
+ function Basic() {
18
+ return <>
19
+ function children() {
20
+ return <><p>{'Card content here'}</p></>;
21
+ }
22
+ <Card {children} />
23
+ </>;
23
24
  }
24
25
 
25
26
  render(Basic);
@@ -32,20 +33,23 @@ describe('basic client > components & composition', () => {
32
33
  });
33
34
 
34
35
  it('does not render a falsy component call', () => {
35
- component Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
36
- <div class="card">
37
- if (props.children) {
38
- {props.children}
36
+ function Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
37
+ return <>
38
+ <div class="card">
39
+ if (props.children) {
40
+ {props.children}
41
+ }
42
+ </div>
43
+ </>;
44
+ }
45
+
46
+ function Basic() {
47
+ return <>
48
+ function test() {
49
+ return <><p>{'Card content here'}</p></>;
39
50
  }
40
- </div>
41
- }
42
-
43
- component Basic() {
44
- component test() {
45
- <p>{'Card content here'}</p>
46
- }
47
-
48
- <Card {test} />
51
+ <Card {test} />
52
+ </>;
49
53
  }
50
54
 
51
55
  render(Basic);
@@ -58,23 +62,25 @@ describe('basic client > components & composition', () => {
58
62
  });
59
63
 
60
64
  it('allows tracked variables alongside explicit component props', () => {
61
- component Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
62
- <div class="card">
63
- if (props.children) {
64
- {props.children}
65
+ function Card(props: PropsWithChildrenOptional<{ test?: Component }>) {
66
+ return <>
67
+ <div class="card">
68
+ if (props.children) {
69
+ {props.children}
70
+ }
71
+ </div>
72
+ </>;
73
+ }
74
+
75
+ function Basic() {
76
+ return <>
77
+ let &[test] = track(false);
78
+ function TestSlot() {
79
+ return <><p>{'Card content here'}</p></>;
65
80
  }
66
- </div>
67
- }
68
-
69
- component Basic() {
70
- let &[test] = track(false);
71
-
72
- component TestSlot() {
73
- <p>{'Card content here'}</p>
74
- }
75
-
76
- <Card test={TestSlot} />
77
- <div>{test ? 'yes' : 'no'}</div>
81
+ <Card test={TestSlot} />
82
+ <div>{test ? 'yes' : 'no'}</div>
83
+ </>;
78
84
  }
79
85
 
80
86
  render(Basic);
@@ -88,15 +94,17 @@ describe('basic client > components & composition', () => {
88
94
  });
89
95
 
90
96
  it('renders a component when children is set a component prop', () => {
91
- component Card(props: PropsWithChildren<{}>) {
92
- <div class="card">{props.children}</div>
97
+ function Card(props: PropsWithChildren<{}>) {
98
+ return <><div class="card">{props.children}</div></>;
93
99
  }
94
100
 
95
- component Basic() {
96
- component children() {
97
- <p>{'Card content here'}</p>
98
- }
99
- <Card {children} />
101
+ function Basic() {
102
+ return <>
103
+ function children() {
104
+ return <><p>{'Card content here'}</p></>;
105
+ }
106
+ <Card {children} />
107
+ </>;
100
108
  }
101
109
 
102
110
  render(Basic);
@@ -108,38 +116,115 @@ describe('basic client > components & composition', () => {
108
116
  expect(paragraph.textContent).toBe('Card content here');
109
117
  });
110
118
 
119
+ it('renders direct component function calls as values', () => {
120
+ function Test({ label }: { label: string }) {
121
+ return <><span>{label}</span></>;
122
+ }
123
+
124
+ function App() {
125
+ return <>{Test({ label: 'direct call' })}</>;
126
+ }
127
+
128
+ render(App);
129
+
130
+ expect(container.querySelector('span')?.textContent).toBe('direct call');
131
+ });
132
+
133
+ it('renders arrays and primitives returned from component calls', () => {
134
+ function Test() {
135
+ const item = <span>{'A'}</span>;
136
+ return [item, 'B', null, undefined];
137
+ }
138
+
139
+ function App() {
140
+ return <><Test /></>;
141
+ }
142
+
143
+ render(App);
144
+
145
+ expect(container.textContent).toBe('AB');
146
+ });
147
+
148
+ it('throws when a TSRXElement value is used as a component type', () => {
149
+ function Test() {
150
+ return <><span>{'value'}</span></>;
151
+ }
152
+
153
+ function App() {
154
+ const El = Test({});
155
+ return <><El /></>;
156
+ }
157
+
158
+ expect(() => render(App)).toThrow('Invalid component type');
159
+ });
160
+
161
+ it('allows a plain function as a component type', () => {
162
+ function Func() {
163
+ return 'plain';
164
+ }
165
+
166
+ function App() {
167
+ return <><Func /></>;
168
+ }
169
+
170
+ render(App);
171
+
172
+ expect(container.textContent).toBe('plain');
173
+ });
174
+
175
+ it('allows a compat-only function as a component type', () => {
176
+ function CompatOnly() {
177
+ return <tsx>
178
+ <div>
179
+ {'compat'}
180
+ </div>
181
+ </tsx>;
182
+ }
183
+
184
+ function App() {
185
+ return <><CompatOnly /></>;
186
+ }
187
+
188
+ render(App);
189
+
190
+ expect(container.textContent).toBe('compat');
191
+ });
192
+
111
193
  it('renders with nested components and prop passing', () => {
112
- component Button(props: PropsWithExtras<{
194
+ function Button(props: PropsWithExtras<{
113
195
  variant: string;
114
196
  label: string;
115
197
  onClick: EventListener;
116
198
  }>) {
117
- <button class={props.variant} onClick={props.onClick}>{props.label}</button>
199
+ return <><button class={props.variant} onClick={props.onClick}>{props.label}</button></>;
118
200
  }
119
201
 
120
- component Card(props: PropsWithExtras<{
202
+ function Card(props: PropsWithExtras<{
121
203
  title: string;
122
204
  content: string;
123
205
  buttonText: string;
124
206
  onAction: EventListener;
125
207
  }>) {
126
- <div class="card">
127
- <h3>{props.title}</h3>
128
- <p>{props.content}</p>
129
- <Button variant="primary" label={props.buttonText} onClick={props.onAction} />
130
- </div>
131
- }
132
-
133
- component Basic() {
134
- let &[clicked] = track(false);
135
-
136
- <Card
137
- title="Test Card"
138
- content="This is a test card"
139
- buttonText="Click me"
140
- onAction={() => (clicked = true)}
141
- />
142
- <div class="status">{clicked ? 'Clicked' : 'Not clicked'}</div>
208
+ return <>
209
+ <div class="card">
210
+ <h3>{props.title}</h3>
211
+ <p>{props.content}</p>
212
+ <Button variant="primary" label={props.buttonText} onClick={props.onAction} />
213
+ </div>
214
+ </>;
215
+ }
216
+
217
+ function Basic() {
218
+ return <>
219
+ let &[clicked] = track(false);
220
+ <Card
221
+ title="Test Card"
222
+ content="This is a test card"
223
+ buttonText="Click me"
224
+ onAction={() => (clicked = true)}
225
+ />
226
+ <div class="status">{clicked ? 'Clicked' : 'Not clicked'}</div>
227
+ </>;
143
228
  }
144
229
 
145
230
  render(Basic);
@@ -163,27 +248,30 @@ describe('basic client > components & composition', () => {
163
248
  });
164
249
 
165
250
  it('renders with reactive component props', () => {
166
- component ChildComponent(props: PropsWithExtras<{
251
+ function ChildComponent(props: PropsWithExtras<{
167
252
  text: Tracked<string>;
168
253
  count: Tracked<number>;
169
254
  }>) {
170
- <div class="child-content">{props.text.value}</div>
171
- <div class="child-count">{props.count.value}</div>
172
- }
173
-
174
- component Basic() {
175
- let &[message, messageTracked] = track('Hello');
176
- let &[number, numberTracked] = track(1);
177
-
178
- <ChildComponent text={messageTracked} count={numberTracked} />
179
- <button
180
- onClick={() => {
181
- message = message === 'Hello' ? 'Goodbye' : 'Hello';
182
- number++;
183
- }}
184
- >
185
- {'Update Props'}
186
- </button>
255
+ return <>
256
+ <div class="child-content">{props.text.value}</div>
257
+ <div class="child-count">{props.count.value}</div>
258
+ </>;
259
+ }
260
+
261
+ function Basic() {
262
+ return <>
263
+ let &[message, messageTracked] = track('Hello');
264
+ let &[number, numberTracked] = track(1);
265
+ <ChildComponent text={messageTracked} count={numberTracked} />
266
+ <button
267
+ onClick={() => {
268
+ message = message === 'Hello' ? 'Goodbye' : 'Hello';
269
+ number++;
270
+ }}
271
+ >
272
+ {'Update Props'}
273
+ </button>
274
+ </>;
187
275
  }
188
276
 
189
277
  render(Basic);
@@ -209,15 +297,16 @@ describe('basic client > components & composition', () => {
209
297
  });
210
298
 
211
299
  it('updates explicit text children props reactively', () => {
212
- component TextProp(&{ children }: PropsWithChildren<{}>) {
213
- <div class="text-prop">{children}</div>
300
+ function TextProp(&{ children }: PropsWithChildren<{}>) {
301
+ return <><div class="text-prop">{children}</div></>;
214
302
  }
215
303
 
216
- component Basic() {
217
- let &[show] = track(false);
218
-
219
- <TextProp children={show ? 'hello' : ''} />
220
- <button class="show-text" onClick={() => (show = true)}>{'Show'}</button>
304
+ function Basic() {
305
+ return <>
306
+ let &[show] = track(false);
307
+ <TextProp children={show ? 'hello' : ''} />
308
+ <button class="show-text" onClick={() => (show = true)}>{'Show'}</button>
309
+ </>;
221
310
  }
222
311
 
223
312
  render(Basic);
@@ -231,7 +320,7 @@ describe('basic client > components & composition', () => {
231
320
  });
232
321
 
233
322
  it('it retains this context with bracketed prop functions and keeps original chaining', () => {
234
- component App() {
323
+ function App() {
235
324
  const SYMBOL_PROP = Symbol();
236
325
  let &[hasError] = track(false);
237
326
  const obj: {
@@ -280,16 +369,17 @@ describe('basic client > components & composition', () => {
280
369
  });
281
370
  }
282
371
 
283
- <button onClick={() => obj['increment']()}>{'Increment'}</button>
284
- <button onClick={() => obj[SYMBOL_PROP]()}>{'Increment'}</button>
285
- <button onClick={trigger_nonexistent}>{'Nonexistent'}</button>
286
- <button onClick={trigger_nonexistent_chaining}>{'Nonexistent chaining'}</button>
287
- <button onClick={trigger_object_null}>{'Object null'}</button>
288
- <button onClick={trigger_object_null_chained}>{'Object null chained'}</button>
289
- <button onClick={() => obj.arr[obj.arr.length - 1]()}>{'BinaryExpression prop'}</button>
290
-
291
- <span>{obj.count.value}</span>
292
- <span>{hasError}</span>
372
+ return <>
373
+ <button onClick={() => obj['increment']()}>{'Increment'}</button>
374
+ <button onClick={() => obj[SYMBOL_PROP]()}>{'Increment'}</button>
375
+ <button onClick={trigger_nonexistent}>{'Nonexistent'}</button>
376
+ <button onClick={trigger_nonexistent_chaining}>{'Nonexistent chaining'}</button>
377
+ <button onClick={trigger_object_null}>{'Object null'}</button>
378
+ <button onClick={trigger_object_null_chained}>{'Object null chained'}</button>
379
+ <button onClick={() => obj.arr[obj.arr.length - 1]()}>{'BinaryExpression prop'}</button>
380
+ <span>{obj.count.value}</span>
381
+ <span>{hasError}</span>
382
+ </>;
293
383
  }
294
384
 
295
385
  render(App);
@@ -340,29 +430,36 @@ describe('basic client > components & composition', () => {
340
430
  });
341
431
 
342
432
  it('renders components as named and anonymous properties', () => {
433
+ function Span() {
434
+ return <><span>{'Hello from Span'}</span></>;
435
+ }
436
+
437
+ function Button({ children }: PropsWithChildren<{}>) {
438
+ return <><button>{children}</button></>;
439
+ }
440
+
441
+ function ArrowButton({ children }: PropsWithChildren<{}>) {
442
+ return <><button class="arrow-button">{children}</button></>;
443
+ }
444
+
343
445
  const UI = {
344
- span: component Span() {
345
- <span>{'Hello from Span'}</span>
346
- },
347
- button: component({ children }: PropsWithChildren<{}>) {
348
- <button>{children}</button>
349
- },
350
- arrowButton: component({ children }: PropsWithChildren<{}>) => {
351
- <button class="arrow-button">{children}</button>
352
- },
446
+ span: Span,
447
+ button: Button,
448
+ arrowButton: ArrowButton,
353
449
  };
354
450
 
355
- component App() {
356
- component children() {
357
- <span>{'Click me!'}</span>
358
- }
359
-
360
- <div>
361
- <h1>{'Component as Property Test'}</h1>
362
- <UI.span />
363
- <UI.button {children} />
364
- <UI.arrowButton {children} />
365
- </div>
451
+ function App() {
452
+ return <>
453
+ function children() {
454
+ return <><span>{'Click me!'}</span></>;
455
+ }
456
+ <div>
457
+ <h1>{'Component as Property Test'}</h1>
458
+ <UI.span />
459
+ <UI.button {children} />
460
+ <UI.arrowButton {children} />
461
+ </div>
462
+ </>;
366
463
  }
367
464
 
368
465
  render(App);
@@ -381,14 +478,16 @@ describe('basic client > components & composition', () => {
381
478
  });
382
479
 
383
480
  it('handles empty string children', () => {
384
- component Button({ children }: PropsWithChildren<{}>) {
385
- {children}
481
+ function Button({ children }: PropsWithChildren<{}>) {
482
+ return <>{children}</>;
386
483
  }
387
484
 
388
- component App() {
389
- let content = '';
390
- <Button>{''}</Button>
391
- <Button>{content}</Button>
485
+ function App() {
486
+ return <>
487
+ let content = '';
488
+ <Button>{''}</Button>
489
+ <Button>{content}</Button>
490
+ </>;
392
491
  }
393
492
 
394
493
  expect(() => {
@@ -397,19 +496,20 @@ describe('basic client > components & composition', () => {
397
496
  });
398
497
 
399
498
  it('handles component without any output', () => {
400
- component Noop() {
401
- // No output
499
+ function Noop() {
500
+ return <></>;
402
501
  }
403
502
 
404
- component Op() {
405
- <div>{'Some HTML content'}</div>
503
+ function Op() {
504
+ return <><div>{'Some HTML content'}</div></>;
406
505
  }
407
506
 
408
- component App() {
409
- let &[Content] = track(() => Noop);
410
- <@Content />
411
-
412
- <button onClick={() => (Content = Op)}>{'Show Op'}</button>
507
+ function App() {
508
+ return <>
509
+ let &[Content] = track(() => Noop);
510
+ <@Content />
511
+ <button onClick={() => (Content = Op)}>{'Show Op'}</button>
512
+ </>;
413
513
  }
414
514
 
415
515
  render(App);
@@ -422,12 +522,12 @@ describe('basic client > components & composition', () => {
422
522
  });
423
523
 
424
524
  it('renders explicit children prop without spread', () => {
425
- component Card(props: PropsWithChildren<{}>) {
426
- <div class="card">{props.children}</div>
525
+ function Card(props: PropsWithChildren<{}>) {
526
+ return <><div class="card">{props.children}</div></>;
427
527
  }
428
528
 
429
- component App() {
430
- <Card children="fallback text" />
529
+ function App() {
530
+ return <><Card children="fallback text" /></>;
431
531
  }
432
532
 
433
533
  render(App);
@@ -435,13 +535,15 @@ describe('basic client > components & composition', () => {
435
535
  });
436
536
 
437
537
  it('renders explicit children before spread', () => {
438
- component Card(props: PropsWithChildren<{ id: string }>) {
439
- <div class="card">{props.children}</div>
538
+ function Card(props: PropsWithChildren<{ id: string }>) {
539
+ return <><div class="card">{props.children}</div></>;
440
540
  }
441
541
 
442
- component App() {
443
- const extra = { id: '1' };
444
- <Card children="fallback text" {...extra} />
542
+ function App() {
543
+ return <>
544
+ const extra = { id: '1' };
545
+ <Card children="fallback text" {...extra} />
546
+ </>;
445
547
  }
446
548
 
447
549
  render(App);
@@ -449,13 +551,15 @@ describe('basic client > components & composition', () => {
449
551
  });
450
552
 
451
553
  it('renders spread before explicit children', () => {
452
- component Card(props: PropsWithChildren<{ id: string }>) {
453
- <div class="card">{props.children}</div>
554
+ function Card(props: PropsWithChildren<{ id: string }>) {
555
+ return <><div class="card">{props.children}</div></>;
454
556
  }
455
557
 
456
- component App() {
457
- const extra = { id: '1' };
458
- <Card {...extra} children="fallback text" />
558
+ function App() {
559
+ return <>
560
+ const extra = { id: '1' };
561
+ <Card {...extra} children="fallback text" />
562
+ </>;
459
563
  }
460
564
 
461
565
  render(App);
@@ -463,15 +567,17 @@ describe('basic client > components & composition', () => {
463
567
  });
464
568
 
465
569
  it('template children override explicit children before spread', () => {
466
- component Card(props: PropsWithChildren<{ id: string }>) {
467
- <div class="card">{props.children}</div>
570
+ function Card(props: PropsWithChildren<{ id: string }>) {
571
+ return <><div class="card">{props.children}</div></>;
468
572
  }
469
573
 
470
- component App() {
471
- const extra = { id: '1' };
472
- <Card children="fallback text" {...extra}>
473
- <span>{'template content'}</span>
474
- </Card>
574
+ function App() {
575
+ return <>
576
+ const extra = { id: '1' };
577
+ <Card children="fallback text" {...extra}>
578
+ <span>{'template content'}</span>
579
+ </Card>
580
+ </>;
475
581
  }
476
582
 
477
583
  render(App);
@@ -480,15 +586,17 @@ describe('basic client > components & composition', () => {
480
586
  });
481
587
 
482
588
  it('template children override explicit children after spread', () => {
483
- component Card(props: PropsWithChildren<{ id: string }>) {
484
- <div class="card">{props.children}</div>
589
+ function Card(props: PropsWithChildren<{ id: string }>) {
590
+ return <><div class="card">{props.children}</div></>;
485
591
  }
486
592
 
487
- component App() {
488
- const extra = { id: '1' };
489
- <Card {...extra} children="fallback text">
490
- <span>{'template content'}</span>
491
- </Card>
593
+ function App() {
594
+ return <>
595
+ const extra = { id: '1' };
596
+ <Card {...extra} children="fallback text">
597
+ <span>{'template content'}</span>
598
+ </Card>
599
+ </>;
492
600
  }
493
601
 
494
602
  render(App);
@@ -497,17 +605,19 @@ describe('basic client > components & composition', () => {
497
605
  });
498
606
 
499
607
  it('spread can override explicit children when no template children', () => {
500
- component Card(props: PropsWithChildren<{ id: string }>) {
501
- <div class="card">{props.children}</div>
608
+ function Card(props: PropsWithChildren<{ id: string }>) {
609
+ return <><div class="card">{props.children}</div></>;
502
610
  }
503
611
 
504
- component App() {
505
- const extra = { id: '1', children: 'from spread' };
506
- <Card
507
- // @ts-expect-error children specified more than once
508
- children="explicit"
509
- {...extra}
510
- />
612
+ function App() {
613
+ return <>
614
+ const extra = { id: '1', children: 'from spread' };
615
+ <Card
616
+ // @ts-expect-error children specified more than once
617
+ children="explicit"
618
+ {...extra}
619
+ />
620
+ </>;
511
621
  }
512
622
 
513
623
  render(App);
@@ -515,13 +625,15 @@ describe('basic client > components & composition', () => {
515
625
  });
516
626
 
517
627
  it('explicit children overrides spread children when it comes after', () => {
518
- component Card(props: PropsWithChildren<{ id: string }>) {
519
- <div class="card">{props.children}</div>
628
+ function Card(props: PropsWithChildren<{ id: string }>) {
629
+ return <><div class="card">{props.children}</div></>;
520
630
  }
521
631
 
522
- component App() {
523
- const extra = { id: '1', children: 'from spread' };
524
- <Card {...extra} children="explicit" />
632
+ function App() {
633
+ return <>
634
+ const extra = { id: '1', children: 'from spread' };
635
+ <Card {...extra} children="explicit" />
636
+ </>;
525
637
  }
526
638
 
527
639
  render(App);
@@ -529,18 +641,20 @@ describe('basic client > components & composition', () => {
529
641
  });
530
642
 
531
643
  it('renders components declared inside composite element children', () => {
532
- component Wrapper(props: PropsWithChildren<{}>) {
533
- <div class="wrapper">{props.children}</div>
644
+ function Wrapper(props: PropsWithChildren<{}>) {
645
+ return <><div class="wrapper">{props.children}</div></>;
534
646
  }
535
647
 
536
- component App() {
537
- <Wrapper>
538
- component Inner() {
539
- <span class="inner">{'inner content'}</span>
540
- }
648
+ function App() {
649
+ return <>
650
+ <Wrapper>
651
+ function Inner() {
652
+ return <><span class="inner">{'inner content'}</span></>;
653
+ }
541
654
 
542
- <Inner />
543
- </Wrapper>
655
+ <Inner />
656
+ </Wrapper>
657
+ </>;
544
658
  }
545
659
 
546
660
  render(App);
@@ -548,25 +662,29 @@ describe('basic client > components & composition', () => {
548
662
  });
549
663
 
550
664
  it('renders nested components declared inside composite children with prop passing', () => {
551
- component Wrapper(props: PropsWithChildren<{}>) {
552
- <div class="wrapper">{props.children}</div>
553
- }
554
-
555
- component App() {
556
- <Wrapper>
557
- component Z() {
558
- <div class="z">{'I am Z'}</div>
559
- }
560
-
561
- component Child(&{ Z }: { Z: Component }) {
562
- <div class="child">
563
- {'Child Component: '}
564
- <Z />
565
- </div>
566
- }
567
-
568
- <Child {Z} />
569
- </Wrapper>
665
+ function Wrapper(props: PropsWithChildren<{}>) {
666
+ return <><div class="wrapper">{props.children}</div></>;
667
+ }
668
+
669
+ function App() {
670
+ return <>
671
+ <Wrapper>
672
+ function Z() {
673
+ return <><div class="z">{'I am Z'}</div></>;
674
+ }
675
+
676
+ function Child(&{ Z }: { Z: Component }) {
677
+ return <>
678
+ <div class="child">
679
+ {'Child Component: '}
680
+ <Z />
681
+ </div>
682
+ </>;
683
+ }
684
+
685
+ <Child {Z} />
686
+ </Wrapper>
687
+ </>;
570
688
  }
571
689
 
572
690
  render(App);