ripple 0.3.67 → 0.3.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +2 -2
  4. package/src/runtime/element.js +1 -1
  5. package/src/runtime/index-client.js +11 -11
  6. package/src/runtime/index-server.js +7 -4
  7. package/src/runtime/internal/client/bindings.js +1 -1
  8. package/src/runtime/internal/client/blocks.js +13 -4
  9. package/src/runtime/internal/client/component.js +55 -0
  10. package/src/runtime/internal/client/composite.js +4 -2
  11. package/src/runtime/internal/client/expression.js +65 -7
  12. package/src/runtime/internal/client/hmr.js +54 -43
  13. package/src/runtime/internal/client/index.js +5 -1
  14. package/src/runtime/internal/client/portal.js +70 -69
  15. package/src/runtime/internal/client/render.js +3 -0
  16. package/src/runtime/internal/server/index.js +92 -8
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
  18. package/tests/client/array/array.copy-within.test.tsrx +33 -31
  19. package/tests/client/array/array.derived.test.tsrx +186 -169
  20. package/tests/client/array/array.iteration.test.tsrx +40 -37
  21. package/tests/client/array/array.mutations.test.tsrx +113 -101
  22. package/tests/client/array/array.static.test.tsrx +119 -101
  23. package/tests/client/array/array.to-methods.test.tsrx +24 -21
  24. package/tests/client/async-suspend.test.tsrx +247 -246
  25. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
  26. package/tests/client/basic/basic.attributes.test.tsrx +428 -423
  27. package/tests/client/basic/basic.collections.test.tsrx +109 -102
  28. package/tests/client/basic/basic.components.test.tsrx +323 -205
  29. package/tests/client/basic/basic.errors.test.tsrx +91 -91
  30. package/tests/client/basic/basic.events.test.tsrx +114 -115
  31. package/tests/client/basic/basic.get-set.test.tsrx +97 -87
  32. package/tests/client/basic/basic.hmr.test.tsrx +19 -16
  33. package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
  34. package/tests/client/basic/basic.rendering.test.tsrx +272 -182
  35. package/tests/client/basic/basic.styling.test.tsrx +23 -22
  36. package/tests/client/basic/basic.utilities.test.tsrx +10 -8
  37. package/tests/client/boundaries.test.tsrx +26 -26
  38. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
  41. package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
  42. package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
  43. package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
  44. package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
  45. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  46. package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
  47. package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
  48. package/tests/client/composite/composite.generics.test.tsrx +168 -192
  49. package/tests/client/composite/composite.props.test.tsrx +97 -81
  50. package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
  51. package/tests/client/composite/composite.render.test.tsrx +122 -105
  52. package/tests/client/computed-properties.test.tsrx +28 -28
  53. package/tests/client/context.test.tsrx +21 -21
  54. package/tests/client/css/global-additional-cases.test.tsrx +58 -58
  55. package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
  56. package/tests/client/css/global-at-rules.test.tsrx +10 -10
  57. package/tests/client/css/global-basic.test.tsrx +14 -14
  58. package/tests/client/css/global-classes-ids.test.tsrx +14 -14
  59. package/tests/client/css/global-combinators.test.tsrx +10 -10
  60. package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
  61. package/tests/client/css/global-edge-cases.test.tsrx +18 -18
  62. package/tests/client/css/global-keyframes.test.tsrx +12 -12
  63. package/tests/client/css/global-nested.test.tsrx +10 -10
  64. package/tests/client/css/global-pseudo.test.tsrx +12 -12
  65. package/tests/client/css/global-scoping.test.tsrx +20 -20
  66. package/tests/client/css/style-identifier.test.tsrx +143 -291
  67. package/tests/client/date.test.tsrx +146 -133
  68. package/tests/client/dynamic-elements.test.tsrx +398 -365
  69. package/tests/client/events.test.tsrx +292 -290
  70. package/tests/client/for.test.tsrx +156 -153
  71. package/tests/client/head.test.tsrx +105 -96
  72. package/tests/client/html.test.tsrx +122 -26
  73. package/tests/client/input-value.test.tsrx +1361 -1314
  74. package/tests/client/lazy-array.test.tsrx +16 -13
  75. package/tests/client/lazy-destructuring.test.tsrx +257 -213
  76. package/tests/client/map.test.tsrx +65 -60
  77. package/tests/client/media-query.test.tsrx +22 -20
  78. package/tests/client/object.test.tsrx +87 -81
  79. package/tests/client/portal.test.tsrx +57 -51
  80. package/tests/client/ref.test.tsrx +233 -202
  81. package/tests/client/return.test.tsrx +71 -2560
  82. package/tests/client/set.test.tsrx +54 -45
  83. package/tests/client/svg.test.tsrx +216 -186
  84. package/tests/client/switch.test.tsrx +194 -193
  85. package/tests/client/track-async-hydration.test.tsrx +18 -14
  86. package/tests/client/tracked-index-access.test.tsrx +28 -18
  87. package/tests/client/try.test.tsrx +675 -548
  88. package/tests/client/tsx.test.tsrx +373 -311
  89. package/tests/client/typescript-generics.test.tsrx +145 -145
  90. package/tests/client/url/url.derived.test.tsrx +33 -28
  91. package/tests/client/url/url.parsing.test.tsrx +61 -51
  92. package/tests/client/url/url.partial-removal.test.tsrx +56 -48
  93. package/tests/client/url/url.reactivity.test.tsrx +142 -125
  94. package/tests/client/url/url.serialization.test.tsrx +13 -11
  95. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
  96. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
  97. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
  98. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
  99. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
  100. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
  101. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
  102. package/tests/hydration/basic.test.js +3 -3
  103. package/tests/hydration/compiled/client/basic.js +586 -651
  104. package/tests/hydration/compiled/client/composite.js +79 -104
  105. package/tests/hydration/compiled/client/events.js +140 -148
  106. package/tests/hydration/compiled/client/for.js +1005 -1018
  107. package/tests/hydration/compiled/client/head.js +124 -134
  108. package/tests/hydration/compiled/client/hmr.js +41 -48
  109. package/tests/hydration/compiled/client/html-in-template.js +38 -41
  110. package/tests/hydration/compiled/client/html.js +970 -1314
  111. package/tests/hydration/compiled/client/if-children.js +234 -249
  112. package/tests/hydration/compiled/client/if.js +182 -189
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
  115. package/tests/hydration/compiled/client/portal.js +65 -85
  116. package/tests/hydration/compiled/client/reactivity.js +84 -90
  117. package/tests/hydration/compiled/client/return.js +38 -1939
  118. package/tests/hydration/compiled/client/switch.js +218 -224
  119. package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
  120. package/tests/hydration/compiled/client/try.js +123 -132
  121. package/tests/hydration/compiled/server/basic.js +773 -831
  122. package/tests/hydration/compiled/server/composite.js +166 -191
  123. package/tests/hydration/compiled/server/events.js +170 -184
  124. package/tests/hydration/compiled/server/for.js +851 -909
  125. package/tests/hydration/compiled/server/head.js +206 -216
  126. package/tests/hydration/compiled/server/hmr.js +64 -72
  127. package/tests/hydration/compiled/server/html-in-template.js +42 -76
  128. package/tests/hydration/compiled/server/html.js +1362 -1667
  129. package/tests/hydration/compiled/server/if-children.js +419 -445
  130. package/tests/hydration/compiled/server/if.js +194 -208
  131. package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
  132. package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
  133. package/tests/hydration/compiled/server/portal.js +152 -160
  134. package/tests/hydration/compiled/server/reactivity.js +94 -106
  135. package/tests/hydration/compiled/server/return.js +28 -2172
  136. package/tests/hydration/compiled/server/switch.js +274 -286
  137. package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
  138. package/tests/hydration/compiled/server/try.js +167 -185
  139. package/tests/hydration/components/basic.tsrx +320 -272
  140. package/tests/hydration/components/composite.tsrx +44 -32
  141. package/tests/hydration/components/events.tsrx +101 -91
  142. package/tests/hydration/components/for.tsrx +510 -452
  143. package/tests/hydration/components/head.tsrx +87 -80
  144. package/tests/hydration/components/hmr.tsrx +22 -17
  145. package/tests/hydration/components/html-in-template.tsrx +22 -17
  146. package/tests/hydration/components/html.tsrx +525 -443
  147. package/tests/hydration/components/if-children.tsrx +158 -148
  148. package/tests/hydration/components/if.tsrx +109 -95
  149. package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
  150. package/tests/hydration/components/nested-control-flow.tsrx +215 -203
  151. package/tests/hydration/components/portal.tsrx +41 -34
  152. package/tests/hydration/components/reactivity.tsrx +37 -27
  153. package/tests/hydration/components/return.tsrx +12 -556
  154. package/tests/hydration/components/switch.tsrx +120 -114
  155. package/tests/hydration/components/track-async-serialization.tsrx +107 -91
  156. package/tests/hydration/components/try.tsrx +55 -40
  157. package/tests/hydration/html.test.js +4 -4
  158. package/tests/hydration/return.test.js +13 -532
  159. package/tests/server/await.test.tsrx +3 -3
  160. package/tests/server/basic.attributes.test.tsrx +264 -195
  161. package/tests/server/basic.components.test.tsrx +296 -169
  162. package/tests/server/basic.test.tsrx +300 -198
  163. package/tests/server/compiler.test.tsrx +62 -60
  164. package/tests/server/composite.props.test.tsrx +77 -63
  165. package/tests/server/composite.test.tsrx +168 -192
  166. package/tests/server/context.test.tsrx +18 -12
  167. package/tests/server/dynamic-elements.test.tsrx +197 -180
  168. package/tests/server/for.test.tsrx +85 -78
  169. package/tests/server/head.test.tsrx +50 -43
  170. package/tests/server/html-nesting-validation.test.tsrx +8 -8
  171. package/tests/server/if.test.tsrx +57 -51
  172. package/tests/server/lazy-destructuring.test.tsrx +366 -294
  173. package/tests/server/return.test.tsrx +76 -1355
  174. package/tests/server/streaming-ssr.test.tsrx +4 -75
  175. package/tests/server/style-identifier.test.tsrx +169 -148
  176. package/tests/server/switch.test.tsrx +91 -85
  177. package/tests/server/track-async-serialization.test.tsrx +105 -85
  178. package/tests/server/try.test.tsrx +374 -280
  179. package/tests/utils/compiler-compat-config.test.js +2 -2
  180. package/tests/utils/runtime-imports.test.js +10 -0
  181. package/types/index.d.ts +8 -0
  182. package/tests/client/__snapshots__/html.test.rsrx.snap +0 -40
@@ -1,1376 +1,97 @@
1
- describe('early return in SSR', () => {
2
- it('skips template content after direct return', async () => {
3
- component App() {
4
- <div>{'before'}</div>
5
- return;
6
- <div>{'after'}</div>
7
- }
8
-
9
- const { body } = await render(App);
10
- expect(body).toBeHtml('<div>before</div>');
11
- });
12
-
13
- it('skips rest of body when return condition is true', async () => {
14
- component App() {
15
- let condition = true;
16
-
17
- if (condition) {
18
- <div>{'guard hit'}</div>
19
- return;
20
- }
21
- <div>{'rest'}</div>
22
- }
23
-
24
- const { body } = await render(App);
25
- expect(body).toBeHtml('<div>guard hit</div>');
26
- });
27
-
28
- it('renders rest of body when return condition is false', async () => {
29
- component App() {
30
- let condition = false;
31
-
32
- if (condition) {
33
- <div>{'guard hit'}</div>
34
- return;
35
- }
36
- <div>{'rest'}</div>
37
- }
38
-
39
- const { body } = await render(App);
40
- expect(body).toBeHtml('<div>rest</div>');
41
- });
42
-
43
- it('emits hydration markers for content guarded by early return flags', async () => {
44
- component App() {
45
- let condition = true;
46
-
47
- if (condition) {
48
- return;
49
- }
50
-
51
- <div>{'rest'}</div>
52
- }
53
-
54
- const { body } = await render(App);
55
- const open_markers = body.match(/<!--\[-->/g) || [];
56
- const close_markers = body.match(/<!--\]-->/g) || [];
57
- expect(open_markers.length).toBeGreaterThanOrEqual(3);
58
- expect(close_markers.length).toBeGreaterThanOrEqual(3);
59
- expect(open_markers.length).toBe(close_markers.length);
60
- expect(body).toBeHtml('');
61
- });
62
-
63
- it('handles nested ifs with return', async () => {
64
- component App() {
65
- let a = true;
66
- let b = true;
67
-
68
- if (a) {
69
- <div>{'a is true'}</div>
70
- if (b) {
71
- <div>{'b is true'}</div>
72
- return;
73
- }
74
- }
75
- <div>{'rest renders only when !(a && b)'}</div>
76
- }
77
-
78
- const { body } = await render(App);
79
- expect(body).toBeHtml('<div>a is true</div><div>b is true</div>');
80
- });
81
-
82
- it('renders rest when nested return condition is not fully met', async () => {
83
- component App() {
84
- let a = true;
85
- let b = false;
86
-
87
- if (a) {
88
- <div>{'a is true'}</div>
89
- if (b) {
90
- <div>{'b is true'}</div>
91
- return;
92
- }
93
- }
94
- <div>{'rest'}</div>
95
- }
96
-
97
- const { body } = await render(App);
98
- expect(body).toBeHtml('<div>a is true</div><div>rest</div>');
99
- });
100
-
101
- it('renders rest when outer condition is false', async () => {
102
- component App() {
103
- let a = false;
104
- let b = true;
105
-
106
- if (a) {
107
- <div>{'a is true'}</div>
108
- if (b) {
109
- <div>{'b is true'}</div>
110
- return;
111
- }
112
- }
113
- <div>{'rest'}</div>
114
- }
115
-
116
- const { body } = await render(App);
117
- expect(body).toBeHtml('<div>rest</div>');
118
- });
119
-
120
- it('handles content before and after the if-with-return', async () => {
121
- component App() {
122
- let shouldReturn = true;
123
-
124
- <div>{'before'}</div>
125
- if (shouldReturn) {
126
- <div>{'guard'}</div>
127
- return;
128
- }
129
- <div>{'after'}</div>
130
- }
131
-
132
- const { body } = await render(App);
133
- expect(body).toBeHtml('<div>before</div><div>guard</div>');
134
- });
135
-
136
- it('renders multiple elements after guard when condition is false', async () => {
137
- component App() {
138
- let shouldReturn = false;
139
-
140
- if (shouldReturn) {
141
- <div>{'guard'}</div>
142
- return;
143
- }
144
- <div>{'first'}</div>
145
- <div>{'second'}</div>
146
- }
147
-
148
- const { body } = await render(App);
149
- expect(body).toBeHtml('<div>first</div><div>second</div>');
150
- });
151
-
152
- it('handles multiple sequential returns - first hits', async () => {
153
- component App() {
154
- let a = true;
155
- let b = true;
156
-
157
- if (a) {
158
- <div>{'first guard'}</div>
159
- return;
160
- }
161
- if (b) {
162
- <div>{'second guard'}</div>
163
- return;
164
- }
165
- <div>{'rest'}</div>
166
- }
167
-
168
- const { body } = await render(App);
169
- expect(body).toBeHtml('<div>first guard</div>');
170
- });
171
-
172
- it('handles multiple sequential returns - second hits', async () => {
173
- component App() {
174
- let a = false;
175
- let b = true;
176
-
177
- if (a) {
178
- <div>{'first guard'}</div>
179
- return;
180
- }
181
- if (b) {
182
- <div>{'second guard'}</div>
183
- return;
184
- }
185
- <div>{'rest'}</div>
186
- }
187
-
188
- const { body } = await render(App);
189
- expect(body).toBeHtml('<div>second guard</div>');
190
- });
191
-
192
- it('handles multiple sequential returns - none hit', async () => {
193
- component App() {
194
- let a = false;
195
- let b = false;
196
-
197
- if (a) {
198
- <div>{'first guard'}</div>
199
- return;
200
- }
201
- if (b) {
202
- <div>{'second guard'}</div>
203
- return;
204
- }
205
- <div>{'rest'}</div>
206
- }
207
-
208
- const { body } = await render(App);
209
- expect(body).toBeHtml('<div>rest</div>');
210
- });
211
-
212
- it('handles deeply nested returns (3 levels)', async () => {
213
- component App() {
214
- let a = true;
215
- let b = true;
216
- let c = true;
217
-
218
- if (a) {
219
- <div>{'a'}</div>
220
- if (b) {
221
- <div>{'b'}</div>
222
- if (c) {
223
- <div>{'c'}</div>
224
- return;
225
- }
226
- }
227
- }
228
- <div>{'rest'}</div>
229
- }
230
-
231
- const { body } = await render(App);
232
- expect(body).toBeHtml('<div>a</div><div>b</div><div>c</div>');
233
- });
234
-
235
- it('handles deeply nested returns (3 levels) - partial', async () => {
236
- component App() {
237
- let a = true;
238
- let b = true;
239
- let c = false;
240
-
241
- if (a) {
242
- <div>{'a'}</div>
243
- if (b) {
244
- <div>{'b'}</div>
245
- if (c) {
246
- <div>{'c'}</div>
247
- return;
248
- }
249
- }
250
- }
251
- <div>{'rest'}</div>
252
- }
253
-
254
- const { body } = await render(App);
255
- expect(body).toBeHtml('<div>a</div><div>b</div><div>rest</div>');
256
- });
257
-
258
- it('handles return with else-if chain - first condition', async () => {
259
- component App() {
260
- let value = 1;
261
-
262
- if (value === 1) {
263
- <div>{'one'}</div>
264
- return;
265
- } else if (value === 2) {
266
- <div>{'two'}</div>
267
- return;
268
- } else {
269
- <div>{'other'}</div>
270
- return;
271
- }
272
- <div>{'never reached'}</div>
273
- }
274
-
275
- const { body } = await render(App);
276
- expect(body).toBeHtml('<div>one</div>');
277
- });
278
-
279
- it('handles return with else-if chain - second condition', async () => {
280
- component App() {
281
- let value = 2;
282
-
283
- if (value === 1) {
284
- <div>{'one'}</div>
285
- return;
286
- } else if (value === 2) {
287
- <div>{'two'}</div>
288
- return;
289
- } else {
290
- <div>{'other'}</div>
291
- return;
292
- }
293
- <div>{'never reached'}</div>
294
- }
295
-
296
- const { body } = await render(App);
297
- expect(body).toBeHtml('<div>two</div>');
298
- });
299
-
300
- it('handles return with else-if chain - else condition', async () => {
301
- component App() {
302
- let value = 3;
303
-
304
- if (value === 1) {
305
- <div>{'one'}</div>
306
- return;
307
- } else if (value === 2) {
308
- <div>{'two'}</div>
309
- return;
310
- } else {
311
- <div>{'other'}</div>
312
- return;
313
- }
314
- <div>{'never reached'}</div>
315
- }
316
-
317
- const { body } = await render(App);
318
- expect(body).toBeHtml('<div>other</div>');
319
- });
320
-
321
- it('handles return with complex boolean expression - AND', async () => {
322
- component App() {
323
- let a = true;
324
- let b = true;
325
- let c = true;
326
-
327
- if (a && b && c) {
328
- <div>{'all true'}</div>
329
- return;
330
- }
331
- <div>{'not all true'}</div>
332
- }
333
-
334
- const { body } = await render(App);
335
- expect(body).toBeHtml('<div>all true</div>');
336
- });
337
-
338
- it('handles return with complex boolean expression - OR', async () => {
339
- component App() {
340
- let a = false;
341
- let b = true;
342
- let c = false;
343
-
344
- if (a || b || c) {
345
- <div>{'at least one true'}</div>
346
- return;
347
- }
348
- <div>{'all false'}</div>
349
- }
350
-
351
- const { body } = await render(App);
352
- expect(body).toBeHtml('<div>at least one true</div>');
353
- });
354
-
355
- it('handles return with complex boolean expression - mixed', async () => {
356
- component App() {
357
- let a = true;
358
- let b = false;
359
- let c = true;
360
-
361
- if (a && !b || c) {
362
- <div>{'complex condition met'}</div>
363
- return;
364
- }
365
- <div>{'complex condition not met'}</div>
366
- }
367
-
368
- const { body } = await render(App);
369
- expect(body).toBeHtml('<div>complex condition met</div>');
370
- });
371
-
372
- it('handles return with numeric comparison', async () => {
373
- component App() {
374
- let num = 10;
375
-
376
- if (num > 5) {
377
- <div>{'greater than 5'}</div>
378
- return;
379
- }
380
- <div>{'5 or less'}</div>
381
- }
382
-
383
- const { body } = await render(App);
384
- expect(body).toBeHtml('<div>greater than 5</div>');
385
- });
386
-
387
- it('handles return with string comparison', async () => {
388
- component App() {
389
- let str = 'hello';
390
-
391
- if (str === 'hello') {
392
- <div>{'greeting'}</div>
393
- return;
394
- }
395
- <div>{'not greeting'}</div>
396
- }
397
-
398
- const { body } = await render(App);
399
- expect(body).toBeHtml('<div>greeting</div>');
400
- });
401
-
402
- it('handles return with negated condition', async () => {
403
- component App() {
404
- let flag = false;
405
-
406
- if (!flag) {
407
- <div>{'flag is false'}</div>
408
- return;
409
- }
410
- <div>{'flag is true'}</div>
411
- }
412
-
413
- const { body } = await render(App);
414
- expect(body).toBeHtml('<div>flag is false</div>');
415
- });
416
-
417
- it('handles return with ternary result', async () => {
418
- component App() {
419
- let condition = true;
420
-
421
- if (condition) {
422
- <div>{condition ? 'yes' : 'no'}</div>
423
- return;
424
- }
425
- <div>{'fallback'}</div>
426
- }
427
-
428
- const { body } = await render(App);
429
- expect(body).toBeHtml('<div>yes</div>');
430
- });
431
-
432
- it('handles return in nested component scope', async () => {
433
- component App() {
434
- let show = true;
435
-
436
- <div>
437
- <span>{'outer'}</span>
438
- if (show) {
439
- <p>{'inner'}</p>
440
- return;
441
- }
442
- <p>{'after'}</p>
443
- </div>
444
- }
445
-
446
- const { body } = await render(App);
447
- expect(body).toBeHtml('<div><span>outer</span><p>inner</p></div>');
448
- });
449
-
450
- it('handles return with multiple elements before and after', async () => {
451
- component App() {
452
- let shouldReturn = true;
453
-
454
- <h1>{'title'}</h1>
455
- <p>{'description'}</p>
456
- if (shouldReturn) {
457
- <div>{'guard'}</div>
458
- <span>{'guard span'}</span>
459
- return;
460
- }
461
- <footer>{'footer'}</footer>
462
- <nav>{'nav'}</nav>
463
- }
464
-
465
- const { body } = await render(App);
466
- expect(body).toBeHtml(
467
- '<h1>title</h1><p>description</p><div>guard</div><span>guard span</span>',
468
- );
469
- });
470
-
471
- it('handles return at the beginning of component', async () => {
472
- component App() {
473
- if (true) {
474
- <div>{'early exit'}</div>
475
- return;
476
- }
477
- <div>{'never reached'}</div>
478
- <div>{'also never reached'}</div>
479
- }
480
-
481
- const { body } = await render(App);
482
- expect(body).toBeHtml('<div>early exit</div>');
483
- });
484
-
485
- it('handles return at the end of component', async () => {
486
- component App() {
487
- <div>{'first'}</div>
488
- <div>{'second'}</div>
489
- if (true) {
490
- <div>{'third'}</div>
491
- return;
492
- }
493
- }
494
-
495
- const { body } = await render(App);
496
- expect(body).toBeHtml('<div>first</div><div>second</div><div>third</div>');
497
- });
498
-
499
- it('handles return with empty elements before (self-closing div)', async () => {
500
- component App() {
501
- <div />
502
- if (true) {
503
- <span>{'content'}</span>
504
- return;
505
- }
506
- <p>{'after'}</p>
507
- }
508
-
509
- const { body } = await render(App);
510
- expect(body).toBeHtml('<div></div><span>content</span>');
511
- });
512
-
513
- it('handles return with function call in condition', async () => {
514
- component App() {
515
- function check() {
516
- return true;
517
- }
518
-
519
- if (check()) {
520
- <div>{'function returned true'}</div>
521
- return;
522
- }
523
- <div>{'function returned false'}</div>
524
- }
525
-
526
- const { body } = await render(App);
527
- expect(body).toBeHtml('<div>function returned true</div>');
528
- });
529
-
530
- it('handles return with arithmetic in condition (corrected: 5+3=8 > 7)', async () => {
531
- component App() {
532
- let x = 5;
533
- let y = 3;
534
-
535
- if (x + y > 7) {
536
- <div>{'sum greater than 7'}</div>
537
- return;
538
- }
539
- <div>{'sum 7 or less'}</div>
540
- }
541
-
542
- const { body } = await render(App);
543
- expect(body).toBeHtml('<div>sum greater than 7</div>');
544
- });
545
-
546
- it('handles multiple sibling returns at same level', async () => {
547
- component App() {
548
- let mode = 'b';
549
-
550
- if (mode === 'a') {
551
- <div>{'mode A'}</div>
552
- return;
553
- }
554
-
555
- if (mode === 'b') {
556
- <div>{'mode B'}</div>
557
- return;
558
- }
559
-
560
- if (mode === 'c') {
561
- <div>{'mode C'}</div>
562
- return;
563
- }
564
-
565
- <div>{'default mode'}</div>
566
- }
567
-
568
- const { body } = await render(App);
569
- expect(body).toBeHtml('<div>mode B</div>');
570
- });
571
-
572
- it('handles return with array length check', async () => {
573
- component App() {
574
- let items = [1, 2, 3];
575
-
576
- if (items.length > 0) {
577
- <div>{'has items'}</div>
578
- return;
579
- }
580
- <div>{'empty'}</div>
581
- }
582
-
583
- const { body } = await render(App);
584
- expect(body).toBeHtml('<div>has items</div>');
585
- });
586
-
587
- it('handles return with object property check', async () => {
588
- component App() {
589
- let obj = { value: 42 };
590
-
591
- if (obj.value === 42) {
592
- <div>{'correct value'}</div>
593
- return;
594
- }
595
- <div>{'wrong value'}</div>
596
- }
597
-
598
- const { body } = await render(App);
599
- expect(body).toBeHtml('<div>correct value</div>');
600
- });
601
-
602
- it('handles return with typeof check', async () => {
603
- component App() {
604
- let value = 'string';
605
-
606
- if (typeof value === 'string') {
607
- <div>{'is string'}</div>
608
- return;
609
- }
610
- <div>{'not string'}</div>
611
- }
612
-
613
- const { body } = await render(App);
614
- expect(body).toBeHtml('<div>is string</div>');
615
- });
616
-
617
- it('handles return with null check', async () => {
618
- component App() {
619
- let value = null;
620
-
621
- if (value === null) {
622
- <div>{'is null'}</div>
623
- return;
624
- }
625
- <div>{'not null'}</div>
626
- }
627
-
628
- const { body } = await render(App);
629
- expect(body).toBeHtml('<div>is null</div>');
630
- });
631
-
632
- it('handles return with undefined check', async () => {
633
- component App() {
634
- let value = undefined;
635
-
636
- if (value === undefined) {
637
- <div>{'is undefined'}</div>
638
- return;
639
- }
640
- <div>{'not undefined'}</div>
641
- }
642
-
643
- const { body } = await render(App);
644
- expect(body).toBeHtml('<div>is undefined</div>');
645
- });
646
-
647
- it('handles return with truthy/falsy values', async () => {
648
- component App() {
649
- let value = 0;
650
-
651
- if (value) {
652
- <div>{'truthy'}</div>
653
- return;
654
- }
655
- <div>{'falsy'}</div>
656
- }
657
-
658
- const { body } = await render(App);
659
- expect(body).toBeHtml('<div>falsy</div>');
660
- });
661
-
662
- it('handles return with empty string check', async () => {
663
- component App() {
664
- let str = '';
665
-
666
- if (str === '') {
667
- <div>{'empty string'}</div>
668
- return;
669
- }
670
- <div>{'non-empty string'}</div>
671
- }
672
-
673
- const { body } = await render(App);
674
- expect(body).toBeHtml('<div>empty string</div>');
675
- });
676
-
677
- it('handles return with instance check', async () => {
678
- component App() {
679
- let arr = [1, 2, 3];
680
-
681
- if (arr instanceof Array) {
682
- <div>{'is array'}</div>
683
- return;
684
- }
685
- <div>{'not array'}</div>
686
- }
687
-
688
- const { body } = await render(App);
689
- expect(body).toBeHtml('<div>is array</div>');
690
- });
691
-
692
- it('handles return with in operator', async () => {
693
- component App() {
694
- let obj = { a: 1, b: 2 };
695
-
696
- if ('a' in obj) {
697
- <div>{'has property a'}</div>
698
- return;
699
- }
700
- <div>{'no property a'}</div>
701
- }
702
-
703
- const { body } = await render(App);
704
- expect(body).toBeHtml('<div>has property a</div>');
705
- });
706
-
707
- it('handles return with switch-like pattern using else-if', async () => {
708
- component App() {
709
- let status = 'loading';
710
-
711
- if (status === 'idle') {
712
- <div>{'idle state'}</div>
713
- return;
714
- } else if (status === 'loading') {
715
- <div>{'loading state'}</div>
716
- return;
717
- } else if (status === 'success') {
718
- <div>{'success state'}</div>
719
- return;
720
- } else if (status === 'error') {
721
- <div>{'error state'}</div>
722
- return;
723
- } else {
724
- <div>{'unknown state'}</div>
725
- return;
726
- }
727
- <div>{'never reached'}</div>
728
- }
729
-
730
- const { body } = await render(App);
731
- expect(body).toBeHtml('<div>loading state</div>');
732
- });
733
-
734
- it('handles return with multiple nested levels and mixed conditions', async () => {
735
- component App() {
736
- let a = true;
737
- let b = false;
738
- let c = true;
739
- let d = true;
740
-
741
- if (a) {
742
- <div>{'a'}</div>
743
- if (b) {
744
- <div>{'b'}</div>
745
- return;
746
- }
747
- if (c) {
748
- <div>{'c'}</div>
749
- if (d) {
750
- <div>{'d'}</div>
751
- return;
752
- }
753
- }
754
- }
755
- <div>{'rest'}</div>
756
- }
757
-
758
- const { body } = await render(App);
759
- expect(body).toBeHtml('<div>a</div><div>c</div><div>d</div>');
760
- });
761
-
762
- it('handles return with conditional rendering before guard', async () => {
763
- component App() {
764
- let showHeader = true;
765
- let shouldReturn = true;
766
-
767
- if (showHeader) {
768
- <h1>{'Header'}</h1>
769
- }
770
- if (shouldReturn) {
771
- <div>{'guard'}</div>
772
- return;
773
- }
774
- <footer>{'Footer'}</footer>
775
- }
776
-
777
- const { body } = await render(App);
778
- expect(body).toBeHtml('<h1>Header</h1><div>guard</div>');
779
- });
780
-
781
- it('handles return with else branch that does not return', async () => {
782
- component App() {
783
- let condition = false;
784
-
785
- if (condition) {
786
- <div>{'condition true'}</div>
787
- return;
788
- } else {
789
- <div>{'condition false'}</div>
790
- }
791
- <div>{'after if-else'}</div>
792
- }
793
-
794
- const { body } = await render(App);
795
- expect(body).toBeHtml('<div>condition false</div><div>after if-else</div>');
796
- });
797
-
798
- it('handles return with else branch that also returns', async () => {
799
- component App() {
800
- let condition = false;
801
-
802
- if (condition) {
803
- <div>{'condition true'}</div>
804
- return;
805
- } else {
806
- <div>{'condition false'}</div>
807
- return;
808
- }
809
- <div>{'never reached'}</div>
810
- }
811
-
812
- const { body } = await render(App);
813
- expect(body).toBeHtml('<div>condition false</div>');
814
- });
815
-
816
- it('handles return with only if branch returning', async () => {
817
- component App() {
818
- let condition = false;
819
-
820
- if (condition) {
821
- <div>{'condition true'}</div>
822
- return;
823
- }
824
- <div>{'condition false or after'}</div>
825
- }
826
-
827
- const { body } = await render(App);
828
- expect(body).toBeHtml('<div>condition false or after</div>');
829
- });
830
-
831
- it('handles return with deeply nested else-if chain', async () => {
832
- component App() {
833
- let x = 1;
834
- let y = 2;
835
-
836
- if (x === 1) {
837
- if (y === 1) {
838
- <div>{'x=1, y=1'}</div>
839
- return;
840
- } else if (y === 2) {
841
- <div>{'x=1, y=2'}</div>
842
- return;
843
- } else {
844
- <div>{'x=1, y=other'}</div>
845
- return;
846
- }
847
- }
848
- <div>{'x!=1'}</div>
849
- }
850
-
851
- const { body } = await render(App);
852
- expect(body).toBeHtml('<div>x=1, y=2</div>');
853
- });
854
-
855
- describe('nested return scenarios', () => {
856
- it('nested return hides content after inner if inside outer if', async () => {
857
- component App() {
858
- let a = true;
859
- let b = true;
860
-
861
- if (a) {
862
- <div>{'a'}</div>
863
- if (b) {
864
- <div>{'b'}</div>
865
- return;
866
- }
867
- <div>{'after inner'}</div>
868
- }
869
- <div>{'rest'}</div>
870
- }
871
-
872
- const { body } = await render(App);
873
- expect(body).toBeHtml('<div>a</div><div>b</div>');
874
- });
875
-
876
- it('nested return shows content after inner if when inner condition is false', async () => {
877
- component App() {
878
- let a = true;
879
- let b = false;
880
-
881
- if (a) {
882
- <div>{'a'}</div>
883
- if (b) {
884
- <div>{'b'}</div>
885
- return;
886
- }
887
- <div>{'after inner'}</div>
888
- }
889
- <div>{'rest'}</div>
890
- }
891
-
892
- const { body } = await render(App);
893
- expect(body).toBeHtml('<div>a</div><div>after inner</div><div>rest</div>');
894
- });
895
-
896
- it('nested return with sibling returns inside outer if', async () => {
897
- component App() {
898
- let outer = true;
899
- let a = false;
900
- let b = true;
901
-
902
- if (outer) {
903
- <div>{'outer'}</div>
904
- if (a) {
905
- <div>{'a'}</div>
906
- return;
907
- }
908
- <div>{'between'}</div>
909
- if (b) {
910
- <div>{'b'}</div>
911
- return;
912
- }
913
- <div>{'after b'}</div>
914
- }
915
- <div>{'rest'}</div>
916
- }
917
-
918
- const { body } = await render(App);
919
- expect(body).toBeHtml('<div>outer</div><div>between</div><div>b</div>');
920
- });
921
-
922
- it('nested return inside else branch', async () => {
923
- component App() {
924
- let a = false;
925
- let b = true;
926
-
927
- if (a) {
928
- <div>{'a'}</div>
929
- } else {
930
- <div>{'else'}</div>
931
- if (b) {
932
- <div>{'b'}</div>
933
- return;
934
- }
935
- }
936
- <div>{'rest'}</div>
937
- }
938
-
939
- const { body } = await render(App);
940
- expect(body).toBeHtml('<div>else</div><div>b</div>');
941
- });
942
-
943
- it('deeply nested returns (4 levels) - all true', async () => {
944
- component App() {
945
- let a = true;
946
- let b = true;
947
- let c = true;
948
- let d = true;
949
-
950
- if (a) {
951
- <div>{'a'}</div>
952
- if (b) {
953
- <div>{'b'}</div>
954
- if (c) {
955
- <div>{'c'}</div>
956
- if (d) {
957
- <div>{'d'}</div>
958
- return;
959
- }
960
- }
961
- }
962
- }
963
- <div>{'rest'}</div>
964
- }
965
-
966
- const { body } = await render(App);
967
- expect(body).toBeHtml('<div>a</div><div>b</div><div>c</div><div>d</div>');
968
- });
969
-
970
- it('deeply nested returns (4 levels) - innermost false', async () => {
971
- component App() {
972
- let a = true;
973
- let b = true;
974
- let c = true;
975
- let d = false;
976
-
977
- if (a) {
978
- <div>{'a'}</div>
979
- if (b) {
980
- <div>{'b'}</div>
981
- if (c) {
982
- <div>{'c'}</div>
983
- if (d) {
984
- <div>{'d'}</div>
985
- return;
986
- }
987
- }
988
- }
989
- }
990
- <div>{'rest'}</div>
991
- }
992
-
993
- const { body } = await render(App);
994
- expect(body).toBeHtml('<div>a</div><div>b</div><div>c</div><div>rest</div>');
995
- });
996
-
997
- it('nested return with else at outer level', async () => {
998
- component App() {
999
- let a = true;
1000
- let b = true;
1001
-
1002
- if (a) {
1003
- <div>{'a'}</div>
1004
- if (b) {
1005
- <div>{'b'}</div>
1006
- return;
1007
- }
1008
- } else {
1009
- <div>{'else'}</div>
1010
- }
1011
- <div>{'rest'}</div>
1012
- }
1013
-
1014
- const { body } = await render(App);
1015
- expect(body).toBeHtml('<div>a</div><div>b</div>');
1016
- });
1017
-
1018
- it('nested return with else at outer level - outer false', async () => {
1019
- component App() {
1020
- let a = false;
1021
- let b = true;
1022
-
1023
- if (a) {
1024
- <div>{'a'}</div>
1025
- if (b) {
1026
- <div>{'b'}</div>
1027
- return;
1028
- }
1029
- } else {
1030
- <div>{'else'}</div>
1031
- }
1032
- <div>{'rest'}</div>
1033
- }
1034
-
1035
- const { body } = await render(App);
1036
- expect(body).toBeHtml('<div>else</div><div>rest</div>');
1037
- });
1038
-
1039
- it('nested return hides content at multiple intermediate levels', async () => {
1040
- component App() {
1041
- let a = true;
1042
- let b = true;
1043
- let c = true;
1044
-
1045
- if (a) {
1046
- <div>{'a'}</div>
1047
- if (b) {
1048
- <div>{'b'}</div>
1049
- if (c) {
1050
- <div>{'c'}</div>
1
+ import { describe, it, expect } from 'vitest';
2
+ import { compile } from '@tsrx/ripple';
3
+
4
+ const TSRX_RETURN_ERROR = 'Return statements are not allowed inside TSRX templates. Move the return before the TSRX return value, or use conditional rendering instead.';
5
+
6
+ describe('returns in prohibited SSR scopes', () => {
7
+ it('throws when return is used inside a TSRX fragment', () => {
8
+ expect(
9
+ () => compile(
10
+ `
11
+ function App() {
12
+ return <>
13
+ if (ready) {
1051
14
  return;
1052
15
  }
1053
- <div>{'after c'}</div>
1054
- }
1055
- <div>{'after b'}</div>
1056
- }
1057
- <div>{'rest'}</div>
1058
- }
1059
-
1060
- const { body } = await render(App);
1061
- expect(body).toBeHtml('<div>a</div><div>b</div><div>c</div>');
1062
- });
1063
- });
1064
-
1065
- it('handles return with comparison operators', async () => {
1066
- component App() {
1067
- let a = 5;
1068
- let b = 10;
1069
-
1070
- if (a < b) {
1071
- <div>{'a less than b'}</div>
1072
- return;
1073
- }
1074
- <div>{'a not less than b'}</div>
1075
- }
1076
-
1077
- const { body } = await render(App);
1078
- expect(body).toBeHtml('<div>a less than b</div>');
1079
- });
1080
-
1081
- it('handles return with equality operators', async () => {
1082
- component App() {
1083
- let a = 5;
1084
- let b = '5';
1085
-
1086
- // @ts-expect-error testing loose equality
1087
- if (a == b) {
1088
- <div>{'loose equality'}</div>
1089
- return;
1090
- }
1091
- <div>{'not loosely equal'}</div>
1092
- }
1093
-
1094
- const { body } = await render(App);
1095
- expect(body).toBeHtml('<div>loose equality</div>');
16
+ <div>{'ready'}</div>
17
+ </>;
18
+ }
19
+ `,
20
+ 'test.tsrx',
21
+ { mode: 'server' },
22
+ ),
23
+ ).toThrow(TSRX_RETURN_ERROR);
24
+ });
25
+
26
+ it('throws when return is used inside a TSRX element', () => {
27
+ expect(
28
+ () => compile(
29
+ `
30
+ function App() {
31
+ return <section>
32
+ if (ready) {
33
+ return null;
34
+ }
35
+ <div>{'ready'}</div>
36
+ </section>;
37
+ }
38
+ `,
39
+ 'test.tsrx',
40
+ { mode: 'server' },
41
+ ),
42
+ ).toThrow(TSRX_RETURN_ERROR);
43
+ });
44
+
45
+ it('allows returns inside regular functions declared in TSRX templates', () => {
46
+ expect(
47
+ () => compile(
48
+ `
49
+ function App() {
50
+ return <>
51
+ function label() {
52
+ return 'ready';
53
+ }
54
+ <div>{label()}</div>
55
+ </>;
56
+ }
57
+ `,
58
+ 'test.tsrx',
59
+ { mode: 'server' },
60
+ ),
61
+ ).not.toThrow();
1096
62
  });
63
+ });
1097
64
 
1098
- it('handles return with strict equality', async () => {
1099
- component App() {
1100
- let a = 5;
1101
- let b = 5;
1102
-
1103
- if (a === b) {
1104
- <div>{'strict equality'}</div>
1105
- return;
1106
- }
1107
- <div>{'not strictly equal'}</div>
1108
- }
1109
-
1110
- const { body } = await render(App);
1111
- expect(body).toBeHtml('<div>strict equality</div>');
1112
- });
1113
-
1114
- it('handles return with greater than or equal', async () => {
1115
- component App() {
1116
- let a = 10;
1117
- let b = 10;
1118
-
1119
- if (a >= b) {
1120
- <div>{'a >= b'}</div>
1121
- return;
65
+ describe('function returns in SSR components', () => {
66
+ it('allows guard returns before TSRX output', async () => {
67
+ function App() {
68
+ const ready = true;
69
+ if (!ready) {
70
+ return null;
1122
71
  }
1123
- <div>{'a < b'}</div>
1124
- }
1125
72
 
1126
- const { body } = await render(App);
1127
- expect(body).toBeHtml('<div>a >= b</div>');
1128
- });
1129
-
1130
- it('handles return with less than or equal (escaped in HTML)', async () => {
1131
- component App() {
1132
- let a = 5;
1133
- let b = 10;
1134
-
1135
- if (a <= b) {
1136
- <div>{'a <= b'}</div>
1137
- return;
1138
- }
1139
- <div>{'a > b'}</div>
73
+ return <><div>{'ready'}</div></>;
1140
74
  }
1141
75
 
1142
76
  const { body } = await render(App);
1143
- expect(body).toBeHtml('<div>a &lt;= b</div>');
77
+ expect(body).toBeHtml('<div>ready</div>');
1144
78
  });
1145
79
 
1146
- it('handles return with not equal', async () => {
1147
- component App() {
1148
- let a = 5;
1149
- let b = 10;
1150
-
1151
- if (a != b) {
1152
- <div>{'a != b'}</div>
1153
- return;
1154
- }
1155
- <div>{'a == b'}</div>
80
+ it('allows components to return null', async () => {
81
+ function App() {
82
+ return null;
1156
83
  }
1157
84
 
1158
85
  const { body } = await render(App);
1159
- expect(body).toBeHtml('<div>a != b</div>');
86
+ expect(body).toBeHtml('');
1160
87
  });
1161
88
 
1162
- it('handles return with strict not equal', async () => {
1163
- component App() {
1164
- let a = 5;
1165
- let b = '5';
1166
-
1167
- // @ts-expect-error testing strict inequality
1168
- if (a !== b) {
1169
- <div>{'a !== b'}</div>
1170
- return;
1171
- }
1172
- <div>{'a === b'}</div>
89
+ it('allows components to return strings', async () => {
90
+ function App() {
91
+ return 'hello';
1173
92
  }
1174
93
 
1175
94
  const { body } = await render(App);
1176
- expect(body).toBeHtml('<div>a !== b</div>');
1177
- });
1178
-
1179
- describe('deeply nested conditions with returns', () => {
1180
- it('handles return inside nested div > if > div > if chain, all false', async () => {
1181
- component App() {
1182
- let a = false;
1183
- let b = false;
1184
- let c = false;
1185
- let d = false;
1186
-
1187
- <div class="outer">
1188
- if (a) {
1189
- <span class="a">{'branch a'}</span>
1190
- }
1191
- <div class="inner">
1192
- if (b) {
1193
- <span class="b">{'branch b'}</span>
1194
- }
1195
- if (c) {
1196
- return;
1197
- }
1198
- if (d) {
1199
- <span class="d">{'branch d'}</span>
1200
- return;
1201
- }
1202
- </div>
1203
- </div>
1204
- <div class="after">{'after'}</div>
1205
- }
1206
-
1207
- const { body } = await render(App);
1208
- expect(body).toContain('class="outer"');
1209
- expect(body).toContain('class="inner"');
1210
- expect(body).toContain('class="after"');
1211
- expect(body).not.toContain('class="a"');
1212
- expect(body).not.toContain('class="b"');
1213
- expect(body).not.toContain('class="d"');
1214
- });
1215
-
1216
- it('nested: first return (c) triggers, hides after', async () => {
1217
- component App() {
1218
- let a = false;
1219
- let b = false;
1220
- let c = true;
1221
- let d = false;
1222
-
1223
- <div class="outer">
1224
- if (a) {
1225
- <span class="a">{'branch a'}</span>
1226
- }
1227
- <div class="inner">
1228
- if (b) {
1229
- <span class="b">{'branch b'}</span>
1230
- }
1231
- if (c) {
1232
- return;
1233
- }
1234
- if (d) {
1235
- <span class="d">{'branch d'}</span>
1236
- return;
1237
- }
1238
- </div>
1239
- </div>
1240
- <div class="after">{'after'}</div>
1241
- }
1242
-
1243
- const { body } = await render(App);
1244
- expect(body).toContain('class="outer"');
1245
- expect(body).toContain('class="inner"');
1246
- expect(body).not.toContain('class="after"');
1247
- expect(body).not.toContain('class="d"');
1248
- });
1249
-
1250
- it('nested: second return (d) triggers with template, hides after', async () => {
1251
- component App() {
1252
- let a = true;
1253
- let b = true;
1254
- let c = false;
1255
- let d = true;
1256
-
1257
- <div class="outer">
1258
- if (a) {
1259
- <span class="a">{'branch a'}</span>
1260
- }
1261
- <div class="inner">
1262
- if (b) {
1263
- <span class="b">{'branch b'}</span>
1264
- }
1265
- if (c) {
1266
- return;
1267
- }
1268
- if (d) {
1269
- <span class="d">{'branch d'}</span>
1270
- return;
1271
- }
1272
- </div>
1273
- </div>
1274
- <div class="after">{'after'}</div>
1275
- }
1276
-
1277
- const { body } = await render(App);
1278
- expect(body).toContain('class="a"');
1279
- expect(body).toContain('class="b"');
1280
- expect(body).toContain('class="d"');
1281
- expect(body).not.toContain('class="after"');
1282
- });
1283
-
1284
- it('nested: both returns active, first (c) wins', async () => {
1285
- component App() {
1286
- let a = false;
1287
- let b = false;
1288
- let c = true;
1289
- let d = true;
1290
-
1291
- <div class="outer">
1292
- if (a) {
1293
- <span class="a">{'branch a'}</span>
1294
- }
1295
- <div class="inner">
1296
- if (b) {
1297
- <span class="b">{'branch b'}</span>
1298
- }
1299
- if (c) {
1300
- return;
1301
- }
1302
- if (d) {
1303
- <span class="d">{'branch d'}</span>
1304
- return;
1305
- }
1306
- </div>
1307
- </div>
1308
- <div class="after">{'after'}</div>
1309
- }
1310
-
1311
- const { body } = await render(App);
1312
- expect(body).toContain('class="outer"');
1313
- expect(body).toContain('class="inner"');
1314
- expect(body).not.toContain('class="d"');
1315
- expect(body).not.toContain('class="after"');
1316
- });
1317
-
1318
- it(
1319
- 'nested outer/inner return guards render fallback paths when inner guard is false',
1320
- async () => {
1321
- component App() {
1322
- let a = true;
1323
- let b = false;
1324
-
1325
- if (a) {
1326
- <div class="a">{'a'}</div>
1327
- if (b) {
1328
- <div class="b">{'b'}</div>
1329
- return;
1330
- }
1331
- <div class="inner-rest">{'inner rest'}</div>
1332
- }
1333
-
1334
- <div class="rest">{'rest'}</div>
1335
- }
1336
-
1337
- const { body } = await render(App);
1338
- expect(body).toBeHtml(
1339
- '<div class="a">a</div><div class="inner-rest">inner rest</div><div class="rest">rest</div>',
1340
- );
1341
- },
1342
- );
1343
-
1344
- it('emits balanced hydration markers for nested sibling return guards', async () => {
1345
- component App() {
1346
- let a = true;
1347
- let b = false;
1348
- let c = true;
1349
-
1350
- if (a) {
1351
- <div class="a">{'a'}</div>
1352
- if (b) {
1353
- <div class="b">{'b'}</div>
1354
- return;
1355
- }
1356
- if (c) {
1357
- <div class="c">{'c'}</div>
1358
- return;
1359
- }
1360
- <div class="inner-rest">{'inner rest'}</div>
1361
- }
1362
-
1363
- <div class="rest">{'rest'}</div>
1364
- }
1365
-
1366
- const { body } = await render(App);
1367
- const open_markers = body.match(/<!--\[-->/g) || [];
1368
- const close_markers = body.match(/<!--\]-->/g) || [];
1369
- expect(open_markers.length).toBe(close_markers.length);
1370
- expect(open_markers.length).toBeGreaterThanOrEqual(3);
1371
- expect(body).toContain('<div class="a">a</div>');
1372
- expect(body).toContain('<div class="c">c</div>');
1373
- expect(body).not.toContain('<div class="rest">rest</div>');
1374
- });
95
+ expect(body).toBeHtml('hello');
1375
96
  });
1376
97
  });