ripple 0.3.72 → 0.3.76

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 (172) hide show
  1. package/CHANGELOG.md +116 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +4 -10
  4. package/src/runtime/dynamic-client.js +33 -0
  5. package/src/runtime/dynamic-server.js +80 -0
  6. package/src/runtime/index-client.js +5 -13
  7. package/src/runtime/index-server.js +2 -0
  8. package/src/runtime/internal/client/blocks.js +6 -27
  9. package/src/runtime/internal/client/composite.js +11 -6
  10. package/src/runtime/internal/client/for.js +80 -5
  11. package/src/runtime/internal/client/index.js +0 -2
  12. package/src/runtime/internal/client/render.js +5 -2
  13. package/src/runtime/internal/client/types.d.ts +0 -10
  14. package/src/runtime/internal/server/index.js +8 -1
  15. package/tests/client/__snapshots__/computed-properties.test.tsrx.snap +8 -0
  16. package/tests/client/__snapshots__/for.test.tsrx.snap +22 -0
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +4 -0
  18. package/tests/client/array/array.copy-within.test.tsrx +19 -19
  19. package/tests/client/array/array.derived.test.tsrx +97 -109
  20. package/tests/client/array/array.iteration.test.tsrx +28 -28
  21. package/tests/client/array/array.mutations.test.tsrx +68 -68
  22. package/tests/client/array/array.static.test.tsrx +82 -92
  23. package/tests/client/array/array.to-methods.test.tsrx +15 -15
  24. package/tests/client/async-suspend.test.tsrx +180 -179
  25. package/tests/client/basic/__snapshots__/basic.attributes.test.tsrx.snap +2 -0
  26. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +4 -0
  27. package/tests/client/basic/basic.attributes.test.tsrx +273 -317
  28. package/tests/client/basic/basic.collections.test.tsrx +55 -61
  29. package/tests/client/basic/basic.components.test.tsrx +198 -220
  30. package/tests/client/basic/basic.errors.test.tsrx +70 -76
  31. package/tests/client/basic/basic.events.test.tsrx +80 -85
  32. package/tests/client/basic/basic.get-set.test.tsrx +54 -64
  33. package/tests/client/basic/basic.hmr.test.tsrx +15 -19
  34. package/tests/client/basic/basic.reactivity.test.tsrx +121 -135
  35. package/tests/client/basic/basic.rendering.test.tsrx +273 -178
  36. package/tests/client/basic/basic.styling.test.tsrx +16 -14
  37. package/tests/client/basic/basic.utilities.test.tsrx +8 -10
  38. package/tests/client/boundaries.test.tsrx +18 -18
  39. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -76
  40. package/tests/client/compiler/compiler.attributes.test.tsrx +18 -14
  41. package/tests/client/compiler/compiler.basic.test.tsrx +357 -288
  42. package/tests/client/compiler/compiler.regex.test.tsrx +40 -44
  43. package/tests/client/compiler/compiler.tracked-access.test.tsrx +57 -38
  44. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  45. package/tests/client/compiler/compiler.typescript.test.tsrx +4 -3
  46. package/tests/client/composite/composite.dynamic-components.test.tsrx +62 -47
  47. package/tests/client/composite/composite.generics.test.tsrx +165 -167
  48. package/tests/client/composite/composite.props.test.tsrx +66 -74
  49. package/tests/client/composite/composite.reactivity.test.tsrx +132 -166
  50. package/tests/client/composite/composite.render.test.tsrx +92 -101
  51. package/tests/client/computed-properties.test.tsrx +14 -18
  52. package/tests/client/context.test.tsrx +14 -18
  53. package/tests/client/css/global-additional-cases.test.tsrx +493 -439
  54. package/tests/client/css/global-advanced-selectors.test.tsrx +169 -153
  55. package/tests/client/css/global-at-rules.test.tsrx +71 -66
  56. package/tests/client/css/global-basic.test.tsrx +105 -98
  57. package/tests/client/css/global-classes-ids.test.tsrx +128 -114
  58. package/tests/client/css/global-combinators.test.tsrx +83 -78
  59. package/tests/client/css/global-complex-nesting.test.tsrx +134 -120
  60. package/tests/client/css/global-edge-cases.test.tsrx +138 -120
  61. package/tests/client/css/global-keyframes.test.tsrx +108 -96
  62. package/tests/client/css/global-nested.test.tsrx +88 -78
  63. package/tests/client/css/global-pseudo.test.tsrx +104 -98
  64. package/tests/client/css/global-scoping.test.tsrx +145 -125
  65. package/tests/client/css/style-identifier.test.tsrx +65 -72
  66. package/tests/client/date.test.tsrx +83 -83
  67. package/tests/client/dynamic-elements.test.tsrx +318 -299
  68. package/tests/client/events.test.tsrx +252 -266
  69. package/tests/client/for.test.tsrx +120 -127
  70. package/tests/client/head.test.tsrx +74 -48
  71. package/tests/client/html.test.tsrx +37 -49
  72. package/tests/client/input-value.test.tsrx +1125 -1354
  73. package/tests/client/lazy-array.test.tsrx +10 -16
  74. package/tests/client/lazy-destructuring.test.tsrx +169 -221
  75. package/tests/client/map.test.tsrx +39 -41
  76. package/tests/client/media-query.test.tsrx +15 -19
  77. package/tests/client/object.test.tsrx +46 -56
  78. package/tests/client/portal.test.tsrx +31 -37
  79. package/tests/client/ref.test.tsrx +173 -193
  80. package/tests/client/return.test.tsrx +62 -37
  81. package/tests/client/set.test.tsrx +33 -33
  82. package/tests/client/svg.test.tsrx +197 -216
  83. package/tests/client/switch.test.tsrx +201 -191
  84. package/tests/client/track-async-hydration.test.tsrx +14 -18
  85. package/tests/client/tracked-index-access.test.tsrx +18 -28
  86. package/tests/client/try.test.tsrx +494 -619
  87. package/tests/client/tsx.test.tsrx +286 -292
  88. package/tests/client/typescript-generics.test.tsrx +121 -129
  89. package/tests/client/url/url.derived.test.tsrx +21 -25
  90. package/tests/client/url/url.parsing.test.tsrx +35 -35
  91. package/tests/client/url/url.partial-removal.test.tsrx +32 -32
  92. package/tests/client/url/url.reactivity.test.tsrx +68 -72
  93. package/tests/client/url/url.serialization.test.tsrx +8 -8
  94. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +21 -27
  95. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +16 -16
  96. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +37 -37
  97. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +56 -60
  98. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +32 -34
  99. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +9 -9
  100. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +10 -10
  101. package/tests/hydration/compiled/client/basic.js +390 -319
  102. package/tests/hydration/compiled/client/composite.js +52 -44
  103. package/tests/hydration/compiled/client/for.js +734 -604
  104. package/tests/hydration/compiled/client/head.js +183 -103
  105. package/tests/hydration/compiled/client/html.js +93 -86
  106. package/tests/hydration/compiled/client/if-children.js +95 -71
  107. package/tests/hydration/compiled/client/if.js +113 -89
  108. package/tests/hydration/compiled/client/mixed-control-flow.js +225 -209
  109. package/tests/hydration/compiled/client/nested-control-flow.js +94 -98
  110. package/tests/hydration/compiled/client/reactivity.js +26 -24
  111. package/tests/hydration/compiled/client/return.js +8 -42
  112. package/tests/hydration/compiled/client/switch.js +208 -173
  113. package/tests/hydration/compiled/client/track-async-serialization.js +176 -128
  114. package/tests/hydration/compiled/client/try.js +29 -21
  115. package/tests/hydration/compiled/server/basic.js +210 -221
  116. package/tests/hydration/compiled/server/composite.js +13 -14
  117. package/tests/hydration/compiled/server/for.js +427 -444
  118. package/tests/hydration/compiled/server/head.js +199 -189
  119. package/tests/hydration/compiled/server/html.js +33 -41
  120. package/tests/hydration/compiled/server/if-children.js +114 -117
  121. package/tests/hydration/compiled/server/if.js +77 -83
  122. package/tests/hydration/compiled/server/mixed-control-flow.js +145 -150
  123. package/tests/hydration/compiled/server/nested-control-flow.js +10 -0
  124. package/tests/hydration/compiled/server/reactivity.js +24 -22
  125. package/tests/hydration/compiled/server/return.js +6 -18
  126. package/tests/hydration/compiled/server/switch.js +179 -176
  127. package/tests/hydration/compiled/server/track-async-serialization.js +88 -70
  128. package/tests/hydration/compiled/server/try.js +31 -35
  129. package/tests/hydration/components/basic.tsrx +216 -258
  130. package/tests/hydration/components/composite.tsrx +32 -42
  131. package/tests/hydration/components/events.tsrx +81 -101
  132. package/tests/hydration/components/for.tsrx +270 -336
  133. package/tests/hydration/components/head.tsrx +43 -39
  134. package/tests/hydration/components/hmr.tsrx +16 -22
  135. package/tests/hydration/components/html-in-template.tsrx +15 -21
  136. package/tests/hydration/components/html.tsrx +442 -526
  137. package/tests/hydration/components/if-children.tsrx +107 -125
  138. package/tests/hydration/components/if.tsrx +68 -90
  139. package/tests/hydration/components/mixed-control-flow.tsrx +65 -72
  140. package/tests/hydration/components/nested-control-flow.tsrx +202 -216
  141. package/tests/hydration/components/portal.tsrx +33 -41
  142. package/tests/hydration/components/reactivity.tsrx +26 -34
  143. package/tests/hydration/components/return.tsrx +4 -6
  144. package/tests/hydration/components/switch.tsrx +73 -78
  145. package/tests/hydration/components/track-async-serialization.tsrx +83 -93
  146. package/tests/hydration/components/try.tsrx +37 -51
  147. package/tests/hydration/switch.test.js +8 -8
  148. package/tests/server/await.test.tsrx +3 -3
  149. package/tests/server/basic.attributes.test.tsrx +117 -162
  150. package/tests/server/basic.components.test.tsrx +164 -194
  151. package/tests/server/basic.test.tsrx +299 -199
  152. package/tests/server/compiler.test.tsrx +142 -72
  153. package/tests/server/composite.props.test.tsrx +54 -58
  154. package/tests/server/composite.test.tsrx +165 -167
  155. package/tests/server/context.test.tsrx +13 -17
  156. package/tests/server/dynamic-elements.test.tsrx +147 -148
  157. package/tests/server/for.test.tsrx +115 -84
  158. package/tests/server/head.test.tsrx +54 -31
  159. package/tests/server/html-nesting-validation.test.tsrx +16 -8
  160. package/tests/server/if.test.tsrx +49 -59
  161. package/tests/server/lazy-destructuring.test.tsrx +288 -366
  162. package/tests/server/return.test.tsrx +58 -36
  163. package/tests/server/streaming-ssr.test.tsrx +4 -4
  164. package/tests/server/style-identifier.test.tsrx +61 -69
  165. package/tests/server/switch.test.tsrx +89 -97
  166. package/tests/server/track-async-serialization.test.tsrx +85 -103
  167. package/tests/server/try.test.tsrx +275 -360
  168. package/tests/utils/ref-types.test.js +72 -0
  169. package/tests/utils/vite-plugin-config.test.js +41 -74
  170. package/types/index.d.ts +29 -4
  171. package/src/runtime/internal/client/compat.js +0 -40
  172. package/tests/utils/compiler-compat-config.test.js +0 -38
@@ -2,304 +2,268 @@ import { track } from 'ripple';
2
2
 
3
3
  // For loop components for hydration testing
4
4
 
5
- export function StaticForLoop() {
6
- return <>
7
- const items = ['Apple', 'Banana', 'Cherry'];
8
- <ul>
9
- for (const item of items) {
10
- <li>{item}</li>
11
- }
12
- </ul>
13
- </>;
5
+ export function StaticForLoop() @{
6
+ const items = ['Apple', 'Banana', 'Cherry'];
7
+ <ul>
8
+ @for (const item of items) {
9
+ <li>{item}</li>
10
+ }
11
+ </ul>
14
12
  }
15
13
 
16
- export function ForLoopWithIndex() {
17
- return <>
18
- const items = ['A', 'B', 'C'];
19
- <ul>
20
- for (const item of items; index i) {
21
- <li>{`${i}: ${item}`}</li>
22
- }
23
- </ul>
24
- </>;
14
+ export function ForLoopWithIndex() @{
15
+ const items = ['A', 'B', 'C'];
16
+ <ul>
17
+ @for (const item of items; index i) {
18
+ <li>{`${i}: ${item}`}</li>
19
+ }
20
+ </ul>
25
21
  }
26
22
 
27
- export function KeyedForLoop() {
28
- return <>
29
- const items = [
30
- { id: 1, name: 'First' },
31
- { id: 2, name: 'Second' },
32
- { id: 3, name: 'Third' },
33
- ];
34
- <ul>
35
- for (const item of items; key item.id) {
36
- <li>{item.name}</li>
37
- }
38
- </ul>
39
- </>;
23
+ export function KeyedForLoop() @{
24
+ const items = [
25
+ { id: 1, name: 'First' },
26
+ { id: 2, name: 'Second' },
27
+ { id: 3, name: 'Third' },
28
+ ];
29
+ <ul>
30
+ @for (const item of items; key item.id) {
31
+ <li>{item.name}</li>
32
+ }
33
+ </ul>
40
34
  }
41
35
 
42
- export function ReactiveForLoopAdd() {
43
- return <>
44
- let &[items] = track(['A', 'B']);
36
+ export function ReactiveForLoopAdd() @{
37
+ let &[items] = track(['A', 'B']);
38
+ <>
45
39
  <button
46
40
  class="add"
47
41
  onClick={() => {
48
42
  items = [...items, 'C'];
49
43
  }}
50
- >
51
- {'Add'}
52
- </button>
44
+ >{'Add'}</button>
53
45
  <ul>
54
- for (const item of items) {
46
+ @for (const item of items) {
55
47
  <li>{item}</li>
56
48
  }
57
49
  </ul>
58
- </>;
50
+ </>
59
51
  }
60
52
 
61
- export function ReactiveForLoopRemove() {
62
- return <>
63
- let &[items] = track(['A', 'B', 'C']);
53
+ export function ReactiveForLoopRemove() @{
54
+ let &[items] = track(['A', 'B', 'C']);
55
+ <>
64
56
  <button
65
57
  class="remove"
66
58
  onClick={() => {
67
59
  items = items.slice(0, -1);
68
60
  }}
69
- >
70
- {'Remove'}
71
- </button>
61
+ >{'Remove'}</button>
72
62
  <ul>
73
- for (const item of items) {
63
+ @for (const item of items) {
74
64
  <li>{item}</li>
75
65
  }
76
66
  </ul>
77
- </>;
67
+ </>
78
68
  }
79
69
 
80
- export function ForLoopInteractive() {
81
- return <>
82
- let &[counts] = track([0, 0, 0]);
83
- <div>
84
- for (const count of counts; index i) {
85
- <div class={`item-${i}`}>
86
- <span class="value">{count}</span>
87
- <button
88
- class="increment"
89
- onClick={() => {
90
- const newCounts = [...counts];
91
- newCounts[i]++;
92
- counts = newCounts;
93
- }}
94
- >
95
- {'+'}
96
- </button>
97
- </div>
98
- }
99
- </div>
100
- </>;
70
+ export function ForLoopInteractive() @{
71
+ let &[counts] = track([0, 0, 0]);
72
+ <div>
73
+ @for (const count of counts; index i) {
74
+ <div class={`item-${i}`}>
75
+ <span class="value">{count}</span>
76
+ <button
77
+ class="increment"
78
+ onClick={() => {
79
+ const newCounts = [...counts];
80
+ newCounts[i]++;
81
+ counts = newCounts;
82
+ }}
83
+ >{'+'}</button>
84
+ </div>
85
+ }
86
+ </div>
101
87
  }
102
88
 
103
- export function NestedForLoop() {
104
- return <>
105
- const grid = [
106
- [1, 2],
107
- [3, 4],
108
- ];
109
- <div class="grid">
110
- for (const row of grid; index rowIndex) {
111
- <div class={`row-${rowIndex}`}>
112
- for (const cell of row; index colIndex) {
113
- <span class={`cell-${rowIndex}-${colIndex}`}>{cell}</span>
114
- }
115
- </div>
116
- }
117
- </div>
118
- </>;
89
+ export function NestedForLoop() @{
90
+ const grid = [
91
+ [1, 2],
92
+ [3, 4],
93
+ ];
94
+ <div class="grid">
95
+ @for (const row of grid; index rowIndex) {
96
+ <div class={`row-${rowIndex}`}>
97
+ @for (const cell of row; index colIndex) {
98
+ <span class={`cell-${rowIndex}-${colIndex}`}>{cell}</span>
99
+ }
100
+ </div>
101
+ }
102
+ </div>
119
103
  }
120
104
 
121
- export function EmptyForLoop() {
122
- return <>
123
- const items: string[] = [];
124
- <div class="container">
125
- for (const item of items) {
126
- <span>{item}</span>
127
- }
128
- </div>
129
- </>;
105
+ export function EmptyForLoop() @{
106
+ const items: string[] = [];
107
+ <div class="container">
108
+ @for (const item of items) {
109
+ <span>{item}</span>
110
+ }
111
+ </div>
130
112
  }
131
113
 
132
- export function ForLoopComplexObjects() {
133
- return <>
134
- const users = [
135
- { id: 1, name: 'Alice', role: 'Admin' },
136
- { id: 2, name: 'Bob', role: 'User' },
137
- ];
138
- <div>
139
- for (const user of users; key user.id) {
140
- <div class={`user-${user.id}`}>
141
- <span class="name">{user.name}</span>
142
- <span class="role">{user.role}</span>
143
- </div>
144
- }
145
- </div>
146
- </>;
114
+ export function ForLoopComplexObjects() @{
115
+ const users = [
116
+ { id: 1, name: 'Alice', role: 'Admin' },
117
+ { id: 2, name: 'Bob', role: 'User' },
118
+ ];
119
+ <div>
120
+ @for (const user of users; key user.id) {
121
+ <div class={`user-${user.id}`}>
122
+ <span class="name">{user.name}</span>
123
+ <span class="role">{user.role}</span>
124
+ </div>
125
+ }
126
+ </div>
147
127
  }
148
128
 
149
129
  // Test reordering items in a keyed for loop
150
- export function KeyedForLoopReorder() {
151
- return <>
152
- let &[items] = track([
153
- { id: 1, name: 'First' },
154
- { id: 2, name: 'Second' },
155
- { id: 3, name: 'Third' },
156
- ]);
130
+ export function KeyedForLoopReorder() @{
131
+ let &[items] = track([
132
+ { id: 1, name: 'First' },
133
+ { id: 2, name: 'Second' },
134
+ { id: 3, name: 'Third' },
135
+ ]);
136
+ <>
157
137
  <button
158
138
  class="reorder"
159
139
  onClick={() => {
160
140
  items = [items[2], items[0], items[1]];
161
141
  }}
162
- >
163
- {'Reorder'}
164
- </button>
142
+ >{'Reorder'}</button>
165
143
  <ul>
166
- for (const item of items; key item.id) {
144
+ @for (const item of items; key item.id) {
167
145
  <li class={`item-${item.id}`}>{item.name}</li>
168
146
  }
169
147
  </ul>
170
- </>;
148
+ </>
171
149
  }
172
150
 
173
151
  // Test for loop with item property updates (keyed)
174
- export function KeyedForLoopUpdate() {
175
- return <>
176
- let &[items] = track([
177
- { id: 1, name: 'Item 1' },
178
- { id: 2, name: 'Item 2' },
179
- ]);
152
+ export function KeyedForLoopUpdate() @{
153
+ let &[items] = track([
154
+ { id: 1, name: 'Item 1' },
155
+ { id: 2, name: 'Item 2' },
156
+ ]);
157
+ <>
180
158
  <button
181
159
  class="update"
182
160
  onClick={() => {
183
161
  items = items.map((item) => (item.id === 1 ? { ...item, name: 'Updated' } : item));
184
162
  }}
185
- >
186
- {'Update'}
187
- </button>
163
+ >{'Update'}</button>
188
164
  <ul>
189
- for (const item of items; key item.id) {
165
+ @for (const item of items; key item.id) {
190
166
  <li class={`item-${item.id}`}>{item.name}</li>
191
167
  }
192
168
  </ul>
193
- </>;
169
+ </>
194
170
  }
195
171
 
196
172
  // Test for loop with combined add/remove/reorder
197
- export function ForLoopMixedOperations() {
198
- return <>
199
- let &[items] = track(['A', 'B', 'C', 'D']);
173
+ export function ForLoopMixedOperations() @{
174
+ let &[items] = track(['A', 'B', 'C', 'D']);
175
+ <>
200
176
  <button
201
177
  class="shuffle"
202
178
  onClick={() => {
203
179
  // Remove B, add E, reorder to D, C, A, E
204
180
  items = ['D', 'C', 'A', 'E'];
205
181
  }}
206
- >
207
- {'Shuffle'}
208
- </button>
182
+ >{'Shuffle'}</button>
209
183
  <ul>
210
- for (const item of items) {
184
+ @for (const item of items) {
211
185
  <li class={`item-${item}`}>{item}</li>
212
186
  }
213
187
  </ul>
214
- </>;
188
+ </>
215
189
  }
216
190
 
217
191
  // Test for loop inside if block (combined control flow)
218
- export function ForLoopInsideIf() {
219
- return <>
220
- let &[showList] = track(true);
221
- let &[items] = track(['X', 'Y', 'Z']);
192
+ export function ForLoopInsideIf() @{
193
+ let &[showList] = track(true);
194
+ let &[items] = track(['X', 'Y', 'Z']);
195
+ <>
222
196
  <button
223
197
  class="toggle"
224
198
  onClick={() => {
225
199
  showList = !showList;
226
200
  }}
227
- >
228
- {'Toggle List'}
229
- </button>
201
+ >{'Toggle List'}</button>
230
202
  <button
231
203
  class="add"
232
204
  onClick={() => {
233
205
  items = [...items, 'W'];
234
206
  }}
235
- >
236
- {'Add Item'}
237
- </button>
238
- if (showList) {
207
+ >{'Add Item'}</button>
208
+ @if (showList) {
239
209
  <ul class="list">
240
- for (const item of items) {
210
+ @for (const item of items) {
241
211
  <li>{item}</li>
242
212
  }
243
213
  </ul>
244
214
  }
245
- </>;
215
+ </>
246
216
  }
247
217
 
248
218
  // Test for loop that transitions from empty to populated
249
- export function ForLoopEmptyToPopulated() {
250
- return <>
251
- let &[items] = track<string[]>([]);
219
+ export function ForLoopEmptyToPopulated() @{
220
+ let &[items] = track<string[]>([]);
221
+ <>
252
222
  <button
253
223
  class="populate"
254
224
  onClick={() => {
255
225
  items = ['One', 'Two', 'Three'];
256
226
  }}
257
- >
258
- {'Populate'}
259
- </button>
227
+ >{'Populate'}</button>
260
228
  <ul class="list">
261
- for (const item of items) {
229
+ @for (const item of items) {
262
230
  <li>{item}</li>
263
231
  }
264
232
  </ul>
265
- </>;
233
+ </>
266
234
  }
267
235
 
268
236
  // Test for loop that transitions from populated to empty
269
- export function ForLoopPopulatedToEmpty() {
270
- return <>
271
- let &[items] = track(['One', 'Two', 'Three']);
237
+ export function ForLoopPopulatedToEmpty() @{
238
+ let &[items] = track(['One', 'Two', 'Three']);
239
+ <>
272
240
  <button
273
241
  class="clear"
274
242
  onClick={() => {
275
243
  items = [];
276
244
  }}
277
- >
278
- {'Clear'}
279
- </button>
245
+ >{'Clear'}</button>
280
246
  <ul class="list">
281
- for (const item of items) {
247
+ @for (const item of items) {
282
248
  <li>{item}</li>
283
249
  }
284
250
  </ul>
285
- </>;
251
+ </>
286
252
  }
287
253
 
288
254
  // Test nested for loops with reactivity
289
- export function NestedForLoopReactive() {
290
- return <>
291
- let &[grid] = track([
292
- [1, 2],
293
- [3, 4],
294
- ]);
255
+ export function NestedForLoopReactive() @{
256
+ let &[grid] = track([
257
+ [1, 2],
258
+ [3, 4],
259
+ ]);
260
+ <div class="nested-for-reactive">
295
261
  <button
296
262
  class="add-row"
297
263
  onClick={() => {
298
264
  grid = [...grid, [5, 6]];
299
265
  }}
300
- >
301
- {'Add Row'}
302
- </button>
266
+ >{'Add Row'}</button>
303
267
  <button
304
268
  class="update-cell"
305
269
  onClick={() => {
@@ -307,112 +271,104 @@ export function NestedForLoopReactive() {
307
271
  newGrid[0][0] = 99;
308
272
  grid = newGrid;
309
273
  }}
310
- >
311
- {'Update Cell'}
312
- </button>
274
+ >{'Update Cell'}</button>
313
275
  <div class="grid">
314
- for (const row of grid; index rowIndex) {
276
+ @for (const row of grid; index rowIndex) {
315
277
  <div class={`row-${rowIndex}`}>
316
- for (const cell of row; index colIndex) {
278
+ @for (const cell of row; index colIndex) {
317
279
  <span class={`cell-${rowIndex}-${colIndex}`}>{cell}</span>
318
280
  }
319
281
  </div>
320
282
  }
321
283
  </div>
322
- </>;
284
+ </div>
323
285
  }
324
286
 
325
287
  // Test for loop with deeply nested data
326
- export function ForLoopDeeplyNested() {
327
- return <>
328
- const departments = [
329
- {
330
- id: 'd1',
331
- name: 'Engineering',
332
- teams: [
333
- { id: 't1', name: 'Frontend', members: ['Alice', 'Bob'] },
334
- { id: 't2', name: 'Backend', members: ['Charlie'] },
335
- ],
336
- },
337
- {
338
- id: 'd2',
339
- name: 'Design',
340
- teams: [
341
- { id: 't3', name: 'UX', members: ['Diana', 'Eve', 'Frank'] },
342
- ],
343
- },
344
- ];
345
- <div class="org">
346
- for (const dept of departments; key dept.id) {
347
- <div class={`dept-${dept.id}`}>
348
- <h2 class="dept-name">{dept.name}</h2>
349
- for (const team of dept.teams; key team.id) {
350
- <div class={`team-${team.id}`}>
351
- <h3 class="team-name">{team.name}</h3>
352
- <ul>
353
- for (const member of team.members) {
354
- <li class="member">{member}</li>
355
- }
356
- </ul>
357
- </div>
358
- }
359
- </div>
360
- }
361
- </div>
362
- </>;
288
+ export function ForLoopDeeplyNested() @{
289
+ const departments = [
290
+ {
291
+ id: 'd1',
292
+ name: 'Engineering',
293
+ teams: [
294
+ { id: 't1', name: 'Frontend', members: ['Alice', 'Bob'] },
295
+ { id: 't2', name: 'Backend', members: ['Charlie'] },
296
+ ],
297
+ },
298
+ {
299
+ id: 'd2',
300
+ name: 'Design',
301
+ teams: [
302
+ { id: 't3', name: 'UX', members: ['Diana', 'Eve', 'Frank'] },
303
+ ],
304
+ },
305
+ ];
306
+ <div class="org">
307
+ @for (const dept of departments; key dept.id) {
308
+ <div class={`dept-${dept.id}`}>
309
+ <h2 class="dept-name">{dept.name}</h2>
310
+ @for (const team of dept.teams; key team.id) {
311
+ <div class={`team-${team.id}`}>
312
+ <h3 class="team-name">{team.name}</h3>
313
+ <ul>
314
+ @for (const member of team.members) {
315
+ <li class="member">{member}</li>
316
+ }
317
+ </ul>
318
+ </div>
319
+ }
320
+ </div>
321
+ }
322
+ </div>
363
323
  }
364
324
 
365
325
  // Test for loop with index that gets updated
366
- export function ForLoopIndexUpdate() {
367
- return <>
368
- let &[items] = track(['First', 'Second', 'Third']);
326
+ export function ForLoopIndexUpdate() @{
327
+ let &[items] = track(['First', 'Second', 'Third']);
328
+ <>
369
329
  <button
370
330
  class="prepend"
371
331
  onClick={() => {
372
332
  items = ['Zeroth', ...items];
373
333
  }}
374
- >
375
- {'Prepend'}
376
- </button>
334
+ >{'Prepend'}</button>
377
335
  <ul>
378
- for (const item of items; index i) {
336
+ @for (const item of items; index i) {
379
337
  <li class={`item-${i}`}>{`[${i}] ${item}`}</li>
380
338
  }
381
339
  </ul>
382
- </>;
340
+ </>
383
341
  }
384
342
 
385
343
  // Test keyed for loop with index
386
- export function KeyedForLoopWithIndex() {
387
- return <>
388
- let &[items] = track([
389
- { id: 'a', value: 'Alpha' },
390
- { id: 'b', value: 'Beta' },
391
- { id: 'c', value: 'Gamma' },
392
- ]);
344
+ export function KeyedForLoopWithIndex() @{
345
+ let &[items] = track([
346
+ { id: 'a', value: 'Alpha' },
347
+ { id: 'b', value: 'Beta' },
348
+ { id: 'c', value: 'Gamma' },
349
+ ]);
350
+ <>
393
351
  <button
394
352
  class="reorder"
395
353
  onClick={() => {
396
354
  items = [items[1], items[2], items[0]];
397
355
  }}
398
- >
399
- {'Rotate'}
400
- </button>
356
+ >{'Rotate'}</button>
401
357
  <ul>
402
- for (const item of items; index i; key item.id) {
358
+ @for (const item of items; index i; key item.id) {
403
359
  <li class={`item-${item.id}`} data-index={i}>{`[${i}] ${item.id}: ${item.value}`}</li>
404
360
  }
405
361
  </ul>
406
- </>;
362
+ </>
407
363
  }
408
364
 
409
365
  // Test for loop with sibling elements
410
- export function ForLoopWithSiblings() {
411
- return <>
412
- let &[items] = track(['A', 'B']);
366
+ export function ForLoopWithSiblings() @{
367
+ let &[items] = track(['A', 'B']);
368
+ <>
413
369
  <div class="wrapper">
414
370
  <header class="before">{'Before'}</header>
415
- for (const item of items) {
371
+ @for (const item of items) {
416
372
  <div class={`item-${item}`}>{item}</div>
417
373
  }
418
374
  <footer class="after">{'After'}</footer>
@@ -422,83 +378,71 @@ export function ForLoopWithSiblings() {
422
378
  onClick={() => {
423
379
  items = [...items, 'C'];
424
380
  }}
425
- >
426
- {'Add'}
427
- </button>
428
- </>;
381
+ >{'Add'}</button>
382
+ </>
429
383
  }
430
384
 
431
385
  // Test for loop items with their own reactive state
432
- export function ForLoopItemState() {
433
- return <>
434
- const initialItems = [
435
- { id: 1, text: 'Todo 1' },
436
- { id: 2, text: 'Todo 2' },
437
- { id: 3, text: 'Todo 3' },
438
- ];
439
- <div>
440
- for (const item of initialItems; key item.id) {
441
- <TodoItem id={item.id} text={item.text} />
442
- }
443
- </div>
444
- </>;
386
+ export function ForLoopItemState() @{
387
+ const initialItems = [
388
+ { id: 1, text: 'Todo 1' },
389
+ { id: 2, text: 'Todo 2' },
390
+ { id: 3, text: 'Todo 3' },
391
+ ];
392
+ <div>
393
+ @for (const item of initialItems; key item.id) {
394
+ <TodoItem id={item.id} text={item.text} />
395
+ }
396
+ </div>
445
397
  }
446
398
 
447
- function TodoItem(props: { id: number; text: string }) {
448
- return <>
449
- let &[done] = track(false);
450
- <div class={`todo-${props.id}`}>
451
- <input
452
- type="checkbox"
453
- class="checkbox"
454
- checked={done}
455
- onChange={(e) => {
456
- done = (e.target as HTMLInputElement).checked;
457
- }}
458
- />
459
- <span class={done ? 'completed' : 'pending'}>
460
- {props.text}
461
- </span>
462
- </div>
463
- </>;
399
+ function TodoItem(props: { id: number; text: string }) @{
400
+ let &[done] = track(false);
401
+ <div class={`todo-${props.id}`}>
402
+ <input
403
+ type="checkbox"
404
+ class="checkbox"
405
+ checked={done}
406
+ onChange={(e) => {
407
+ done = (e.target as HTMLInputElement).checked;
408
+ }}
409
+ />
410
+ <span class={done ? 'completed' : 'pending'}>{props.text}</span>
411
+ </div>
464
412
  }
465
413
 
466
414
  // Test for loop with single item
467
- export function ForLoopSingleItem() {
468
- return <>
469
- const items = ['Only'];
470
- <ul>
471
- for (const item of items) {
472
- <li class="single">{item}</li>
473
- }
474
- </ul>
475
- </>;
415
+ export function ForLoopSingleItem() @{
416
+ const items = ['Only'];
417
+ <ul>
418
+ @for (const item of items) {
419
+ <li class="single">{item}</li>
420
+ }
421
+ </ul>
476
422
  }
477
423
 
478
424
  // Test for loop adding at beginning
479
- export function ForLoopAddAtBeginning() {
480
- return <>
481
- let &[items] = track(['B', 'C']);
425
+ export function ForLoopAddAtBeginning() @{
426
+ let &[items] = track(['B', 'C']);
427
+ <>
482
428
  <button
483
429
  class="prepend"
484
430
  onClick={() => {
485
431
  items = ['A', ...items];
486
432
  }}
487
- >
488
- {'Prepend A'}
489
- </button>
433
+ >{'Prepend A'}</button>
490
434
  <ul>
491
- for (const item of items) {
435
+ @for (const item of items) {
492
436
  <li class={`item-${item}`}>{item}</li>
493
437
  }
494
438
  </ul>
495
- </>;
439
+ </>
496
440
  }
497
441
 
498
442
  // Test for loop adding in the middle
499
- export function ForLoopAddInMiddle() {
500
- return <>
501
- let &[items] = track(['A', 'C']);
443
+ export function ForLoopAddInMiddle() @{
444
+ let &[items] = track(['A', 'C']);
445
+ <>
502
446
  <button
503
447
  class="insert"
504
448
  onClick={() => {
@@ -506,53 +450,47 @@ export function ForLoopAddInMiddle() {
506
450
  copy.splice(1, 0, 'B');
507
451
  items = copy;
508
452
  }}
509
- >
510
- {'Insert B'}
511
- </button>
453
+ >{'Insert B'}</button>
512
454
  <ul>
513
- for (const item of items) {
455
+ @for (const item of items) {
514
456
  <li class={`item-${item}`}>{item}</li>
515
457
  }
516
458
  </ul>
517
- </>;
459
+ </>
518
460
  }
519
461
 
520
462
  // Test for loop removing from the middle
521
- export function ForLoopRemoveFromMiddle() {
522
- return <>
523
- let &[items] = track(['A', 'B', 'C']);
463
+ export function ForLoopRemoveFromMiddle() @{
464
+ let &[items] = track(['A', 'B', 'C']);
465
+ <>
524
466
  <button
525
467
  class="remove-middle"
526
468
  onClick={() => {
527
469
  items = items.filter((item) => item !== 'B');
528
470
  }}
529
- >
530
- {'Remove B'}
531
- </button>
471
+ >{'Remove B'}</button>
532
472
  <ul>
533
- for (const item of items) {
473
+ @for (const item of items) {
534
474
  <li class={`item-${item}`}>{item}</li>
535
475
  }
536
476
  </ul>
537
- </>;
477
+ </>
538
478
  }
539
479
 
540
480
  // Test for loop with large list
541
- export function ForLoopLargeList() {
542
- return <>
543
- const items = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`);
544
- <ul class="large-list">
545
- for (const item of items; index i) {
546
- <li class={`item-${i}`}>{item}</li>
547
- }
548
- </ul>
549
- </>;
481
+ export function ForLoopLargeList() @{
482
+ const items = Array.from({ length: 50 }, (_, i) => `Item ${i + 1}`);
483
+ <ul class="large-list">
484
+ @for (const item of items; index i) {
485
+ <li class={`item-${i}`}>{item}</li>
486
+ }
487
+ </ul>
550
488
  }
551
489
 
552
490
  // Test for loop with swap operation
553
- export function ForLoopSwap() {
554
- return <>
555
- let &[items] = track(['A', 'B', 'C', 'D']);
491
+ export function ForLoopSwap() @{
492
+ let &[items] = track(['A', 'B', 'C', 'D']);
493
+ <>
556
494
  <button
557
495
  class="swap"
558
496
  onClick={() => {
@@ -560,33 +498,29 @@ export function ForLoopSwap() {
560
498
  [copy[0], copy[3]] = [copy[3], copy[0]];
561
499
  items = copy;
562
500
  }}
563
- >
564
- {'Swap First and Last'}
565
- </button>
501
+ >{'Swap First and Last'}</button>
566
502
  <ul>
567
- for (const item of items) {
503
+ @for (const item of items) {
568
504
  <li class={`item-${item}`}>{item}</li>
569
505
  }
570
506
  </ul>
571
- </>;
507
+ </>
572
508
  }
573
509
 
574
510
  // Test for loop with reverse operation
575
- export function ForLoopReverse() {
576
- return <>
577
- let &[items] = track(['A', 'B', 'C', 'D']);
511
+ export function ForLoopReverse() @{
512
+ let &[items] = track(['A', 'B', 'C', 'D']);
513
+ <>
578
514
  <button
579
515
  class="reverse"
580
516
  onClick={() => {
581
517
  items = [...items].reverse();
582
518
  }}
583
- >
584
- {'Reverse'}
585
- </button>
519
+ >{'Reverse'}</button>
586
520
  <ul>
587
- for (const item of items) {
521
+ @for (const item of items) {
588
522
  <li class={`item-${item}`}>{item}</li>
589
523
  }
590
524
  </ul>
591
- </>;
525
+ </>
592
526
  }