ripple 0.3.67 → 0.3.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/package.json +3 -3
  3. package/src/jsx-runtime.d.ts +2 -2
  4. package/src/runtime/element.js +1 -1
  5. package/src/runtime/index-client.js +11 -11
  6. package/src/runtime/index-server.js +7 -4
  7. package/src/runtime/internal/client/bindings.js +1 -1
  8. package/src/runtime/internal/client/blocks.js +13 -4
  9. package/src/runtime/internal/client/component.js +55 -0
  10. package/src/runtime/internal/client/composite.js +4 -2
  11. package/src/runtime/internal/client/expression.js +65 -7
  12. package/src/runtime/internal/client/hmr.js +54 -43
  13. package/src/runtime/internal/client/index.js +5 -1
  14. package/src/runtime/internal/client/portal.js +70 -69
  15. package/src/runtime/internal/client/render.js +3 -0
  16. package/src/runtime/internal/server/index.js +92 -8
  17. package/tests/client/__snapshots__/html.test.tsrx.snap +3 -3
  18. package/tests/client/array/array.copy-within.test.tsrx +33 -31
  19. package/tests/client/array/array.derived.test.tsrx +186 -169
  20. package/tests/client/array/array.iteration.test.tsrx +40 -37
  21. package/tests/client/array/array.mutations.test.tsrx +113 -101
  22. package/tests/client/array/array.static.test.tsrx +119 -101
  23. package/tests/client/array/array.to-methods.test.tsrx +24 -21
  24. package/tests/client/async-suspend.test.tsrx +247 -246
  25. package/tests/client/basic/__snapshots__/basic.rendering.test.tsrx.snap +0 -1
  26. package/tests/client/basic/basic.attributes.test.tsrx +428 -423
  27. package/tests/client/basic/basic.collections.test.tsrx +109 -102
  28. package/tests/client/basic/basic.components.test.tsrx +323 -205
  29. package/tests/client/basic/basic.errors.test.tsrx +91 -91
  30. package/tests/client/basic/basic.events.test.tsrx +114 -115
  31. package/tests/client/basic/basic.get-set.test.tsrx +97 -87
  32. package/tests/client/basic/basic.hmr.test.tsrx +19 -16
  33. package/tests/client/basic/basic.reactivity.test.tsrx +199 -191
  34. package/tests/client/basic/basic.rendering.test.tsrx +272 -182
  35. package/tests/client/basic/basic.styling.test.tsrx +23 -22
  36. package/tests/client/basic/basic.utilities.test.tsrx +10 -8
  37. package/tests/client/boundaries.test.tsrx +26 -26
  38. package/tests/client/compiler/__snapshots__/compiler.assignments.test.rsrx.snap +5 -5
  39. package/tests/client/compiler/__snapshots__/compiler.assignments.test.tsrx.snap +5 -5
  40. package/tests/client/compiler/compiler.assignments.test.tsrx +77 -81
  41. package/tests/client/compiler/compiler.attributes.test.tsrx +15 -15
  42. package/tests/client/compiler/compiler.basic.test.tsrx +322 -314
  43. package/tests/client/compiler/compiler.regex.test.tsrx +44 -47
  44. package/tests/client/compiler/compiler.tracked-access.test.tsrx +38 -38
  45. package/tests/client/compiler/compiler.try-in-function.test.tsrx +16 -16
  46. package/tests/client/compiler/compiler.typescript.test.tsrx +2 -2
  47. package/tests/client/composite/composite.dynamic-components.test.tsrx +47 -48
  48. package/tests/client/composite/composite.generics.test.tsrx +168 -192
  49. package/tests/client/composite/composite.props.test.tsrx +97 -81
  50. package/tests/client/composite/composite.reactivity.test.tsrx +177 -147
  51. package/tests/client/composite/composite.render.test.tsrx +122 -105
  52. package/tests/client/computed-properties.test.tsrx +28 -28
  53. package/tests/client/context.test.tsrx +21 -21
  54. package/tests/client/css/global-additional-cases.test.tsrx +58 -58
  55. package/tests/client/css/global-advanced-selectors.test.tsrx +16 -16
  56. package/tests/client/css/global-at-rules.test.tsrx +10 -10
  57. package/tests/client/css/global-basic.test.tsrx +14 -14
  58. package/tests/client/css/global-classes-ids.test.tsrx +14 -14
  59. package/tests/client/css/global-combinators.test.tsrx +10 -10
  60. package/tests/client/css/global-complex-nesting.test.tsrx +14 -14
  61. package/tests/client/css/global-edge-cases.test.tsrx +18 -18
  62. package/tests/client/css/global-keyframes.test.tsrx +12 -12
  63. package/tests/client/css/global-nested.test.tsrx +10 -10
  64. package/tests/client/css/global-pseudo.test.tsrx +12 -12
  65. package/tests/client/css/global-scoping.test.tsrx +20 -20
  66. package/tests/client/css/style-identifier.test.tsrx +143 -291
  67. package/tests/client/date.test.tsrx +146 -133
  68. package/tests/client/dynamic-elements.test.tsrx +398 -365
  69. package/tests/client/events.test.tsrx +292 -290
  70. package/tests/client/for.test.tsrx +156 -153
  71. package/tests/client/head.test.tsrx +105 -96
  72. package/tests/client/html.test.tsrx +122 -26
  73. package/tests/client/input-value.test.tsrx +1361 -1314
  74. package/tests/client/lazy-array.test.tsrx +16 -13
  75. package/tests/client/lazy-destructuring.test.tsrx +257 -213
  76. package/tests/client/map.test.tsrx +65 -60
  77. package/tests/client/media-query.test.tsrx +22 -20
  78. package/tests/client/object.test.tsrx +87 -81
  79. package/tests/client/portal.test.tsrx +57 -51
  80. package/tests/client/ref.test.tsrx +233 -202
  81. package/tests/client/return.test.tsrx +71 -2560
  82. package/tests/client/set.test.tsrx +54 -45
  83. package/tests/client/svg.test.tsrx +216 -186
  84. package/tests/client/switch.test.tsrx +194 -193
  85. package/tests/client/track-async-hydration.test.tsrx +18 -14
  86. package/tests/client/tracked-index-access.test.tsrx +28 -18
  87. package/tests/client/try.test.tsrx +675 -548
  88. package/tests/client/tsx.test.tsrx +373 -311
  89. package/tests/client/typescript-generics.test.tsrx +145 -145
  90. package/tests/client/url/url.derived.test.tsrx +33 -28
  91. package/tests/client/url/url.parsing.test.tsrx +61 -51
  92. package/tests/client/url/url.partial-removal.test.tsrx +56 -48
  93. package/tests/client/url/url.reactivity.test.tsrx +142 -125
  94. package/tests/client/url/url.serialization.test.tsrx +13 -11
  95. package/tests/client/url-search-params/url-search-params.derived.test.tsrx +34 -29
  96. package/tests/client/url-search-params/url-search-params.initialization.test.tsrx +25 -21
  97. package/tests/client/url-search-params/url-search-params.iteration.test.tsrx +50 -45
  98. package/tests/client/url-search-params/url-search-params.mutation.test.tsrx +111 -99
  99. package/tests/client/url-search-params/url-search-params.retrieval.test.tsrx +49 -43
  100. package/tests/client/url-search-params/url-search-params.serialization.test.tsrx +14 -12
  101. package/tests/client/url-search-params/url-search-params.tracked-url.test.tsrx +16 -14
  102. package/tests/hydration/basic.test.js +3 -3
  103. package/tests/hydration/compiled/client/basic.js +586 -651
  104. package/tests/hydration/compiled/client/composite.js +79 -104
  105. package/tests/hydration/compiled/client/events.js +140 -148
  106. package/tests/hydration/compiled/client/for.js +1005 -1018
  107. package/tests/hydration/compiled/client/head.js +124 -134
  108. package/tests/hydration/compiled/client/hmr.js +41 -48
  109. package/tests/hydration/compiled/client/html-in-template.js +38 -41
  110. package/tests/hydration/compiled/client/html.js +970 -1314
  111. package/tests/hydration/compiled/client/if-children.js +234 -249
  112. package/tests/hydration/compiled/client/if.js +182 -189
  113. package/tests/hydration/compiled/client/mixed-control-flow.js +347 -303
  114. package/tests/hydration/compiled/client/nested-control-flow.js +1084 -832
  115. package/tests/hydration/compiled/client/portal.js +65 -85
  116. package/tests/hydration/compiled/client/reactivity.js +84 -90
  117. package/tests/hydration/compiled/client/return.js +38 -1939
  118. package/tests/hydration/compiled/client/switch.js +218 -224
  119. package/tests/hydration/compiled/client/track-async-serialization.js +250 -259
  120. package/tests/hydration/compiled/client/try.js +123 -132
  121. package/tests/hydration/compiled/server/basic.js +773 -831
  122. package/tests/hydration/compiled/server/composite.js +166 -191
  123. package/tests/hydration/compiled/server/events.js +170 -184
  124. package/tests/hydration/compiled/server/for.js +851 -909
  125. package/tests/hydration/compiled/server/head.js +206 -216
  126. package/tests/hydration/compiled/server/hmr.js +64 -72
  127. package/tests/hydration/compiled/server/html-in-template.js +42 -76
  128. package/tests/hydration/compiled/server/html.js +1362 -1667
  129. package/tests/hydration/compiled/server/if-children.js +419 -445
  130. package/tests/hydration/compiled/server/if.js +194 -208
  131. package/tests/hydration/compiled/server/mixed-control-flow.js +249 -257
  132. package/tests/hydration/compiled/server/nested-control-flow.js +491 -515
  133. package/tests/hydration/compiled/server/portal.js +152 -160
  134. package/tests/hydration/compiled/server/reactivity.js +94 -106
  135. package/tests/hydration/compiled/server/return.js +28 -2172
  136. package/tests/hydration/compiled/server/switch.js +274 -286
  137. package/tests/hydration/compiled/server/track-async-serialization.js +340 -358
  138. package/tests/hydration/compiled/server/try.js +167 -185
  139. package/tests/hydration/components/basic.tsrx +320 -272
  140. package/tests/hydration/components/composite.tsrx +44 -32
  141. package/tests/hydration/components/events.tsrx +101 -91
  142. package/tests/hydration/components/for.tsrx +510 -452
  143. package/tests/hydration/components/head.tsrx +87 -80
  144. package/tests/hydration/components/hmr.tsrx +22 -17
  145. package/tests/hydration/components/html-in-template.tsrx +22 -17
  146. package/tests/hydration/components/html.tsrx +525 -443
  147. package/tests/hydration/components/if-children.tsrx +158 -148
  148. package/tests/hydration/components/if.tsrx +109 -95
  149. package/tests/hydration/components/mixed-control-flow.tsrx +100 -96
  150. package/tests/hydration/components/nested-control-flow.tsrx +215 -203
  151. package/tests/hydration/components/portal.tsrx +41 -34
  152. package/tests/hydration/components/reactivity.tsrx +37 -27
  153. package/tests/hydration/components/return.tsrx +12 -556
  154. package/tests/hydration/components/switch.tsrx +120 -114
  155. package/tests/hydration/components/track-async-serialization.tsrx +107 -91
  156. package/tests/hydration/components/try.tsrx +55 -40
  157. package/tests/hydration/html.test.js +4 -4
  158. package/tests/hydration/return.test.js +13 -532
  159. package/tests/server/await.test.tsrx +3 -3
  160. package/tests/server/basic.attributes.test.tsrx +264 -195
  161. package/tests/server/basic.components.test.tsrx +296 -169
  162. package/tests/server/basic.test.tsrx +300 -198
  163. package/tests/server/compiler.test.tsrx +62 -60
  164. package/tests/server/composite.props.test.tsrx +77 -63
  165. package/tests/server/composite.test.tsrx +168 -192
  166. package/tests/server/context.test.tsrx +18 -12
  167. package/tests/server/dynamic-elements.test.tsrx +197 -180
  168. package/tests/server/for.test.tsrx +85 -78
  169. package/tests/server/head.test.tsrx +50 -43
  170. package/tests/server/html-nesting-validation.test.tsrx +8 -8
  171. package/tests/server/if.test.tsrx +57 -51
  172. package/tests/server/lazy-destructuring.test.tsrx +366 -294
  173. package/tests/server/return.test.tsrx +76 -1355
  174. package/tests/server/streaming-ssr.test.tsrx +4 -75
  175. package/tests/server/style-identifier.test.tsrx +169 -148
  176. package/tests/server/switch.test.tsrx +91 -85
  177. package/tests/server/track-async-serialization.test.tsrx +105 -85
  178. package/tests/server/try.test.tsrx +374 -280
  179. package/tests/utils/compiler-compat-config.test.js +2 -2
  180. package/tests/utils/runtime-imports.test.js +10 -0
  181. package/types/index.d.ts +8 -0
  182. package/tests/client/__snapshots__/html.test.rsrx.snap +0 -40
@@ -10,17 +10,17 @@ describe('refs', () => {
10
10
  expect(isRefProp({ value: null })).toBe(false);
11
11
  });
12
12
 
13
- it('captures a host element with a named ref prop', () => {
13
+ it('captures a host element with ref={...}', () => {
14
14
  let captured: HTMLInputElement | null = null;
15
15
 
16
- component App() {
17
- let input: HTMLInputElement | undefined;
18
-
19
- <input type="text" input_ref={ref input} />
20
-
21
- effect(() => {
22
- captured = input ?? null;
23
- });
16
+ function App() {
17
+ return <>
18
+ let input: HTMLInputElement | undefined;
19
+ <input type="text" ref={input} />
20
+ effect(() => {
21
+ captured = input ?? null;
22
+ });
23
+ </>;
24
24
  }
25
25
 
26
26
  render(App);
@@ -32,13 +32,12 @@ describe('refs', () => {
32
32
  it('forwards a named ref prop explicitly through a component', () => {
33
33
  let captured: HTMLInputElement | null = null;
34
34
 
35
- component Child(props: PropsWithExtras<{}>) {
36
- expect(isRefProp(props.input_ref)).toBe(true);
37
- <input type="text" ref={props.input_ref} />
35
+ function Child(props: PropsWithExtras<{}>) {
36
+ return <><input type="text" ref={props.input_ref} /></>;
38
37
  }
39
38
 
40
- component App() {
41
- <Child input_ref={ref (node: HTMLInputElement | null) => (captured = node)} />
39
+ function App() {
40
+ return <><Child input_ref={(node: HTMLInputElement | null) => (captured = node)} /></>;
42
41
  }
43
42
 
44
43
  render(App);
@@ -47,16 +46,18 @@ describe('refs', () => {
47
46
  expect(captured).toBeInstanceOf(HTMLInputElement);
48
47
  });
49
48
 
50
- it('applies named ref props from host spreads', () => {
49
+ it('forwards an ordinary named prop explicitly from a host spread', () => {
51
50
  let captured: HTMLInputElement | null = null;
52
51
 
53
- component Child(props: PropsWithExtras<{}>) {
54
- expect(isRefProp(props.input_ref)).toBe(true);
55
- <input type="text" {...props} />
52
+ function Child(props: PropsWithExtras<{}>) {
53
+ return <>
54
+ const { input_ref, ...rest } = props;
55
+ <input type="text" ref={input_ref} {...rest} />
56
+ </>;
56
57
  }
57
58
 
58
- component App() {
59
- <Child input_ref={ref (node: HTMLInputElement | null) => (captured = node)} />
59
+ function App() {
60
+ return <><Child input_ref={(node: HTMLInputElement | null) => (captured = node)} /></>;
60
61
  }
61
62
 
62
63
  render(App);
@@ -68,14 +69,16 @@ describe('refs', () => {
68
69
  it('capture a <div>', () => {
69
70
  let div: HTMLDivElement | undefined;
70
71
 
71
- component Component() {
72
- <div
73
- {ref (node: HTMLDivElement) => {
74
- div = node;
75
- }}
76
- >
77
- {'Hello World'}
78
- </div>
72
+ function Component() {
73
+ return <>
74
+ <div
75
+ ref={(node: HTMLDivElement) => {
76
+ div = node;
77
+ }}
78
+ >
79
+ {'Hello World'}
80
+ </div>
81
+ </>;
79
82
  }
80
83
  render(Component);
81
84
  flushSync();
@@ -86,20 +89,22 @@ describe('refs', () => {
86
89
  type Child = typeof Child;
87
90
  let _node: Child | undefined;
88
91
 
89
- component Component() {
90
- let items = RippleArray.from([1, 2, 3]);
91
-
92
- function componentRef(node: Child) {
93
- _node = node;
94
- }
95
-
96
- <Child {ref componentRef} {items} />
92
+ function Component() {
93
+ return <>
94
+ let items = RippleArray.from([1, 2, 3]);
95
+ function componentRef(node: Child) {
96
+ _node = node;
97
+ }
98
+ <Child ref={componentRef} {items} />
99
+ </>;
97
100
  }
98
101
 
99
- component Child(props: { items: RippleArray<number> }) {
100
- const { items, ...rest } = props;
101
- <pre {...rest}>{JSON.stringify(items)}</pre>
102
- <pre>{items.length}</pre>
102
+ function Child(props: { items: RippleArray<number> }) {
103
+ return <>
104
+ const { items, ...rest } = props;
105
+ <pre {...rest}>{JSON.stringify(items)}</pre>
106
+ <pre>{items.length}</pre>
107
+ </>;
103
108
  }
104
109
 
105
110
  render(Component);
@@ -111,26 +116,24 @@ describe('refs', () => {
111
116
  it('should handle spreading into composite refs', () => {
112
117
  let logs: string[] = [];
113
118
 
114
- component App() {
115
- let &[value] = track('test');
116
-
117
- function inputRef(node: HTMLInputElement) {
118
- logs.push('ref called');
119
- }
120
-
121
- const props = {
122
- id: 'example',
123
- value,
124
- [createRefKey()]: inputRef,
125
- };
126
-
127
- <input type="text" {...props} />
128
-
129
- <Input {...props} />
119
+ function App() {
120
+ return <>
121
+ let &[value] = track('test');
122
+ function inputRef(node: HTMLInputElement) {
123
+ logs.push('ref called');
124
+ }
125
+ const props = {
126
+ id: 'example',
127
+ value,
128
+ [createRefKey()]: inputRef,
129
+ };
130
+ <input type="text" {...props} />
131
+ <Input {...props} />
132
+ </>;
130
133
  }
131
134
 
132
- component Input({ id, value, ...rest }: PropsWithExtras<{ id: string; value: string }>) {
133
- <input type="text" {id} {value} {...rest} />
135
+ function Input({ id, value, ...rest }: PropsWithExtras<{ id: string; value: string }>) {
136
+ return <><input type="text" {id} {value} {...rest} /></>;
134
137
  }
135
138
 
136
139
  render(App);
@@ -139,14 +142,15 @@ describe('refs', () => {
139
142
  expect(logs).toEqual(['ref called', 'ref called']);
140
143
  });
141
144
 
142
- it('captures a host element into a Tracked via {ref tracker}', () => {
145
+ it('captures a host element into a Tracked via ref={tracker}', () => {
143
146
  let captured: Tracked<HTMLDivElement | null> | undefined;
144
147
 
145
- component Component() {
146
- const tracker = track<HTMLDivElement | null>(null);
147
- captured = tracker;
148
-
149
- <div {ref tracker}>{'Hello World'}</div>
148
+ function Component() {
149
+ return <>
150
+ const tracker = track<HTMLDivElement | null>(null);
151
+ captured = tracker;
152
+ <div ref={tracker}>{'Hello World'}</div>
153
+ </>;
150
154
  }
151
155
 
152
156
  render(Component);
@@ -158,17 +162,16 @@ describe('refs', () => {
158
162
  it('forwards a Tracked through a composite component via prop destructuring + spread', () => {
159
163
  let captured: Tracked<HTMLInputElement | null> | undefined;
160
164
 
161
- component Child({ id, ...rest }: PropsWithExtras<{ id: string }>) {
162
- // Symbol-keyed `ref` prop survives `...rest` destructuring and
163
- // arrives on the DOM element via spread.
164
- <input type="text" {id} {...rest} />
165
+ function Child({ id, ...rest }: PropsWithExtras<{ id: string }>) {
166
+ return <><input type="text" {id} {...rest} /></>;
165
167
  }
166
168
 
167
- component App() {
168
- const tracker = track<HTMLInputElement | null>(null);
169
- captured = tracker;
170
-
171
- <Child id="example" {ref tracker} />
169
+ function App() {
170
+ return <>
171
+ const tracker = track<HTMLInputElement | null>(null);
172
+ captured = tracker;
173
+ <Child id="example" ref={tracker} />
174
+ </>;
172
175
  }
173
176
 
174
177
  render(App);
@@ -177,20 +180,20 @@ describe('refs', () => {
177
180
  expect(captured!.value!.id).toBe('example');
178
181
  });
179
182
 
180
- it('assigns a host element to a plain let variable via {ref var}', () => {
183
+ it('assigns a host element to a plain let variable via ref={var}', () => {
181
184
  let captured: HTMLDivElement | null = null;
182
185
 
183
- component App() {
184
- let div: HTMLDivElement | undefined;
185
-
186
- <div {ref div}>{'Hello World'}</div>
187
-
188
- // Read the captured element through an effect so the assertion
189
- // observes the post-mount value (component setup runs before the
190
- // element is created).
191
- effect(() => {
192
- captured = div ?? null;
193
- });
186
+ function App() {
187
+ return <>
188
+ let div: HTMLDivElement | undefined;
189
+ <div ref={div}>{'Hello World'}</div>
190
+ // Read the captured element through an effect so the assertion
191
+ // observes the post-mount value (component setup runs before the
192
+ // element is created).
193
+ effect(() => {
194
+ captured = div ?? null;
195
+ });
196
+ </>;
194
197
  }
195
198
 
196
199
  render(App);
@@ -202,14 +205,14 @@ describe('refs', () => {
202
205
  it('assigns a host element to a plain let variable via ref={var}', () => {
203
206
  let captured: HTMLDivElement | null = null;
204
207
 
205
- component App() {
206
- let div: HTMLDivElement | undefined;
207
-
208
- <div ref={div}>{'Hello ref attr'}</div>
209
-
210
- effect(() => {
211
- captured = div ?? null;
212
- });
208
+ function App() {
209
+ return <>
210
+ let div: HTMLDivElement | undefined;
211
+ <div ref={div}>{'Hello ref attr'}</div>
212
+ effect(() => {
213
+ captured = div ?? null;
214
+ });
215
+ </>;
213
216
  }
214
217
 
215
218
  render(App);
@@ -221,14 +224,14 @@ describe('refs', () => {
221
224
  it('clears a plain let variable via ref={var} when the host element unmounts', () => {
222
225
  let div: HTMLDivElement | null | undefined;
223
226
 
224
- component App() {
225
- let &[show] = track(true);
226
-
227
- if (show) {
228
- <div ref={div}>{'Hello cleanup'}</div>
229
- }
230
-
231
- <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
227
+ function App() {
228
+ return <>
229
+ let &[show] = track(true);
230
+ if (show) {
231
+ <div ref={div}>{'Hello cleanup'}</div>
232
+ }
233
+ <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
234
+ </>;
232
235
  }
233
236
 
234
237
  render(App);
@@ -243,14 +246,14 @@ describe('refs', () => {
243
246
  it('assigns a host element to a member expression via ref={state.var}', () => {
244
247
  let captured: HTMLInputElement | null = null;
245
248
 
246
- component App() {
247
- const state: { input?: HTMLInputElement } = {};
248
-
249
- <input type="text" ref={state.input} />
250
-
251
- effect(() => {
252
- captured = state.input ?? null;
253
- });
249
+ function App() {
250
+ return <>
251
+ const state: { input?: HTMLInputElement } = {};
252
+ <input type="text" ref={state.input} />
253
+ effect(() => {
254
+ captured = state.input ?? null;
255
+ });
256
+ </>;
254
257
  }
255
258
 
256
259
  render(App);
@@ -262,18 +265,18 @@ describe('refs', () => {
262
265
  let input: HTMLInputElement | null | undefined;
263
266
  let previous: HTMLInputElement | undefined;
264
267
 
265
- component Child(props: PropsWithExtras<{}>) {
266
- <input type="text" value="keep" {...props} />
268
+ function Child(props: PropsWithExtras<{}>) {
269
+ return <><input type="text" value="keep" {...props} /></>;
267
270
  }
268
271
 
269
- component App() {
270
- let &[show] = track(true);
271
-
272
- if (show) {
273
- <Child ref={input} />
274
- }
275
-
276
- <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
272
+ function App() {
273
+ return <>
274
+ let &[show] = track(true);
275
+ if (show) {
276
+ <Child ref={input} />
277
+ }
278
+ <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
279
+ </>;
277
280
  }
278
281
 
279
282
  render(App);
@@ -293,12 +296,13 @@ describe('refs', () => {
293
296
  () => {
294
297
  let logs: string[] = [];
295
298
 
296
- component App() {
297
- let cb = (node: HTMLDivElement) => {
298
- logs.push(`mount:${node.textContent}`);
299
- };
300
-
301
- <div {ref cb}>{'Hello'}</div>
299
+ function App() {
300
+ return <>
301
+ let cb = (node: HTMLDivElement) => {
302
+ logs.push(`mount:${node.textContent}`);
303
+ };
304
+ <div ref={cb}>{'Hello'}</div>
305
+ </>;
302
306
  }
303
307
 
304
308
  render(App);
@@ -312,12 +316,13 @@ describe('refs', () => {
312
316
  () => {
313
317
  let captured: Tracked<HTMLDivElement | null> | undefined;
314
318
 
315
- component App() {
316
- const tracker = track<HTMLDivElement | null>(null);
317
- let slot = tracker;
318
- captured = tracker;
319
-
320
- <div {ref slot}>{'Hello'}</div>
319
+ function App() {
320
+ return <>
321
+ const tracker = track<HTMLDivElement | null>(null);
322
+ let slot = tracker;
323
+ captured = tracker;
324
+ <div ref={slot}>{'Hello'}</div>
325
+ </>;
321
326
  }
322
327
 
323
328
  render(App);
@@ -329,18 +334,18 @@ describe('refs', () => {
329
334
  it('propagates a plain let variable through a composite component via {...rest}', () => {
330
335
  let captured: HTMLInputElement | null = null;
331
336
 
332
- component Child({ id, ...rest }: PropsWithExtras<{ id: string }>) {
333
- <input type="text" {id} {...rest} />
337
+ function Child({ id, ...rest }: PropsWithExtras<{ id: string }>) {
338
+ return <><input type="text" {id} {...rest} /></>;
334
339
  }
335
340
 
336
- component App() {
337
- let input: HTMLInputElement | undefined;
338
-
339
- <Child id="example" {ref input} />
340
-
341
- effect(() => {
342
- captured = input ?? null;
343
- });
341
+ function App() {
342
+ return <>
343
+ let input: HTMLInputElement | undefined;
344
+ <Child id="example" ref={input} />
345
+ effect(() => {
346
+ captured = input ?? null;
347
+ });
348
+ </>;
344
349
  }
345
350
 
346
351
  render(App);
@@ -351,23 +356,30 @@ describe('refs', () => {
351
356
  });
352
357
 
353
358
  it(
354
- 'clears a plain let variable forwarded through a named ref prop when the host element unmounts',
359
+ 'clears a plain let variable forwarded through a component ref prop when the host element unmounts',
355
360
  () => {
356
361
  let input: HTMLInputElement | null | undefined;
357
362
  let previous: HTMLInputElement | undefined;
358
363
 
359
- component Child(props: PropsWithExtras<{}>) {
360
- <input type="text" value="keep" {...props} />
364
+ function Child(props: PropsWithExtras<{}>) {
365
+ return <><input type="text" value="keep" {...props} /></>;
361
366
  }
362
367
 
363
- component App() {
364
- let &[show] = track(true);
365
-
366
- if (show) {
367
- <Child input_ref={ref input} />
368
- }
369
-
370
- <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
368
+ function App() {
369
+ return <>
370
+ let &[show] = track(true);
371
+ if (show) {
372
+ <Child
373
+ ref={(node: HTMLInputElement | null) => {
374
+ input = node;
375
+ return () => {
376
+ input = null;
377
+ };
378
+ }}
379
+ />
380
+ }
381
+ <button class="toggle" onClick={() => (show = false)}>{'hide'}</button>
382
+ </>;
371
383
  }
372
384
 
373
385
  render(App);
@@ -383,23 +395,33 @@ describe('refs', () => {
383
395
  },
384
396
  );
385
397
 
386
- it('clears a named ref prop when a host spread changes it to a regular prop', () => {
398
+ it('clears a component ref prop when a host spread changes it to a regular prop', () => {
387
399
  let input: HTMLInputElement | null | undefined;
388
400
  let previous: HTMLInputElement | undefined;
389
401
 
390
- component Child(props: PropsWithExtras<{}>) {
391
- let &[as_ref] = track(true);
392
-
393
- <input
394
- type="text"
395
- value="keep"
396
- {...(as_ref ? { input_ref: props.input_ref } : { input_ref: 'regular prop' })}
397
- />
398
- <button class="toggle" onClick={() => (as_ref = false)}>{'toggle'}</button>
402
+ function Child(props: PropsWithExtras<{}>) {
403
+ return <>
404
+ let &[as_ref] = track(true);
405
+ <input
406
+ type="text"
407
+ value="keep"
408
+ {...(as_ref ? { ref: props.ref } : { input_ref: 'regular prop' })}
409
+ />
410
+ <button class="toggle" onClick={() => (as_ref = false)}>{'toggle'}</button>
411
+ </>;
399
412
  }
400
413
 
401
- component App() {
402
- <Child input_ref={ref input} />
414
+ function App() {
415
+ return <>
416
+ <Child
417
+ ref={(node: HTMLInputElement | null) => {
418
+ input = node;
419
+ return () => {
420
+ input = null;
421
+ };
422
+ }}
423
+ />
424
+ </>;
403
425
  }
404
426
 
405
427
  render(App);
@@ -416,23 +438,33 @@ describe('refs', () => {
416
438
  expect(previous.getAttribute('input_ref')).toBe('regular prop');
417
439
  });
418
440
 
419
- it('removes a regular spread attribute when the key changes to a named ref prop', () => {
441
+ it('removes a regular spread attribute when the key changes to a component ref prop', () => {
420
442
  let input: HTMLInputElement | null | undefined;
421
443
  let previous: HTMLInputElement | undefined;
422
444
 
423
- component Child(props: PropsWithExtras<{}>) {
424
- let &[as_ref] = track(false);
425
-
426
- <input
427
- type="text"
428
- value="keep"
429
- {...(as_ref ? { input_ref: props.input_ref } : { input_ref: 'regular prop' })}
430
- />
431
- <button class="toggle" onClick={() => (as_ref = true)}>{'toggle'}</button>
445
+ function Child(props: PropsWithExtras<{}>) {
446
+ return <>
447
+ let &[as_ref] = track(false);
448
+ <input
449
+ type="text"
450
+ value="keep"
451
+ {...(as_ref ? { ref: props.ref } : { input_ref: 'regular prop' })}
452
+ />
453
+ <button class="toggle" onClick={() => (as_ref = true)}>{'toggle'}</button>
454
+ </>;
432
455
  }
433
456
 
434
- component App() {
435
- <Child input_ref={ref input} />
457
+ function App() {
458
+ return <>
459
+ <Child
460
+ ref={(node: HTMLInputElement | null) => {
461
+ input = node;
462
+ return () => {
463
+ input = null;
464
+ };
465
+ }}
466
+ />
467
+ </>;
436
468
  }
437
469
 
438
470
  render(App);
@@ -453,15 +485,16 @@ describe('refs', () => {
453
485
  let captured: Tracked<HTMLDivElement | null> | undefined;
454
486
  let toggle: Tracked<boolean> | undefined;
455
487
 
456
- component Component() {
457
- const tracker = track<HTMLDivElement | null>(null);
458
- const show = track(true);
459
- captured = tracker;
460
- toggle = show;
461
-
462
- if (show.value) {
463
- <div {ref tracker}>{'Hello World'}</div>
464
- }
488
+ function Component() {
489
+ return <>
490
+ const tracker = track<HTMLDivElement | null>(null);
491
+ const show = track(true);
492
+ captured = tracker;
493
+ toggle = show;
494
+ if (show.value) {
495
+ <div ref={tracker}>{'Hello World'}</div>
496
+ }
497
+ </>;
465
498
  }
466
499
 
467
500
  render(Component);
@@ -476,25 +509,23 @@ describe('refs', () => {
476
509
  it('should handle spreading props with a static ref', () => {
477
510
  let logs: string[] = [];
478
511
 
479
- component App() {
480
- let &[value] = track('test');
481
-
482
- function inputRef(node: HTMLInputElement) {
483
- logs.push('ref called');
484
- }
485
-
486
- const props = {
487
- id: 'example',
488
- value,
489
- };
490
-
491
- <input type="text" {ref inputRef} {...props} />
492
-
493
- <Input {ref inputRef} {...props} />
512
+ function App() {
513
+ return <>
514
+ let &[value] = track('test');
515
+ function inputRef(node: HTMLInputElement) {
516
+ logs.push('ref called');
517
+ }
518
+ const props = {
519
+ id: 'example',
520
+ value,
521
+ };
522
+ <input type="text" ref={inputRef} {...props} />
523
+ <Input ref={inputRef} {...props} />
524
+ </>;
494
525
  }
495
526
 
496
- component Input({ id, value, ...rest }: PropsWithExtras<{ id: string; value: string }>) {
497
- <input type="text" {id} {value} {...rest} />
527
+ function Input({ id, value, ...rest }: PropsWithExtras<{ id: string; value: string }>) {
528
+ return <><input type="text" {id} {value} {...rest} /></>;
498
529
  }
499
530
 
500
531
  render(App);