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,15 +2,19 @@ import { trackAsync } from 'ripple';
2
2
 
3
3
  describe('try block with catch and pending (server)', () => {
4
4
  it('renders resolved content with an empty pending fallback', async () => {
5
- function App() {
6
- return <>
5
+ function DataChild() @{
6
+ let &[data] = trackAsync(() => Promise.resolve('resolved value'));
7
+ <p>{data}</p>
8
+ }
9
+
10
+ function App() @{
11
+ <>
7
12
  <span>{'before'}</span>
8
- try {
9
- let &[data] = trackAsync(() => Promise.resolve('resolved value'));
10
- <p>{data}</p>
11
- } pending {}
13
+ @try {
14
+ <DataChild />
15
+ } @pending {}
12
16
  <span>{'after'}</span>
13
- </>;
17
+ </>
14
18
  }
15
19
 
16
20
  const { body } = await render(App);
@@ -22,23 +26,17 @@ describe('try block with catch and pending (server)', () => {
22
26
 
23
27
  it('catch block works when component throws before await with pending block', async () => {
24
28
  function ThrowingChild() {
25
- return <>
26
- throw new Error('sync error');
27
- let &[data] = trackAsync(() => Promise.resolve('hello'));
28
- <p>{data}</p>
29
- </>;
29
+ throw new Error('sync error');
30
30
  }
31
31
 
32
- function App() {
33
- return <>
34
- try {
35
- <ThrowingChild />
36
- } pending {
37
- <p>{'loading...'}</p>
38
- } catch (err) {
39
- <p>{'caught error'}</p>
40
- }
41
- </>;
32
+ function App() @{
33
+ @try {
34
+ <ThrowingChild />
35
+ } @pending {
36
+ <p>{'loading...'}</p>
37
+ } @catch (err) {
38
+ <p>{'caught error'}</p>
39
+ }
42
40
  }
43
41
 
44
42
  const { body } = await render(App);
@@ -47,24 +45,20 @@ describe('try block with catch and pending (server)', () => {
47
45
  });
48
46
 
49
47
  it('catch block works when component throws after await with pending block', async () => {
50
- function ThrowingAfterAwait() {
51
- return <>
52
- let &[data] = trackAsync(() => Promise.resolve('hello'));
53
- throw new Error('error after await');
54
- <p>{data}</p>
55
- </>;
56
- }
57
-
58
- function App() {
59
- return <>
60
- try {
61
- <ThrowingAfterAwait />
62
- } pending {
63
- <p>{'loading...'}</p>
64
- } catch (err) {
65
- <p>{'caught error'}</p>
66
- }
67
- </>;
48
+ function ThrowingAfterAwait() @{
49
+ let &[data] = trackAsync(() => Promise.resolve('hello'));
50
+ throw new Error('error after await');
51
+ <p>{data}</p>
52
+ }
53
+
54
+ function App() @{
55
+ @try {
56
+ <ThrowingAfterAwait />
57
+ } @pending {
58
+ <p>{'loading...'}</p>
59
+ } @catch (err) {
60
+ <p>{'caught error'}</p>
61
+ }
68
62
  }
69
63
 
70
64
  const { body } = await render(App);
@@ -73,17 +67,15 @@ describe('try block with catch and pending (server)', () => {
73
67
  });
74
68
 
75
69
  it('catch block works with try/catch/pending when async body rejects', async () => {
76
- function App() {
77
- return <>
78
- try {
79
- let &[data] = trackAsync(() => Promise.reject(new Error('rejected')));
80
- <p>{data}</p>
81
- } pending {
82
- <p>{'loading...'}</p>
83
- } catch (err) {
84
- <p>{'caught rejection'}</p>
85
- }
86
- </>;
70
+ function App() @{
71
+ @try {
72
+ let &[data] = trackAsync(() => Promise.reject(new Error('rejected')));
73
+ <p>{data}</p>
74
+ } @pending {
75
+ <p>{'loading...'}</p>
76
+ } @catch (err) {
77
+ <p>{'caught rejection'}</p>
78
+ }
87
79
  }
88
80
 
89
81
  const { body } = await render(App);
@@ -92,19 +84,17 @@ describe('try block with catch and pending (server)', () => {
92
84
  });
93
85
 
94
86
  it('removes pending content for nested try/pending blocks', async () => {
95
- function App() {
96
- return <>
97
- try {
98
- try {
99
- let &[data] = trackAsync(() => Promise.resolve('resolved'));
100
- <p>{data}</p>
101
- } pending {
102
- <p>{'inner loading...'}</p>
103
- }
104
- } pending {
105
- <p>{'outer loading...'}</p>
87
+ function App() @{
88
+ @try {
89
+ @try {
90
+ let &[data] = trackAsync(() => Promise.resolve('resolved'));
91
+ <p>{data}</p>
92
+ } @pending {
93
+ <p>{'inner loading...'}</p>
106
94
  }
107
- </>;
95
+ } @pending {
96
+ <p>{'outer loading...'}</p>
97
+ }
108
98
  }
109
99
 
110
100
  const { body } = await render(App);
@@ -116,21 +106,17 @@ describe('try block with catch and pending (server)', () => {
116
106
 
117
107
  describe('trackAsync directly in component body (server)', () => {
118
108
  it('resolves trackAsync used directly in a component', async () => {
119
- function App() {
120
- return <>
121
- try {
122
- <DataChild />
123
- } catch (err) {
124
- <p>{'error'}</p>
125
- }
126
- </>;
109
+ function App() @{
110
+ @try {
111
+ <DataChild />
112
+ } @catch (err) {
113
+ <p>{'error'}</p>
114
+ }
127
115
  }
128
116
 
129
- function DataChild() {
130
- return <>
131
- let &[data] = trackAsync(() => Promise.resolve('from child'));
132
- <p>{data}</p>
133
- </>;
117
+ function DataChild() @{
118
+ let &[data] = trackAsync(() => Promise.resolve('from child'));
119
+ <p>{data}</p>
134
120
  }
135
121
 
136
122
  const { body } = await render(App);
@@ -139,20 +125,18 @@ describe('trackAsync directly in component body (server)', () => {
139
125
  });
140
126
 
141
127
  it('resolves multiple trackAsync values in the same component', async () => {
142
- function App() {
143
- return <>
144
- try {
145
- let &[a] = trackAsync(() => Promise.resolve('hello'));
146
- let &[b] = trackAsync(() => Promise.resolve('world'));
147
- <p>
148
- {a}
149
- {' '}
150
- {b}
151
- </p>
152
- } catch (err) {
153
- <p>{'error'}</p>
154
- }
155
- </>;
128
+ function App() @{
129
+ @try {
130
+ let &[a] = trackAsync(() => Promise.resolve('hello'));
131
+ let &[b] = trackAsync(() => Promise.resolve('world'));
132
+ <p>
133
+ {a}
134
+ {' '}
135
+ {b}
136
+ </p>
137
+ } @catch (err) {
138
+ <p>{'error'}</p>
139
+ }
156
140
  }
157
141
 
158
142
  const { body } = await render(App);
@@ -161,21 +145,17 @@ describe('trackAsync directly in component body (server)', () => {
161
145
  });
162
146
 
163
147
  it('renders catch when trackAsync rejects in a child component without its own try', async () => {
164
- function RejectChild() {
165
- return <>
166
- let &[data] = trackAsync(() => Promise.reject(new Error('child rejected')));
167
- <p>{data}</p>
168
- </>;
148
+ function RejectChild() @{
149
+ let &[data] = trackAsync(() => Promise.reject(new Error('child rejected')));
150
+ <p>{data}</p>
169
151
  }
170
152
 
171
- function App() {
172
- return <>
173
- try {
174
- <RejectChild />
175
- } catch (err) {
176
- <p>{'parent caught it'}</p>
177
- }
178
- </>;
153
+ function App() @{
154
+ @try {
155
+ <RejectChild />
156
+ } @catch (err) {
157
+ <p>{'parent caught it'}</p>
158
+ }
179
159
  }
180
160
 
181
161
  const { body } = await render(App);
@@ -183,19 +163,17 @@ describe('trackAsync directly in component body (server)', () => {
183
163
  });
184
164
 
185
165
  it('chained trackAsync resolves both values', async () => {
186
- function App() {
187
- return <>
188
- try {
189
- let &[name] = trackAsync(() => Promise.resolve('ripple'));
190
- let &[upper] = trackAsync(() => {
191
- const n = name;
192
- return Promise.resolve(n.toUpperCase());
193
- });
194
- <p>{upper}</p>
195
- } catch (err) {
196
- <p>{'error'}</p>
197
- }
198
- </>;
166
+ function App() @{
167
+ @try {
168
+ let &[name] = trackAsync(() => Promise.resolve('ripple'));
169
+ let &[upper] = trackAsync(() => {
170
+ const n = name;
171
+ return Promise.resolve(n.toUpperCase());
172
+ });
173
+ <p>{upper}</p>
174
+ } @catch (err) {
175
+ <p>{'error'}</p>
176
+ }
199
177
  }
200
178
 
201
179
  const { body } = await render(App);
@@ -204,19 +182,17 @@ describe('trackAsync directly in component body (server)', () => {
204
182
  });
205
183
 
206
184
  it('chained trackAsync catches when first rejects', async () => {
207
- function App() {
208
- return <>
209
- try {
210
- let &[name] = trackAsync(() => Promise.reject<string>(new Error('first failed')));
211
- let &[upper] = trackAsync(() => {
212
- const n = name;
213
- return Promise.resolve(n.toUpperCase());
214
- });
215
- <p>{upper}</p>
216
- } catch (err: Error) {
217
- <p>{err.message}</p>
218
- }
219
- </>;
185
+ function App() @{
186
+ @try {
187
+ let &[name] = trackAsync(() => Promise.reject<string>(new Error('first failed')));
188
+ let &[upper] = trackAsync(() => {
189
+ const n = name;
190
+ return Promise.resolve(n.toUpperCase());
191
+ });
192
+ <p>{upper}</p>
193
+ } @catch (err: Error) {
194
+ <p>{err.message}</p>
195
+ }
220
196
  }
221
197
 
222
198
  const { body } = await render(App);
@@ -224,19 +200,17 @@ describe('trackAsync directly in component body (server)', () => {
224
200
  });
225
201
 
226
202
  it('chained trackAsync catches when second rejects', async () => {
227
- function App() {
228
- return <>
229
- try {
230
- let &[name] = trackAsync(() => Promise.resolve('ripple'));
231
- let &[upper] = trackAsync(() => {
232
- const n = name;
233
- return Promise.reject(new Error('second failed'));
234
- });
235
- <p>{upper}</p>
236
- } catch (err: Error) {
237
- <p>{err.message}</p>
238
- }
239
- </>;
203
+ function App() @{
204
+ @try {
205
+ let &[name] = trackAsync(() => Promise.resolve('ripple'));
206
+ let &[upper] = trackAsync(() => {
207
+ const n = name;
208
+ return Promise.reject(new Error('second failed'));
209
+ });
210
+ <p>{upper}</p>
211
+ } @catch (err: Error) {
212
+ <p>{err.message}</p>
213
+ }
240
214
  }
241
215
 
242
216
  const { body } = await render(App);
@@ -247,30 +221,23 @@ describe('trackAsync directly in component body (server)', () => {
247
221
  describe('nested child components with try/catch boundaries (server)', () => {
248
222
  it('inner try/catch catches error from its own child', async () => {
249
223
  function ThrowingChild() {
250
- return <>
251
- throw new Error('inner error');
252
- <p>{'should not render'}</p>
253
- </>;
224
+ throw new Error('inner error');
254
225
  }
255
226
 
256
- function Inner() {
257
- return <>
258
- try {
259
- <ThrowingChild />
260
- } catch (err: Error) {
261
- <p>{err.message}</p>
262
- }
263
- </>;
227
+ function Inner() @{
228
+ @try {
229
+ <ThrowingChild />
230
+ } @catch (err: Error) {
231
+ <p>{err.message}</p>
232
+ }
264
233
  }
265
234
 
266
- function App() {
267
- return <>
268
- try {
269
- <Inner />
270
- } catch (err) {
271
- <p>{'outer caught'}</p>
272
- }
273
- </>;
235
+ function App() @{
236
+ @try {
237
+ <Inner />
238
+ } @catch (err) {
239
+ <p>{'outer caught'}</p>
240
+ }
274
241
  }
275
242
 
276
243
  const { body } = await render(App);
@@ -279,31 +246,25 @@ describe('nested child components with try/catch boundaries (server)', () => {
279
246
  });
280
247
 
281
248
  it('inner try/catch catches async rejection from its own child', async () => {
282
- function AsyncChild() {
283
- return <>
284
- let &[data] = trackAsync(() => Promise.reject(new Error('async inner error')));
285
- <p>{data}</p>
286
- </>;
249
+ function AsyncChild() @{
250
+ let &[data] = trackAsync(() => Promise.reject(new Error('async inner error')));
251
+ <p>{data}</p>
287
252
  }
288
253
 
289
- function Inner() {
290
- return <>
291
- try {
292
- <AsyncChild />
293
- } catch (err: Error) {
294
- <p>{err.message}</p>
295
- }
296
- </>;
254
+ function Inner() @{
255
+ @try {
256
+ <AsyncChild />
257
+ } @catch (err: Error) {
258
+ <p>{err.message}</p>
259
+ }
297
260
  }
298
261
 
299
- function App() {
300
- return <>
301
- try {
302
- <Inner />
303
- } catch (err) {
304
- <p>{'outer caught'}</p>
305
- }
306
- </>;
262
+ function App() @{
263
+ @try {
264
+ <Inner />
265
+ } @catch (err) {
266
+ <p>{'outer caught'}</p>
267
+ }
307
268
  }
308
269
 
309
270
  const { body } = await render(App);
@@ -313,30 +274,23 @@ describe('nested child components with try/catch boundaries (server)', () => {
313
274
 
314
275
  it('error propagates to outer catch when inner try has no catch', async () => {
315
276
  function ThrowingChild() {
316
- return <>
317
- throw new Error('propagated error');
318
- <p>{'should not render'}</p>
319
- </>;
277
+ throw new Error('propagated error');
320
278
  }
321
279
 
322
- function Inner() {
323
- return <>
324
- try {
325
- <ThrowingChild />
326
- } pending {
327
- <p>{'loading...'}</p>
328
- }
329
- </>;
280
+ function Inner() @{
281
+ @try {
282
+ <ThrowingChild />
283
+ } @pending {
284
+ <p>{'loading...'}</p>
285
+ }
330
286
  }
331
287
 
332
- function App() {
333
- return <>
334
- try {
335
- <Inner />
336
- } catch (err: Error) {
337
- <p>{err.message}</p>
338
- }
339
- </>;
288
+ function App() @{
289
+ @try {
290
+ <Inner />
291
+ } @catch (err: Error) {
292
+ <p>{err.message}</p>
293
+ }
340
294
  }
341
295
 
342
296
  const { body } = await render(App);
@@ -345,31 +299,25 @@ describe('nested child components with try/catch boundaries (server)', () => {
345
299
  });
346
300
 
347
301
  it('async rejection propagates to outer catch when inner try has no catch', async () => {
348
- function AsyncChild() {
349
- return <>
350
- let &[data] = trackAsync(() => Promise.reject(new Error('async propagated')));
351
- <p>{data}</p>
352
- </>;
302
+ function AsyncChild() @{
303
+ let &[data] = trackAsync(() => Promise.reject(new Error('async propagated')));
304
+ <p>{data}</p>
353
305
  }
354
306
 
355
- function Inner() {
356
- return <>
357
- try {
358
- <AsyncChild />
359
- } pending {
360
- <p>{'loading...'}</p>
361
- }
362
- </>;
307
+ function Inner() @{
308
+ @try {
309
+ <AsyncChild />
310
+ } @pending {
311
+ <p>{'loading...'}</p>
312
+ }
363
313
  }
364
314
 
365
- function App() {
366
- return <>
367
- try {
368
- <Inner />
369
- } catch (err: Error) {
370
- <p>{err.message}</p>
371
- }
372
- </>;
315
+ function App() @{
316
+ @try {
317
+ <Inner />
318
+ } @catch (err: Error) {
319
+ <p>{err.message}</p>
320
+ }
373
321
  }
374
322
 
375
323
  const { body } = await render(App);
@@ -381,40 +329,31 @@ describe('nested child components with try/catch boundaries (server)', () => {
381
329
  'multiple nested levels: error propagates through pending-only boundaries to nearest catch',
382
330
  async () => {
383
331
  function ThrowingChild() {
384
- return <>
385
- throw new Error('deep error');
386
- <p>{'should not render'}</p>
387
- </>;
332
+ throw new Error('deep error');
388
333
  }
389
334
 
390
- function Level3() {
391
- return <>
392
- try {
393
- <ThrowingChild />
394
- } pending {
395
- <p>{'level3 loading'}</p>
396
- }
397
- </>;
335
+ function Level3() @{
336
+ @try {
337
+ <ThrowingChild />
338
+ } @pending {
339
+ <p>{'level3 loading'}</p>
340
+ }
398
341
  }
399
342
 
400
- function Level2() {
401
- return <>
402
- try {
403
- <Level3 />
404
- } pending {
405
- <p>{'level2 loading'}</p>
406
- }
407
- </>;
343
+ function Level2() @{
344
+ @try {
345
+ <Level3 />
346
+ } @pending {
347
+ <p>{'level2 loading'}</p>
348
+ }
408
349
  }
409
350
 
410
- function App() {
411
- return <>
412
- try {
413
- <Level2 />
414
- } catch (err: Error) {
415
- <p>{err.message}</p>
416
- }
417
- </>;
351
+ function App() @{
352
+ @try {
353
+ <Level2 />
354
+ } @catch (err: Error) {
355
+ <p>{err.message}</p>
356
+ }
418
357
  }
419
358
 
420
359
  const { body } = await render(App);
@@ -423,29 +362,25 @@ describe('nested child components with try/catch boundaries (server)', () => {
423
362
  );
424
363
 
425
364
  it('sibling components: one fails, the other does not affect catch', async () => {
426
- function GoodChild() {
427
- return <>
428
- let &[data] = trackAsync(() => Promise.resolve('good'));
429
- <p>{data}</p>
430
- </>;
365
+ function GoodChild() @{
366
+ let &[data] = trackAsync(() => Promise.resolve('good'));
367
+ <p>{data}</p>
431
368
  }
432
369
 
433
- function BadChild() {
434
- return <>
435
- let &[data] = trackAsync(() => Promise.reject(new Error('bad child')));
436
- <p>{data}</p>
437
- </>;
370
+ function BadChild() @{
371
+ let &[data] = trackAsync(() => Promise.reject(new Error('bad child')));
372
+ <p>{data}</p>
438
373
  }
439
374
 
440
- function App() {
441
- return <>
442
- try {
375
+ function App() @{
376
+ @try {
377
+ <>
443
378
  <GoodChild />
444
379
  <BadChild />
445
- } catch (err: Error) {
446
- <p>{err.message}</p>
447
- }
448
- </>;
380
+ </>
381
+ } @catch (err: Error) {
382
+ <p>{err.message}</p>
383
+ }
449
384
  }
450
385
 
451
386
  const { body } = await render(App);
@@ -454,33 +389,29 @@ describe('nested child components with try/catch boundaries (server)', () => {
454
389
  });
455
390
 
456
391
  it('independent try/catch boundaries each handle their own errors', async () => {
457
- function FailChild() {
458
- return <>
459
- let &[data] = trackAsync(() => Promise.reject(new Error('fail')));
460
- <p>{data}</p>
461
- </>;
392
+ function FailChild() @{
393
+ let &[data] = trackAsync(() => Promise.reject(new Error('fail')));
394
+ <p>{data}</p>
462
395
  }
463
396
 
464
- function SuccessChild() {
465
- return <>
466
- let &[data] = trackAsync(() => Promise.resolve('success'));
467
- <p>{data}</p>
468
- </>;
397
+ function SuccessChild() @{
398
+ let &[data] = trackAsync(() => Promise.resolve('success'));
399
+ <p>{data}</p>
469
400
  }
470
401
 
471
- function App() {
472
- return <>
473
- try {
402
+ function App() @{
403
+ <>
404
+ @try {
474
405
  <FailChild />
475
- } catch (err: Error) {
406
+ } @catch (err: Error) {
476
407
  <p>{err.message}</p>
477
408
  }
478
- try {
409
+ @try {
479
410
  <SuccessChild />
480
- } catch (err) {
411
+ } @catch (err) {
481
412
  <p>{'should not catch'}</p>
482
413
  }
483
- </>;
414
+ </>
484
415
  }
485
416
 
486
417
  const { body } = await render(App);
@@ -490,30 +421,24 @@ describe('nested child components with try/catch boundaries (server)', () => {
490
421
  });
491
422
 
492
423
  it('inner catch handles rejection, outer renders normally', async () => {
493
- function AsyncChild() {
494
- return <>
495
- let &[data] = trackAsync(() => Promise.reject(new Error('handled inside')));
496
- <p>{data}</p>
497
- </>;
424
+ function AsyncChild() @{
425
+ let &[data] = trackAsync(() => Promise.reject(new Error('handled inside')));
426
+ <p>{data}</p>
498
427
  }
499
428
 
500
- function Inner() {
501
- return <>
502
- try {
503
- <AsyncChild />
504
- } catch (err: Error) {
505
- <span>{err.message}</span>
506
- }
507
- </>;
429
+ function Inner() @{
430
+ @try {
431
+ <AsyncChild />
432
+ } @catch (err: Error) {
433
+ <span>{err.message}</span>
434
+ }
508
435
  }
509
436
 
510
- function App() {
511
- return <>
512
- <div>
513
- <h1>{'App'}</h1>
514
- <Inner />
515
- </div>
516
- </>;
437
+ function App() @{
438
+ <div>
439
+ <h1>{'App'}</h1>
440
+ <Inner />
441
+ </div>
517
442
  }
518
443
 
519
444
  const { body } = await render(App);
@@ -522,26 +447,22 @@ describe('nested child components with try/catch boundaries (server)', () => {
522
447
  });
523
448
 
524
449
  it('sync error in child after trackAsync routes to catch boundary', async () => {
525
- function BrokenChild() {
526
- return <>
527
- let &[data] = trackAsync(() => Promise.resolve('loaded'));
528
- throw new Error('sync after async');
529
- <p>{data}</p>
530
- </>;
450
+ function BrokenChild() @{
451
+ let &[data] = trackAsync(() => Promise.resolve('loaded'));
452
+ throw new Error('sync after async');
453
+ <p>{data}</p>
531
454
  }
532
455
 
533
- function Inner() {
534
- return <>
535
- try {
536
- <BrokenChild />
537
- } catch (err: Error) {
538
- <p>{err.message}</p>
539
- }
540
- </>;
456
+ function Inner() @{
457
+ @try {
458
+ <BrokenChild />
459
+ } @catch (err: Error) {
460
+ <p>{err.message}</p>
461
+ }
541
462
  }
542
463
 
543
- function App() {
544
- return <><Inner /></>;
464
+ function App() @{
465
+ <Inner />
545
466
  }
546
467
 
547
468
  const { body } = await render(App);
@@ -549,31 +470,25 @@ describe('nested child components with try/catch boundaries (server)', () => {
549
470
  });
550
471
 
551
472
  it('outer try with pending, inner try with catch: rejection goes to inner catch', async () => {
552
- function AsyncChild() {
553
- return <>
554
- let &[data] = trackAsync(() => Promise.reject(new Error('inner rejection')));
555
- <p>{data}</p>
556
- </>;
473
+ function AsyncChild() @{
474
+ let &[data] = trackAsync(() => Promise.reject(new Error('inner rejection')));
475
+ <p>{data}</p>
557
476
  }
558
477
 
559
- function Inner() {
560
- return <>
561
- try {
562
- <AsyncChild />
563
- } catch (err: Error) {
564
- <p>{err.message}</p>
565
- }
566
- </>;
478
+ function Inner() @{
479
+ @try {
480
+ <AsyncChild />
481
+ } @catch (err: Error) {
482
+ <p>{err.message}</p>
483
+ }
567
484
  }
568
485
 
569
- function App() {
570
- return <>
571
- try {
572
- <Inner />
573
- } pending {
574
- <p>{'outer loading'}</p>
575
- }
576
- </>;
486
+ function App() @{
487
+ @try {
488
+ <Inner />
489
+ } @pending {
490
+ <p>{'outer loading'}</p>
491
+ }
577
492
  }
578
493
 
579
494
  const { body } = await render(App);
@@ -582,29 +497,29 @@ describe('nested child components with try/catch boundaries (server)', () => {
582
497
  });
583
498
 
584
499
  it('deeply nested: async resolves through multiple component layers', async () => {
585
- function DataFetcher() {
586
- return <>
587
- let &[data] = trackAsync(() => Promise.resolve('deep data'));
588
- <span>{data}</span>
589
- </>;
500
+ function DataFetcher() @{
501
+ let &[data] = trackAsync(() => Promise.resolve('deep data'));
502
+ <span>{data}</span>
590
503
  }
591
504
 
592
- function Level2() {
593
- return <><div><DataFetcher /></div></>;
505
+ function Level2() @{
506
+ <div>
507
+ <DataFetcher />
508
+ </div>
594
509
  }
595
510
 
596
- function Level1() {
597
- return <><section><Level2 /></section></>;
511
+ function Level1() @{
512
+ <section>
513
+ <Level2 />
514
+ </section>
598
515
  }
599
516
 
600
- function App() {
601
- return <>
602
- try {
603
- <Level1 />
604
- } catch (err) {
605
- <p>{'error'}</p>
606
- }
607
- </>;
517
+ function App() @{
518
+ @try {
519
+ <Level1 />
520
+ } @catch (err) {
521
+ <p>{'error'}</p>
522
+ }
608
523
  }
609
524
 
610
525
  const { body } = await render(App);