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
@@ -1,12 +1,10 @@
1
1
  describe('for statements in SSR', () => {
2
2
  it('renders a simple static array', async () => {
3
- function App() {
4
- return <>
5
- const items = ['Item 1', 'Item 2', 'Item 3'];
6
- for (const item of items) {
7
- <div class={item}>{item}</div>
8
- }
9
- </>;
3
+ function App() @{
4
+ const items = ['Item 1', 'Item 2', 'Item 3'];
5
+ @for (const item of items) {
6
+ <div class={item}>{item}</div>
7
+ }
10
8
  }
11
9
 
12
10
  const { body } = await render(App);
@@ -15,28 +13,43 @@ describe('for statements in SSR', () => {
15
13
  );
16
14
  });
17
15
 
16
+ it('allows plain JavaScript control flow in setup when rendered output follows', () => {
17
+ expect(
18
+ () => compile(
19
+ `function App() @{
20
+ const items = [1, 2, 3];
21
+ for (const item of items) {
22
+ <div class="selected">{item}</div>
23
+ }
24
+ }`,
25
+ 'App.tsrx',
26
+ { mode: 'server' },
27
+ ),
28
+ ).not.toThrow();
29
+ });
30
+
18
31
  it('renders nested for...of loops', async () => {
19
- function App() {
20
- return <>
21
- const groups = [
22
- {
23
- name: 'Group 1',
24
- items: ['Item 1.1', 'Item 1.2'],
25
- },
26
- {
27
- name: 'Group 2',
28
- items: ['Item 2.1', 'Item 2.2'],
29
- },
30
- ];
31
- for (const group of groups) {
32
+ function App() @{
33
+ const groups = [
34
+ {
35
+ name: 'Group 1',
36
+ items: ['Item 1.1', 'Item 1.2'],
37
+ },
38
+ {
39
+ name: 'Group 2',
40
+ items: ['Item 2.1', 'Item 2.2'],
41
+ },
42
+ ];
43
+ @for (const group of groups) {
44
+ <>
32
45
  <h1>{group.name}</h1>
33
46
  <ul>
34
- for (const item of group.items) {
47
+ @for (const item of group.items) {
35
48
  <li>{item}</li>
36
49
  }
37
50
  </ul>
38
- }
39
- </>;
51
+ </>
52
+ }
40
53
  }
41
54
 
42
55
  const { body } = await render(App);
@@ -46,13 +59,11 @@ describe('for statements in SSR', () => {
46
59
  });
47
60
 
48
61
  it('renders an array with index', async () => {
49
- function App() {
50
- return <>
51
- const items = ['Item 1', 'Item 2', 'Item 3'];
52
- for (const item of items; index i) {
53
- <div class={`${item} ${i}`}>{`${item} ${i}`}</div>
54
- }
55
- </>;
62
+ function App() @{
63
+ const items = ['Item 1', 'Item 2', 'Item 3'];
64
+ @for (const item of items; index i) {
65
+ <div class={`${item} ${i}`}>{`${item} ${i}`}</div>
66
+ }
56
67
  }
57
68
 
58
69
  const { body } = await render(App);
@@ -62,17 +73,15 @@ describe('for statements in SSR', () => {
62
73
  });
63
74
 
64
75
  it('renders an array with key', async () => {
65
- function App() {
66
- return <>
67
- const items = [
68
- { id: 1, name: 'Item' },
69
- { id: 2, name: 'Item' },
70
- { id: 3, name: 'Item' },
71
- ];
72
- for (const item of items; key item.id) {
73
- <div class={`${item.name} ${item.id}`}>{`${item.name} ${item.id}`}</div>
74
- }
75
- </>;
76
+ function App() @{
77
+ const items = [
78
+ { id: 1, name: 'Item' },
79
+ { id: 2, name: 'Item' },
80
+ { id: 3, name: 'Item' },
81
+ ];
82
+ @for (const item of items; key item.id) {
83
+ <div class={`${item.name} ${item.id}`}>{`${item.name} ${item.id}`}</div>
84
+ }
76
85
  }
77
86
 
78
87
  const { body } = await render(App);
@@ -82,17 +91,15 @@ describe('for statements in SSR', () => {
82
91
  });
83
92
 
84
93
  it('renders an array with index and key', async () => {
85
- function App() {
86
- return <>
87
- const items = [
88
- { id: 1, name: 'Item' },
89
- { id: 2, name: 'Item' },
90
- { id: 3, name: 'Item' },
91
- ];
92
- for (const item of items; index i; key item.id) {
93
- <div class={`${item.name} ${item.id} ${i}`}>{`${item.name} ${item.id} ${i}`}</div>
94
- }
95
- </>;
94
+ function App() @{
95
+ const items = [
96
+ { id: 1, name: 'Item' },
97
+ { id: 2, name: 'Item' },
98
+ { id: 3, name: 'Item' },
99
+ ];
100
+ @for (const item of items; index i; key item.id) {
101
+ <div class={`${item.name} ${item.id} ${i}`}>{`${item.name} ${item.id} ${i}`}</div>
102
+ }
96
103
  }
97
104
 
98
105
  const { body } = await render(App);
@@ -101,39 +108,48 @@ describe('for statements in SSR', () => {
101
108
  );
102
109
  });
103
110
 
104
- it('allows continue to skip a for...of iteration', async () => {
105
- function App() {
106
- return <>
107
- const items = ['Item 1', '', 'Item 3'];
108
- for (const item of items) {
109
- if (!item) continue;
110
- <div>{item}</div>
111
- }
112
- </>;
111
+ it('renders filtered items before passing them to @for', async () => {
112
+ function App() @{
113
+ const items = ['Item 1', '', 'Item 3'].filter(Boolean);
114
+ @for (const item of items) {
115
+ <div>{item}</div>
116
+ }
113
117
  }
114
118
 
115
119
  const { body } = await render(App);
116
120
  expect(body).toBeHtml('<div>Item 1</div><div>Item 3</div>');
117
121
  });
118
122
 
123
+ it('renders an empty fallback', async () => {
124
+ function App() @{
125
+ const items = [];
126
+ @for (const item of items) {
127
+ <div>{item}</div>
128
+ } @empty {
129
+ <p>No items</p>
130
+ }
131
+ }
132
+
133
+ const { body } = await render(App);
134
+ expect(body).toBeHtml('<p>No items</p>');
135
+ });
136
+
119
137
  it('allows ordinary function control flow inside for...of loops', async () => {
120
- function App() {
121
- return <>
122
- const items = ['Item 1', '', 'Item 3'];
123
- for (const item of items) {
124
- function label(value) {
125
- for (let i = 0; i < 1; i++) {
126
- while (i < 0) {
127
- break;
128
- }
129
- if (!value) return 'missing';
138
+ function App() @{
139
+ const items = ['Item 1', '', 'Item 3'];
140
+ @for (const item of items) {
141
+ function label(value) {
142
+ for (let i = 0; i < 1; i++) {
143
+ while (i < 0) {
144
+ break;
130
145
  }
131
- return value;
146
+ if (!value) return 'missing';
132
147
  }
133
-
134
- <div>{label(item)}</div>
148
+ return value;
135
149
  }
136
- </>;
150
+
151
+ <div>{label(item)}</div>
152
+ }
137
153
  }
138
154
 
139
155
  const { body } = await render(App);
@@ -143,30 +159,45 @@ describe('for statements in SSR', () => {
143
159
  it('throws for return statements inside for...of loops', () => {
144
160
  expect(
145
161
  () => compile(
146
- `function App(items) { return <>
147
- for (const item of items) {
148
- if (!item) return
162
+ `function App(items) @{
163
+ @for (const item of items) {
164
+ if (!item) return
149
165
  <div>{item}</div>
150
166
  }
151
- </>; }`,
167
+ }`,
152
168
  'App.tsrx',
153
169
  { mode: 'server' },
154
170
  ),
155
- ).toThrow('Return statements are not allowed inside TSRX templates.');
171
+ ).toThrow('Return statements are not allowed inside TSRX template for...of loops.');
156
172
  });
157
173
 
158
174
  it('throws for break statements targeting for...of loops', () => {
159
175
  expect(
160
176
  () => compile(
161
- `function App(items) { return <>
162
- for (const item of items) {
163
- if (!item) break
177
+ `function App(items) @{
178
+ @for (const item of items) {
179
+ if (!item) break
164
180
  <div>{item}</div>
165
181
  }
166
- </>; }`,
182
+ }`,
167
183
  'App.tsrx',
168
184
  { mode: 'server' },
169
185
  ),
170
186
  ).toThrow('Break statements are not allowed inside TSRX template for...of loops');
171
187
  });
188
+
189
+ it('throws for continue statements inside for...of loops', () => {
190
+ expect(
191
+ () => compile(
192
+ `function App(items) @{
193
+ @for (const item of items) {
194
+ if (!item) continue
195
+ <div>{item}</div>
196
+ }
197
+ }`,
198
+ 'App.tsrx',
199
+ { mode: 'server' },
200
+ ),
201
+ ).toThrow('Continue statements are not allowed inside TSRX template for...of loops');
202
+ });
172
203
  });
@@ -2,13 +2,13 @@ import { track } from 'ripple';
2
2
 
3
3
  describe('head elements', () => {
4
4
  it('renders static title element', async () => {
5
- function App() {
6
- return <>
5
+ function App() @{
6
+ <>
7
7
  <head>
8
8
  <title>{'Static Test Title'}</title>
9
9
  </head>
10
10
  <div>{'Content'}</div>
11
- </>;
11
+ </>
12
12
  }
13
13
 
14
14
  const { head, body } = await render(App);
@@ -19,16 +19,16 @@ describe('head elements', () => {
19
19
  });
20
20
 
21
21
  it('renders reactive title element', async () => {
22
- function App() {
23
- return <>
24
- let &[title] = track('Initial Title');
22
+ function App() @{
23
+ let &[title] = track('Initial Title');
24
+ <>
25
25
  <head>
26
26
  <title>{title}</title>
27
27
  </head>
28
28
  <div>
29
29
  <span>{title}</span>
30
30
  </div>
31
- </>;
31
+ </>
32
32
  }
33
33
 
34
34
  const { head, body } = await render(App);
@@ -39,13 +39,11 @@ describe('head elements', () => {
39
39
  });
40
40
 
41
41
  it('renders title with template literal', async () => {
42
- function App() {
43
- return <>
44
- let &[name] = track('World');
45
- <head>
46
- <title>{`Hello ${name}!`}</title>
47
- </head>
48
- </>;
42
+ function App() @{
43
+ let &[name] = track('World');
44
+ <head>
45
+ <title>{`Hello ${name}!`}</title>
46
+ </head>
49
47
  }
50
48
 
51
49
  const { head, body } = await render(App);
@@ -55,17 +53,19 @@ describe('head elements', () => {
55
53
  });
56
54
 
57
55
  it('renders title with computed value', async () => {
58
- function App() {
59
- return <>
60
- let &[count] = track(0);
61
- let prefix = 'Count: ';
56
+ function App() @{
57
+ let &[count] = track(0);
58
+ let prefix = 'Count: ';
59
+ <>
62
60
  <head>
63
- <title>{prefix + count}</title>
61
+ <title>
62
+ {prefix + count}
63
+ </title>
64
64
  </head>
65
65
  <div>
66
66
  <span>{count}</span>
67
67
  </div>
68
- </>;
68
+ </>
69
69
  }
70
70
 
71
71
  const { head, body } = await render(App);
@@ -75,13 +75,13 @@ describe('head elements', () => {
75
75
  });
76
76
 
77
77
  it('renders empty title', async () => {
78
- function App() {
79
- return <>
78
+ function App() @{
79
+ <>
80
80
  <head>
81
81
  <title>{''}</title>
82
82
  </head>
83
83
  <div>{'Empty title test'}</div>
84
- </>;
84
+ </>
85
85
  }
86
86
 
87
87
  const { head, body } = await render(App);
@@ -90,15 +90,38 @@ describe('head elements', () => {
90
90
  expect(document.title).toBe('');
91
91
  });
92
92
 
93
+ it('renders external scripts with src attributes from a loop', async () => {
94
+ const Head = ({ scripts }: { scripts: { src: string }[] }) => @{
95
+ <head>
96
+ @for (const script of scripts) {
97
+ <script src={script.src} />
98
+ }
99
+ </head>
100
+ };
101
+
102
+ function App() @{
103
+ <Head scripts={[{ src: '/a.js' }, { src: '/b.js' }]} />
104
+ }
105
+
106
+ const { head, body } = await render(App);
107
+ const { document } = parseAsFullHtml(head, body);
108
+
109
+ const srcs = Array.from(document.querySelectorAll('head script[src]')).map(
110
+ (node) => node.getAttribute('src'),
111
+ ).sort();
112
+
113
+ expect(srcs).toEqual(['/a.js', '/b.js']);
114
+ });
115
+
93
116
  it('renders title with conditional content', async () => {
94
- function App() {
95
- return <>
96
- let &[showPrefix] = track(true);
97
- let &[title] = track('Main Page');
98
- <head>
99
- <title>{showPrefix ? 'App - ' + title : title}</title>
100
- </head>
101
- </>;
117
+ function App() @{
118
+ let &[showPrefix] = track(true);
119
+ let &[title] = track('Main Page');
120
+ <head>
121
+ <title>
122
+ {showPrefix ? 'App - ' + title : title}
123
+ </title>
124
+ </head>
102
125
  }
103
126
 
104
127
  const { head, body } = await render(App);
@@ -180,11 +180,13 @@ describe('HTML nesting validation', () => {
180
180
  describe('compiler dev mode output', () => {
181
181
  it('emits push_element and pop_element calls in dev mode', () => {
182
182
  const lines = [];
183
- lines.push('function App() { return <>');
183
+ lines.push('function App() @{');
184
+ lines.push(' <>');
184
185
  lines.push(' <div>');
185
186
  lines.push(' <span>{"Hello"}</span>');
186
187
  lines.push(' </div>');
187
- lines.push('</>; }');
188
+ lines.push(' </>');
189
+ lines.push('}');
188
190
  const source = lines.join('\n');
189
191
 
190
192
  const result = compile(source, 'test.tsrx', { mode: 'server', dev: true });
@@ -195,11 +197,13 @@ describe('HTML nesting validation', () => {
195
197
 
196
198
  it('does not emit push_element or pop_element in non-dev mode', () => {
197
199
  const lines = [];
198
- lines.push('function App() { return <>');
200
+ lines.push('function App() @{');
201
+ lines.push(' <>');
199
202
  lines.push(' <div>');
200
203
  lines.push(' <span>{"Hello"}</span>');
201
204
  lines.push(' </div>');
202
- lines.push('</>; }');
205
+ lines.push(' </>');
206
+ lines.push('}');
203
207
  const source = lines.join('\n');
204
208
 
205
209
  const result = compile(source, 'test.tsrx', { mode: 'server', dev: false });
@@ -210,9 +214,11 @@ describe('HTML nesting validation', () => {
210
214
 
211
215
  it('emits push_element with correct tag name', () => {
212
216
  const lines = [];
213
- lines.push('function App() { return <>');
217
+ lines.push('function App() @{');
218
+ lines.push(' <>');
214
219
  lines.push(' <button>{"Click"}</button>');
215
- lines.push('</>; }');
220
+ lines.push(' </>');
221
+ lines.push('}');
216
222
  const source = lines.join('\n');
217
223
 
218
224
  const result = compile(source, 'test.tsrx', { mode: 'server', dev: true });
@@ -222,9 +228,11 @@ describe('HTML nesting validation', () => {
222
228
 
223
229
  it('does not emit push_element for client mode', () => {
224
230
  const lines = [];
225
- lines.push('function App() { return <>');
231
+ lines.push('function App() @{');
232
+ lines.push(' <>');
226
233
  lines.push(' <div>{"Hello"}</div>');
227
- lines.push('</>; }');
234
+ lines.push(' </>');
235
+ lines.push('}');
228
236
  const source = lines.join('\n');
229
237
 
230
238
  const result = compile(source, 'test.tsrx', { mode: 'client', dev: true });
@@ -1,12 +1,10 @@
1
1
  describe('if statements in SSR', () => {
2
2
  it('renders if block when condition is true', async () => {
3
- function App() {
4
- return <>
5
- let condition = true;
6
- if (condition) {
7
- <div>{'If block'}</div>
8
- }
9
- </>;
3
+ function App() @{
4
+ let condition = true;
5
+ @if (condition) {
6
+ <div>{'If block'}</div>
7
+ }
10
8
  }
11
9
 
12
10
  const { body } = await render(App);
@@ -14,29 +12,27 @@ describe('if statements in SSR', () => {
14
12
  });
15
13
 
16
14
  it('renders else block when condition is false', async () => {
17
- function App() {
18
- return <>
19
- let condition = false;
20
- if (condition) {
21
- <div>{'If block'}</div>
22
- } else {
23
- <div>{'Else block'}</div>
24
- }
25
- </>;
15
+ function App() @{
16
+ let condition = false;
17
+ @if (condition) {
18
+ <div>{'If block'}</div>
19
+ } @else {
20
+ <div>{'Else block'}</div>
21
+ }
26
22
  }
27
23
 
28
24
  const { body } = await render(App);
29
25
  expect(body).toBeHtml('<div>Else block</div>');
30
26
  });
31
27
 
32
- it('renders bare double-quoted text in if-else branches', async () => {
33
- function App() {
34
- return <>
35
- let condition = false;
36
- if (condition) {
37
- "Hello Ripple"
38
- } else "Hello React"
39
- </>;
28
+ it('renders bare JSX text in if-else branches', async () => {
29
+ function App() @{
30
+ let condition = false;
31
+ @if (condition) {
32
+ <>Hello Ripple</>
33
+ } @else {
34
+ <>Hello React</>
35
+ }
40
36
  }
41
37
 
42
38
  const { body } = await render(App);
@@ -44,17 +40,15 @@ describe('if statements in SSR', () => {
44
40
  });
45
41
 
46
42
  it('renders else if block when condition is true', async () => {
47
- function App() {
48
- return <>
49
- let value = 'b';
50
- if (value === 'a') {
51
- <div>{'Case A'}</div>
52
- } else if (value === 'b') {
53
- <div>{'Case B'}</div>
54
- } else {
55
- <div>{'Default Case'}</div>
56
- }
57
- </>;
43
+ function App() @{
44
+ let value = 'b';
45
+ @if (value === 'a') {
46
+ <div>Case A</div>
47
+ } @else if (value === 'b') {
48
+ <div>Case B</div>
49
+ } @else {
50
+ <div>{'Default Case'}</div>
51
+ }
58
52
  }
59
53
 
60
54
  const { body } = await render(App);
@@ -62,17 +56,15 @@ describe('if statements in SSR', () => {
62
56
  });
63
57
 
64
58
  it('renders final else block in an if-else if-else chain', async () => {
65
- function App() {
66
- return <>
67
- let value = 'c';
68
- if (value === 'a') {
69
- <div>{'Case A'}</div>
70
- } else if (value === 'b') {
71
- <div>{'Case B'}</div>
72
- } else {
73
- <div>{'Default Case'}</div>
74
- }
75
- </>;
59
+ function App() @{
60
+ let value = 'c';
61
+ @if (value === 'a') {
62
+ <div>Case A</div>
63
+ } @else if (value === 'b') {
64
+ <div>Case B</div>
65
+ } @else {
66
+ <div>{'Default Case'}</div>
67
+ }
76
68
  }
77
69
 
78
70
  const { body } = await render(App);
@@ -80,20 +72,18 @@ describe('if statements in SSR', () => {
80
72
  });
81
73
 
82
74
  it('renders nested if-else blocks correctly', async () => {
83
- function App() {
84
- return <>
85
- let outer = true;
86
- let inner = false;
87
- if (outer) {
88
- if (inner) {
89
- <div>{'Outer true, Inner true'}</div>
90
- } else {
91
- <div>{'Outer true, Inner false'}</div>
92
- }
93
- } else {
94
- <div>{'Outer false'}</div>
75
+ function App() @{
76
+ let outer = true;
77
+ let inner = false;
78
+ @if (outer) {
79
+ @if (inner) {
80
+ <div>{'Outer true, Inner true'}</div>
81
+ } @else {
82
+ <div>{'Outer true, Inner false'}</div>
95
83
  }
96
- </>;
84
+ } @else {
85
+ <div>{'Outer false'}</div>
86
+ }
97
87
  }
98
88
 
99
89
  const { body } = await render(App);