ripple 0.2.208 → 0.2.211

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 (112) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +2 -1
  3. package/package.json +2 -6
  4. package/shims/rollup-estree-types.d.ts +1 -1
  5. package/src/compiler/index.d.ts +1 -0
  6. package/src/compiler/index.js +7 -1
  7. package/src/compiler/phases/1-parse/index.js +24 -7
  8. package/src/compiler/phases/2-analyze/css-analyze.js +100 -104
  9. package/src/compiler/phases/2-analyze/index.js +215 -2
  10. package/src/compiler/phases/3-transform/client/index.js +388 -50
  11. package/src/compiler/phases/3-transform/segments.js +123 -39
  12. package/src/compiler/phases/3-transform/server/index.js +266 -13
  13. package/src/compiler/types/index.d.ts +15 -3
  14. package/src/compiler/utils.js +1 -15
  15. package/src/constants.js +0 -2
  16. package/src/helpers.d.ts +4 -0
  17. package/src/html-tree-validation.js +211 -0
  18. package/src/jsx-runtime.d.ts +260 -259
  19. package/src/jsx-runtime.js +12 -12
  20. package/src/runtime/array.js +17 -17
  21. package/src/runtime/create-subscriber.js +1 -1
  22. package/src/runtime/index-client.js +1 -5
  23. package/src/runtime/index-server.js +15 -0
  24. package/src/runtime/internal/client/compat.js +3 -3
  25. package/src/runtime/internal/client/composite.js +6 -1
  26. package/src/runtime/internal/client/head.js +50 -4
  27. package/src/runtime/internal/client/html.js +73 -12
  28. package/src/runtime/internal/client/hydration.js +12 -0
  29. package/src/runtime/internal/client/index.js +1 -1
  30. package/src/runtime/internal/client/portal.js +54 -29
  31. package/src/runtime/internal/client/rpc.js +3 -1
  32. package/src/runtime/internal/client/switch.js +5 -0
  33. package/src/runtime/internal/client/template.js +119 -12
  34. package/src/runtime/internal/client/try.js +1 -0
  35. package/src/runtime/internal/server/index.js +113 -1
  36. package/src/runtime/internal/server/rpc.js +4 -4
  37. package/src/runtime/map.js +2 -2
  38. package/src/runtime/object.js +6 -6
  39. package/src/runtime/proxy.js +12 -11
  40. package/src/runtime/reactive-value.js +9 -1
  41. package/src/runtime/set.js +12 -7
  42. package/src/runtime/url-search-params.js +0 -1
  43. package/src/server/index.js +4 -0
  44. package/src/utils/hashing.js +15 -0
  45. package/src/utils/normalize_css_property_name.js +1 -1
  46. package/tests/client/array/array.mutations.test.ripple +8 -8
  47. package/tests/client/basic/basic.errors.test.ripple +28 -0
  48. package/tests/client/basic/basic.events.test.ripple +6 -3
  49. package/tests/client/basic/basic.utilities.test.ripple +1 -1
  50. package/tests/client/compiler/compiler.regex.test.ripple +10 -8
  51. package/tests/client/composite/composite.generics.test.ripple +5 -2
  52. package/tests/client/dynamic-elements.test.ripple +30 -1
  53. package/tests/client/function-overload-import.ripple +6 -7
  54. package/tests/client/html.test.ripple +0 -1
  55. package/tests/client/object.test.ripple +2 -2
  56. package/tests/client/portal.test.ripple +3 -3
  57. package/tests/client/return.test.ripple +2500 -0
  58. package/tests/client/try.test.ripple +69 -0
  59. package/tests/client/typescript-generics.test.ripple +1 -1
  60. package/tests/client/url/url.derived.test.ripple +1 -1
  61. package/tests/client/url/url.parsing.test.ripple +3 -3
  62. package/tests/client/url/url.partial-removal.test.ripple +7 -7
  63. package/tests/client/url/url.reactivity.test.ripple +15 -15
  64. package/tests/client/url/url.serialization.test.ripple +2 -2
  65. package/tests/hydration/basic.test.js +23 -0
  66. package/tests/hydration/build-components.js +10 -4
  67. package/tests/hydration/compiled/client/basic.js +165 -3
  68. package/tests/hydration/compiled/client/composite.js +139 -0
  69. package/tests/hydration/compiled/client/for.js +1140 -23
  70. package/tests/hydration/compiled/client/head.js +234 -0
  71. package/tests/hydration/compiled/client/html.js +135 -0
  72. package/tests/hydration/compiled/client/portal.js +172 -0
  73. package/tests/hydration/compiled/client/reactivity.js +3 -1
  74. package/tests/hydration/compiled/client/return.js +1976 -0
  75. package/tests/hydration/compiled/client/switch.js +162 -0
  76. package/tests/hydration/compiled/server/basic.js +249 -0
  77. package/tests/hydration/compiled/server/composite.js +176 -0
  78. package/tests/hydration/compiled/server/events.js +1 -1
  79. package/tests/hydration/compiled/server/for.js +891 -1
  80. package/tests/hydration/compiled/server/head.js +291 -0
  81. package/tests/hydration/compiled/server/html.js +133 -0
  82. package/tests/hydration/compiled/server/if.js +1 -1
  83. package/tests/hydration/compiled/server/portal.js +250 -0
  84. package/tests/hydration/compiled/server/reactivity.js +1 -1
  85. package/tests/hydration/compiled/server/return.js +1969 -0
  86. package/tests/hydration/compiled/server/switch.js +130 -0
  87. package/tests/hydration/components/basic.ripple +55 -0
  88. package/tests/hydration/components/composite.ripple +37 -0
  89. package/tests/hydration/components/for.ripple +403 -0
  90. package/tests/hydration/components/head.ripple +111 -0
  91. package/tests/hydration/components/html.ripple +38 -0
  92. package/tests/hydration/components/portal.ripple +49 -0
  93. package/tests/hydration/components/return.ripple +564 -0
  94. package/tests/hydration/components/switch.ripple +51 -0
  95. package/tests/hydration/composite.test.js +42 -0
  96. package/tests/hydration/for.test.js +363 -0
  97. package/tests/hydration/head.test.js +105 -0
  98. package/tests/hydration/html.test.js +46 -0
  99. package/tests/hydration/portal.test.js +71 -0
  100. package/tests/hydration/return.test.js +544 -0
  101. package/tests/hydration/switch.test.js +42 -0
  102. package/tests/server/basic.attributes.test.ripple +1 -1
  103. package/tests/server/compiler.test.ripple +22 -0
  104. package/tests/server/composite.test.ripple +5 -2
  105. package/tests/server/html-nesting-validation.test.ripple +237 -0
  106. package/tests/server/return.test.ripple +1379 -0
  107. package/tests/setup-hydration.js +6 -1
  108. package/tests/utils/escaping.test.js +3 -1
  109. package/tests/utils/normalize_css_property_name.test.js +0 -1
  110. package/tests/utils/patterns.test.js +6 -2
  111. package/tests/utils/sanitize_template_string.test.js +3 -2
  112. package/types/server.d.ts +16 -0
@@ -0,0 +1,564 @@
1
+ // Return statement components for hydration testing
2
+ import { track } from 'ripple';
3
+
4
+ // Basic return - skips content after direct return
5
+ export component DirectReturn() {
6
+ <div class="before">{'before'}</div>
7
+ return;
8
+ <div class="after">{'after'}</div>
9
+ }
10
+
11
+ // Conditional return - condition is true, skips rest
12
+ export component ConditionalReturnTrue() {
13
+ let condition = true;
14
+
15
+ if (condition) {
16
+ <div class="guard">{'guard hit'}</div>
17
+ return;
18
+ }
19
+ <div class="rest">{'rest'}</div>
20
+ }
21
+
22
+ // Conditional return - condition is false, shows rest
23
+ export component ConditionalReturnFalse() {
24
+ let condition = false;
25
+
26
+ if (condition) {
27
+ <div class="guard">{'guard hit'}</div>
28
+ return;
29
+ }
30
+ <div class="rest">{'rest'}</div>
31
+ }
32
+
33
+ // Content before and after return guard
34
+ export component ContentBeforeAfterReturn() {
35
+ let shouldReturn = true;
36
+
37
+ <div class="before">{'before'}</div>
38
+ if (shouldReturn) {
39
+ <div class="guard">{'guard'}</div>
40
+ return;
41
+ }
42
+ <div class="after">{'after'}</div>
43
+ }
44
+
45
+ // Multiple elements after guard when condition is false
46
+ export component MultipleElementsAfterGuard() {
47
+ let shouldReturn = false;
48
+
49
+ if (shouldReturn) {
50
+ <div class="guard">{'guard'}</div>
51
+ return;
52
+ }
53
+ <div class="first">{'first'}</div>
54
+ <div class="second">{'second'}</div>
55
+ }
56
+
57
+ // Multiple sequential returns - first hits
58
+ export component MultipleReturnsFirstHits() {
59
+ let a = true;
60
+ let b = true;
61
+
62
+ if (a) {
63
+ <div class="first">{'first guard'}</div>
64
+ return;
65
+ }
66
+ if (b) {
67
+ <div class="second">{'second guard'}</div>
68
+ return;
69
+ }
70
+ <div class="rest">{'rest'}</div>
71
+ }
72
+
73
+ // Multiple sequential returns - second hits
74
+ export component MultipleReturnsSecondHits() {
75
+ let a = false;
76
+ let b = true;
77
+
78
+ if (a) {
79
+ <div class="first">{'first guard'}</div>
80
+ return;
81
+ }
82
+ if (b) {
83
+ <div class="second">{'second guard'}</div>
84
+ return;
85
+ }
86
+ <div class="rest">{'rest'}</div>
87
+ }
88
+
89
+ // Multiple sequential returns - none hit
90
+ export component MultipleReturnsNoneHit() {
91
+ let a = false;
92
+ let b = false;
93
+
94
+ if (a) {
95
+ <div class="first">{'first guard'}</div>
96
+ return;
97
+ }
98
+ if (b) {
99
+ <div class="second">{'second guard'}</div>
100
+ return;
101
+ }
102
+ <div class="rest">{'rest'}</div>
103
+ }
104
+
105
+ // Nested returns - both conditions true
106
+ export component NestedReturnsAllTrue() {
107
+ let a = true;
108
+ let b = true;
109
+
110
+ if (a) {
111
+ <div class="a">{'a is true'}</div>
112
+ if (b) {
113
+ <div class="b">{'b is true'}</div>
114
+ return;
115
+ }
116
+ }
117
+ <div class="rest">{'rest'}</div>
118
+ }
119
+
120
+ // Nested returns - inner condition false
121
+ export component NestedReturnsInnerFalse() {
122
+ let a = true;
123
+ let b = false;
124
+
125
+ if (a) {
126
+ <div class="a">{'a is true'}</div>
127
+ if (b) {
128
+ <div class="b">{'b is true'}</div>
129
+ return;
130
+ }
131
+ }
132
+ <div class="rest">{'rest'}</div>
133
+ }
134
+
135
+ // Nested returns - outer condition false
136
+ export component NestedReturnsOuterFalse() {
137
+ let a = false;
138
+ let b = true;
139
+
140
+ if (a) {
141
+ <div class="a">{'a is true'}</div>
142
+ if (b) {
143
+ <div class="b">{'b is true'}</div>
144
+ return;
145
+ }
146
+ }
147
+ <div class="rest">{'rest'}</div>
148
+ }
149
+
150
+ // Deeply nested returns (3 levels) - all true
151
+ export component DeeplyNestedReturnsAllTrue() {
152
+ let a = true;
153
+ let b = true;
154
+ let c = true;
155
+
156
+ if (a) {
157
+ <div class="a">{'a'}</div>
158
+ if (b) {
159
+ <div class="b">{'b'}</div>
160
+ if (c) {
161
+ <div class="c">{'c'}</div>
162
+ return;
163
+ }
164
+ }
165
+ }
166
+ <div class="rest">{'rest'}</div>
167
+ }
168
+
169
+ // Deeply nested returns (3 levels) - innermost false
170
+ export component DeeplyNestedReturnsInnermostFalse() {
171
+ let a = true;
172
+ let b = true;
173
+ let c = false;
174
+
175
+ if (a) {
176
+ <div class="a">{'a'}</div>
177
+ if (b) {
178
+ <div class="b">{'b'}</div>
179
+ if (c) {
180
+ <div class="c">{'c'}</div>
181
+ return;
182
+ }
183
+ }
184
+ }
185
+ <div class="rest">{'rest'}</div>
186
+ }
187
+
188
+ // Return with else-if chain - first condition
189
+ export component ElseIfChainFirst() {
190
+ let value = 1;
191
+
192
+ if (value === 1) {
193
+ <div class="one">{'one'}</div>
194
+ return;
195
+ } else if (value === 2) {
196
+ <div class="two">{'two'}</div>
197
+ return;
198
+ } else {
199
+ <div class="other">{'other'}</div>
200
+ return;
201
+ }
202
+ <div class="never">{'never reached'}</div>
203
+ }
204
+
205
+ // Return with else-if chain - second condition
206
+ export component ElseIfChainSecond() {
207
+ let value = 2;
208
+
209
+ if (value === 1) {
210
+ <div class="one">{'one'}</div>
211
+ return;
212
+ } else if (value === 2) {
213
+ <div class="two">{'two'}</div>
214
+ return;
215
+ } else {
216
+ <div class="other">{'other'}</div>
217
+ return;
218
+ }
219
+ <div class="never">{'never reached'}</div>
220
+ }
221
+
222
+ // Return with else-if chain - else condition
223
+ export component ElseIfChainElse() {
224
+ let value = 3;
225
+
226
+ if (value === 1) {
227
+ <div class="one">{'one'}</div>
228
+ return;
229
+ } else if (value === 2) {
230
+ <div class="two">{'two'}</div>
231
+ return;
232
+ } else {
233
+ <div class="other">{'other'}</div>
234
+ return;
235
+ }
236
+ <div class="never">{'never reached'}</div>
237
+ }
238
+
239
+ // Return with else branch that does not return
240
+ export component ReturnWithElseNoReturn() {
241
+ let condition = false;
242
+
243
+ if (condition) {
244
+ <div class="true">{'condition true'}</div>
245
+ return;
246
+ } else {
247
+ <div class="false">{'condition false'}</div>
248
+ }
249
+ <div class="after">{'after if-else'}</div>
250
+ }
251
+
252
+ // Return with else branch that also returns
253
+ export component ReturnWithElseBothReturn() {
254
+ let condition = false;
255
+
256
+ if (condition) {
257
+ <div class="true">{'condition true'}</div>
258
+ return;
259
+ } else {
260
+ <div class="false">{'condition false'}</div>
261
+ return;
262
+ }
263
+ <div class="never">{'never reached'}</div>
264
+ }
265
+
266
+ // Reactive return - starts true, can toggle to false
267
+ export component ReactiveReturnTrueToFalse() {
268
+ let condition = track(true);
269
+
270
+ <button
271
+ class="toggle"
272
+ onClick={() => {
273
+ @condition = !@condition;
274
+ }}
275
+ >
276
+ {'Toggle'}
277
+ </button>
278
+ if (@condition) {
279
+ <div class="guard">{'guard hit'}</div>
280
+ return;
281
+ }
282
+ <div class="rest">{'rest'}</div>
283
+ }
284
+
285
+ // Reactive return - starts false, can toggle to true
286
+ export component ReactiveReturnFalseToTrue() {
287
+ let condition = track(false);
288
+
289
+ <button
290
+ class="toggle"
291
+ onClick={() => {
292
+ @condition = !@condition;
293
+ }}
294
+ >
295
+ {'Toggle'}
296
+ </button>
297
+ if (@condition) {
298
+ <div class="guard">{'guard hit'}</div>
299
+ return;
300
+ }
301
+ <div class="rest">{'rest'}</div>
302
+ }
303
+
304
+ // Reactive nested return - only inner condition (b) is tracked
305
+ export component ReactiveNestedReturn() {
306
+ let a = true;
307
+ let b = track(true);
308
+
309
+ <button
310
+ class="toggle"
311
+ onClick={() => {
312
+ @b = !@b;
313
+ }}
314
+ >
315
+ {'Toggle'}
316
+ </button>
317
+ if (a) {
318
+ <div class="a">{'a'}</div>
319
+ if (@b) {
320
+ <div class="b">{'b'}</div>
321
+ return;
322
+ }
323
+ }
324
+ <div class="rest">{'rest'}</div>
325
+ }
326
+
327
+ // Return inside nested element scope
328
+ export component ReturnInNestedElement() {
329
+ let show = true;
330
+
331
+ <div class="outer">
332
+ <span class="label">{'outer'}</span>
333
+ if (show) {
334
+ <p class="inner">{'inner'}</p>
335
+ return;
336
+ }
337
+ </div>
338
+ <div class="after">{'after'}</div>
339
+ }
340
+
341
+ // Return with multiple elements before and after
342
+ export component ReturnWithMultipleElements() {
343
+ let shouldReturn = true;
344
+
345
+ <h1 class="title">{'title'}</h1>
346
+ <p class="desc">{'description'}</p>
347
+ if (shouldReturn) {
348
+ <div class="guard">{'guard'}</div>
349
+ <span class="guard-span">{'guard span'}</span>
350
+ return;
351
+ }
352
+ <footer class="footer">{'footer'}</footer>
353
+ <nav class="nav">{'nav'}</nav>
354
+ }
355
+
356
+ // Return at the beginning of component
357
+ export component ReturnAtBeginning() {
358
+ if (true) {
359
+ <div class="early">{'early exit'}</div>
360
+ return;
361
+ }
362
+ <div class="never1">{'never reached 1'}</div>
363
+ <div class="never2">{'never reached 2'}</div>
364
+ }
365
+
366
+ // Return at the end of component (after all content)
367
+ export component ReturnAtEnd() {
368
+ <div class="first">{'first'}</div>
369
+ <div class="second">{'second'}</div>
370
+ if (true) {
371
+ <div class="third">{'third'}</div>
372
+ return;
373
+ }
374
+ }
375
+
376
+ // Multiple sibling returns at same level
377
+ export component MultipleSiblingReturns() {
378
+ let mode = 'b';
379
+
380
+ if (mode === 'a') {
381
+ <div class="mode-a">{'mode A'}</div>
382
+ return;
383
+ }
384
+
385
+ if (mode === 'b') {
386
+ <div class="mode-b">{'mode B'}</div>
387
+ return;
388
+ }
389
+
390
+ if (mode === 'c') {
391
+ <div class="mode-c">{'mode C'}</div>
392
+ return;
393
+ }
394
+
395
+ <div class="default">{'default mode'}</div>
396
+ }
397
+
398
+ // Reactive sibling returns - cycles first -> second -> fallback
399
+ export component ReactiveSiblingReturns() {
400
+ let mode = track('first');
401
+
402
+ <button
403
+ class="toggle"
404
+ onClick={() => {
405
+ if (@mode === 'first') {
406
+ @mode = 'second';
407
+ } else if (@mode === 'second') {
408
+ @mode = 'none';
409
+ } else {
410
+ @mode = 'first';
411
+ }
412
+ }}
413
+ >
414
+ {'Toggle'}
415
+ </button>
416
+
417
+ if (@mode === 'first') {
418
+ <div class="first">{'first guard'}</div>
419
+ return;
420
+ }
421
+
422
+ if (@mode === 'second') {
423
+ <div class="second">{'second guard'}</div>
424
+ return;
425
+ }
426
+
427
+ <div class="rest">{'rest'}</div>
428
+ }
429
+
430
+ // Reactive nested returns with tracked outer and inner conditions
431
+ export component ReactiveOuterInnerReturns() {
432
+ let a = track(true);
433
+ let b = track(true);
434
+
435
+ <button
436
+ class="toggle-a"
437
+ onClick={() => {
438
+ @a = !@a;
439
+ }}
440
+ >
441
+ {'Toggle A'}
442
+ </button>
443
+
444
+ <button
445
+ class="toggle-b"
446
+ onClick={() => {
447
+ @b = !@b;
448
+ }}
449
+ >
450
+ {'Toggle B'}
451
+ </button>
452
+
453
+ if (@a) {
454
+ <div class="a">{'a'}</div>
455
+ if (@b) {
456
+ <div class="b">{'b'}</div>
457
+ return;
458
+ }
459
+ }
460
+
461
+ <div class="rest">{@a ? 'a-on rest' : 'a-off rest'}</div>
462
+ }
463
+
464
+ // Reactive else-if return chain that transitions between return and non-return states
465
+ export component ReactiveElseIfReturns() {
466
+ let status = track(0);
467
+
468
+ <button
469
+ class="toggle"
470
+ onClick={() => {
471
+ @status = (@status + 1) % 3;
472
+ }}
473
+ >
474
+ {'Toggle'}
475
+ </button>
476
+
477
+ if (@status === 0) {
478
+ <div class="zero">{'zero'}</div>
479
+ return;
480
+ } else if (@status === 1) {
481
+ <div class="one">{'one'}</div>
482
+ return;
483
+ }
484
+
485
+ <div class="rest">{'rest'}</div>
486
+ <div class="tail">{'tail'}</div>
487
+ }
488
+
489
+ // Deeply nested independent return guards with multiple root-level siblings
490
+ export component ReactiveDeepNestedIndependentReturns() {
491
+ let c1 = track(false);
492
+ let c2 = track(false);
493
+ let c3 = track(false);
494
+ let c4 = track(false);
495
+
496
+ <button
497
+ class="toggle-c1"
498
+ onClick={() => {
499
+ @c1 = !@c1;
500
+ }}
501
+ >
502
+ {'Toggle C1'}
503
+ </button>
504
+ <button
505
+ class="toggle-c2"
506
+ onClick={() => {
507
+ @c2 = !@c2;
508
+ }}
509
+ >
510
+ {'Toggle C2'}
511
+ </button>
512
+ <button
513
+ class="toggle-c3"
514
+ onClick={() => {
515
+ @c3 = !@c3;
516
+ }}
517
+ >
518
+ {'Toggle C3'}
519
+ </button>
520
+ <button
521
+ class="toggle-c4"
522
+ onClick={() => {
523
+ @c4 = !@c4;
524
+ }}
525
+ >
526
+ {'Toggle C4'}
527
+ </button>
528
+
529
+ <div class="top">{'top'}</div>
530
+
531
+ if (@c1) {
532
+ <div class="hit-1">{'hit-1'}</div>
533
+ return;
534
+ }
535
+
536
+ <div class="middle">{'middle'}</div>
537
+ <section class="nest-1">
538
+ <div class="nest-1-a">{'nest-1-a'}</div>
539
+ if (@c2) {
540
+ <div class="hit-2">{'hit-2'}</div>
541
+ return;
542
+ }
543
+
544
+ <div class="nest-1-b">{'nest-1-b'}</div>
545
+ <section class="nest-2">
546
+ <div class="nest-2-a">{'nest-2-a'}</div>
547
+ if (@c3) {
548
+ <div class="hit-3">{'hit-3'}</div>
549
+ return;
550
+ }
551
+
552
+ <div class="nest-2-b">{'nest-2-b'}</div>
553
+ if (@c4) {
554
+ <div class="hit-4">{'hit-4'}</div>
555
+ return;
556
+ }
557
+ </section>
558
+ </section>
559
+
560
+ <div class="root-1">{'root-1'}</div>
561
+ <div class="root-2">{'root-2'}</div>
562
+ <div class="root-3">{'root-3'}</div>
563
+ <div class="root-4">{'root-4'}</div>
564
+ }
@@ -0,0 +1,51 @@
1
+ import { track } from 'ripple';
2
+
3
+ export component SwitchStatic() {
4
+ const status = 'success';
5
+ switch (status) {
6
+ case 'success':
7
+ <div class="status-success">{'Success'}</div>
8
+ break;
9
+ case 'error':
10
+ <div class="status-error">{'Error'}</div>
11
+ break;
12
+ default:
13
+ <div class="status-unknown">{'Unknown'}</div>
14
+ }
15
+ }
16
+
17
+ export component SwitchReactive() {
18
+ let status = track<'a' | 'b' | 'c'>('a');
19
+ <button
20
+ class="toggle"
21
+ onClick={() => {
22
+ if (@status === 'a') @status = 'b';
23
+ else if (@status === 'b') @status = 'c';
24
+ else @status = 'a';
25
+ }}
26
+ >
27
+ {'Toggle'}
28
+ </button>
29
+ switch (@status) {
30
+ case 'a':
31
+ <div class="case-a">{'Case A'}</div>
32
+ break;
33
+ case 'b':
34
+ <div class="case-b">{'Case B'}</div>
35
+ break;
36
+ default:
37
+ <div class="case-c">{'Case C'}</div>
38
+ }
39
+ }
40
+
41
+ export component SwitchFallthrough() {
42
+ const val = 1;
43
+ switch (val) {
44
+ case 1:
45
+ case 2:
46
+ <div class="case-1-2">{'1 or 2'}</div>
47
+ break;
48
+ default:
49
+ <div class="case-other">{'Other'}</div>
50
+ }
51
+ }
@@ -0,0 +1,42 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { hydrateComponent, container } from '../setup-hydration.js';
3
+
4
+ import * as ServerComponents from './compiled/server/composite.js';
5
+ import * as ClientComponents from './compiled/client/composite.js';
6
+
7
+ describe('hydration > composite', () => {
8
+ it('hydrates a layout with no children', async () => {
9
+ await hydrateComponent(ServerComponents.EmptyLayout, ClientComponents.EmptyLayout);
10
+ expect(container.innerHTML).toBeHtml('<div class=\"layout\"></div>');
11
+ });
12
+
13
+ it('hydrates a layout with a single child component', async () => {
14
+ await hydrateComponent(
15
+ ServerComponents.LayoutWithSingleChild,
16
+ ClientComponents.LayoutWithSingleChild,
17
+ );
18
+ expect(container.innerHTML).toBeHtml(
19
+ '<div class=\"layout\"><div class=\"single\">single</div></div>',
20
+ );
21
+ });
22
+
23
+ it('hydrates a layout with multiple children', async () => {
24
+ await hydrateComponent(
25
+ ServerComponents.LayoutWithMultipleChildren,
26
+ ClientComponents.LayoutWithMultipleChildren,
27
+ );
28
+ expect(container.innerHTML).toBeHtml(
29
+ '<div class=\"layout\"><div class=\"single\">single</div><div class=\"extra\">extra</div></div>',
30
+ );
31
+ });
32
+
33
+ it('hydrates a layout with a child that has multiple roots', async () => {
34
+ await hydrateComponent(
35
+ ServerComponents.LayoutWithMultiRootChild,
36
+ ClientComponents.LayoutWithMultiRootChild,
37
+ );
38
+ expect(container.innerHTML).toBeHtml(
39
+ '<div class=\"layout\"><h1>title</h1><p>description</p></div>',
40
+ );
41
+ });
42
+ });