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
@@ -2,17 +2,20 @@ import { RippleSet, flushSync, track } from 'ripple';
2
2
 
3
3
  describe('RippleSet', () => {
4
4
  it('handles add and delete operations', () => {
5
- component SetTest() {
6
- let items = new RippleSet([1, 2, 3]);
7
-
8
- <button onClick={() => items.add(4)}>{'add'}</button>
9
- <button onClick={() => items.delete(2)}>{'delete'}</button>
10
- <Child {items} />
5
+ function SetTest() {
6
+ return <>
7
+ let items = new RippleSet([1, 2, 3]);
8
+ <button onClick={() => items.add(4)}>{'add'}</button>
9
+ <button onClick={() => items.delete(2)}>{'delete'}</button>
10
+ <Child {items} />
11
+ </>;
11
12
  }
12
13
 
13
- component Child({ items }: { items: RippleSet<number> }) {
14
- <pre>{JSON.stringify(items)}</pre>
15
- <pre>{items.size}</pre>
14
+ function Child({ items }: { items: RippleSet<number> }) {
15
+ return <>
16
+ <pre>{JSON.stringify(items)}</pre>
17
+ <pre>{items.size}</pre>
18
+ </>;
16
19
  }
17
20
 
18
21
  render(SetTest);
@@ -34,16 +37,19 @@ describe('RippleSet', () => {
34
37
  });
35
38
 
36
39
  it('handles clear operation', () => {
37
- component SetTest() {
38
- let items = new RippleSet([1, 2, 3]);
39
-
40
- <button onClick={() => items.clear()}>{'clear'}</button>
41
- <Child {items} />
40
+ function SetTest() {
41
+ return <>
42
+ let items = new RippleSet([1, 2, 3]);
43
+ <button onClick={() => items.clear()}>{'clear'}</button>
44
+ <Child {items} />
45
+ </>;
42
46
  }
43
47
 
44
- component Child({ items }: { items: RippleSet<number> }) {
45
- <pre>{JSON.stringify(items)}</pre>
46
- <pre>{items.size}</pre>
48
+ function Child({ items }: { items: RippleSet<number> }) {
49
+ return <>
50
+ <pre>{JSON.stringify(items)}</pre>
51
+ <pre>{items.size}</pre>
52
+ </>;
47
53
  }
48
54
 
49
55
  render(SetTest);
@@ -58,12 +64,13 @@ describe('RippleSet', () => {
58
64
  });
59
65
 
60
66
  it('handles has operation', () => {
61
- component SetTest() {
62
- let items = new RippleSet([1, 2, 3]);
63
- let &[hasValue] = track(() => items.has(2));
64
-
65
- <button onClick={() => items.delete(2)}>{'delete'}</button>
66
- <pre>{hasValue}</pre>
67
+ function SetTest() {
68
+ return <>
69
+ let items = new RippleSet([1, 2, 3]);
70
+ let &[hasValue] = track(() => items.has(2));
71
+ <button onClick={() => items.delete(2)}>{'delete'}</button>
72
+ <pre>{hasValue}</pre>
73
+ </>;
67
74
  }
68
75
 
69
76
  render(SetTest);
@@ -79,11 +86,12 @@ describe('RippleSet', () => {
79
86
  });
80
87
 
81
88
  it('creates empty RippleSet using RippleSet() shorthand syntax', () => {
82
- component SetTest() {
83
- let items = RippleSet();
84
-
85
- <button onClick={() => items.add(1)}>{'add'}</button>
86
- <pre>{items.size}</pre>
89
+ function SetTest() {
90
+ return <>
91
+ let items = RippleSet();
92
+ <button onClick={() => items.add(1)}>{'add'}</button>
93
+ <pre>{items.size}</pre>
94
+ </>;
87
95
  }
88
96
 
89
97
  render(SetTest);
@@ -98,13 +106,14 @@ describe('RippleSet', () => {
98
106
  });
99
107
 
100
108
  it('creates RippleSet with initial values using RippleSet() shorthand syntax', () => {
101
- component SetTest() {
102
- let items = RippleSet([1, 2, 3, 4]);
103
- let &[hasValue] = track(() => items.has(3));
104
-
105
- <button onClick={() => items.delete(3)}>{'delete'}</button>
106
- <pre>{items.size}</pre>
107
- <pre>{hasValue}</pre>
109
+ function SetTest() {
110
+ return <>
111
+ let items = RippleSet([1, 2, 3, 4]);
112
+ let &[hasValue] = track(() => items.has(3));
113
+ <button onClick={() => items.delete(3)}>{'delete'}</button>
114
+ <pre>{items.size}</pre>
115
+ <pre>{hasValue}</pre>
116
+ </>;
108
117
  }
109
118
 
110
119
  render(SetTest);
@@ -121,16 +130,16 @@ describe('RippleSet', () => {
121
130
  });
122
131
 
123
132
  it('handles all operations with RippleSet() shorthand syntax', () => {
124
- component SetTest() {
125
- let items = RippleSet([10, 20, 30]);
126
- let &[values] = track(() => Array.from(items.values()));
127
-
128
- <button onClick={() => items.add(40)}>{'add'}</button>
129
- <button onClick={() => items.delete(20)}>{'delete'}</button>
130
- <button onClick={() => items.clear()}>{'clear'}</button>
131
-
132
- <pre>{JSON.stringify(values)}</pre>
133
- <pre>{items.size}</pre>
133
+ function SetTest() {
134
+ return <>
135
+ let items = RippleSet([10, 20, 30]);
136
+ let &[values] = track(() => Array.from(items.values()));
137
+ <button onClick={() => items.add(40)}>{'add'}</button>
138
+ <button onClick={() => items.delete(20)}>{'delete'}</button>
139
+ <button onClick={() => items.clear()}>{'clear'}</button>
140
+ <pre>{JSON.stringify(values)}</pre>
141
+ <pre>{items.size}</pre>
142
+ </>;
134
143
  }
135
144
 
136
145
  render(SetTest);
@@ -3,18 +3,20 @@ import type { Component, PropsWithChildren, PropsWithExtras } from 'ripple';
3
3
 
4
4
  describe('SVG namespace handling', () => {
5
5
  it('should render static SVG elements with correct namespace', () => {
6
- component App() {
7
- <svg
8
- xmlns="http://www.w3.org/2000/svg"
9
- width="24"
10
- height="24"
11
- fill="none"
12
- stroke="currentColor"
13
- >
14
- <path d="m14 12 4 4 4-4" />
15
- <circle cx="12" cy="12" r="4" />
16
- <rect x="4" y="4" width="16" height="16" />
17
- </svg>
6
+ function App() {
7
+ return <>
8
+ <svg
9
+ xmlns="http://www.w3.org/2000/svg"
10
+ width="24"
11
+ height="24"
12
+ fill="none"
13
+ stroke="currentColor"
14
+ >
15
+ <path d="m14 12 4 4 4-4" />
16
+ <circle cx="12" cy="12" r="4" />
17
+ <rect x="4" y="4" width="16" height="16" />
18
+ </svg>
19
+ </>;
18
20
  }
19
21
 
20
22
  render(App);
@@ -40,25 +42,26 @@ describe('SVG namespace handling', () => {
40
42
  });
41
43
 
42
44
  it('should render dynamic SVG paths with for loop (original issue)', () => {
43
- component App() {
44
- const d = [
45
- 'm14 12 4 4 4-4',
46
- 'M18 16V7',
47
- 'm2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16',
48
- 'M3.304 13h6.392',
49
- ];
50
-
51
- <svg
52
- xmlns="http://www.w3.org/2000/svg"
53
- width="24"
54
- height="24"
55
- fill="none"
56
- stroke="currentColor"
57
- >
58
- for (const pathData of d) {
59
- <path d={pathData} />
60
- }
61
- </svg>
45
+ function App() {
46
+ return <>
47
+ const d = [
48
+ 'm14 12 4 4 4-4',
49
+ 'M18 16V7',
50
+ 'm2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16',
51
+ 'M3.304 13h6.392',
52
+ ];
53
+ <svg
54
+ xmlns="http://www.w3.org/2000/svg"
55
+ width="24"
56
+ height="24"
57
+ fill="none"
58
+ stroke="currentColor"
59
+ >
60
+ for (const pathData of d) {
61
+ <path d={pathData} />
62
+ }
63
+ </svg>
64
+ </>;
62
65
  }
63
66
 
64
67
  render(App);
@@ -92,22 +95,23 @@ describe('SVG namespace handling', () => {
92
95
  });
93
96
 
94
97
  it('should handle mixed static and dynamic SVG elements', () => {
95
- component App() {
96
- const dynamicPaths = ['M12 2L2 7v10c0 5.55 3.84 10 9 11 5.16-1 9-5.45 9-11V7l-10-5z'];
97
-
98
- <svg
99
- xmlns="http://www.w3.org/2000/svg"
100
- width="24"
101
- height="24"
102
- fill="none"
103
- stroke="currentColor"
104
- >
105
- <circle cx="12" cy="12" r="10" />
106
- for (const pathData of dynamicPaths) {
107
- <path d={pathData} />
108
- }
109
- <rect x="4" y="4" width="16" height="16" />
110
- </svg>
98
+ function App() {
99
+ return <>
100
+ const dynamicPaths = ['M12 2L2 7v10c0 5.55 3.84 10 9 11 5.16-1 9-5.45 9-11V7l-10-5z'];
101
+ <svg
102
+ xmlns="http://www.w3.org/2000/svg"
103
+ width="24"
104
+ height="24"
105
+ fill="none"
106
+ stroke="currentColor"
107
+ >
108
+ <circle cx="12" cy="12" r="10" />
109
+ for (const pathData of dynamicPaths) {
110
+ <path d={pathData} />
111
+ }
112
+ <rect x="4" y="4" width="16" height="16" />
113
+ </svg>
114
+ </>;
111
115
  }
112
116
 
113
117
  render(App);
@@ -130,19 +134,20 @@ describe('SVG namespace handling', () => {
130
134
  });
131
135
 
132
136
  it('should handle nested SVG groups with for loops', () => {
133
- component App() {
134
- const items = [
135
- { x: '10', y: '10', width: '20', height: '20' },
136
- { x: '40', y: '40', width: '20', height: '20' },
137
- ];
138
-
139
- <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
140
- <g fill="blue">
141
- for (const item of items) {
142
- <rect x={item.x} y={item.y} width={item.width} height={item.height} />
143
- }
144
- </g>
145
- </svg>
137
+ function App() {
138
+ return <>
139
+ const items = [
140
+ { x: '10', y: '10', width: '20', height: '20' },
141
+ { x: '40', y: '40', width: '20', height: '20' },
142
+ ];
143
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100">
144
+ <g fill="blue">
145
+ for (const item of items) {
146
+ <rect x={item.x} y={item.y} width={item.width} height={item.height} />
147
+ }
148
+ </g>
149
+ </svg>
150
+ </>;
146
151
  }
147
152
 
148
153
  render(App);
@@ -168,16 +173,17 @@ describe('SVG namespace handling', () => {
168
173
  });
169
174
 
170
175
  it('should handle SVG class attributes correctly', () => {
171
- component App() {
172
- let className = 'svg-element';
173
- const paths = ['M10 10 L20 20'];
174
-
175
- <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" class={className}>
176
- for (const pathData of paths) {
177
- <path d={pathData} class="dynamic-path" />
178
- }
179
- <circle cx="50" cy="50" r="10" class={className} />
180
- </svg>
176
+ function App() {
177
+ return <>
178
+ let className = 'svg-element';
179
+ const paths = ['M10 10 L20 20'];
180
+ <svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" class={className}>
181
+ for (const pathData of paths) {
182
+ <path d={pathData} class="dynamic-path" />
183
+ }
184
+ <circle cx="50" cy="50" r="10" class={className} />
185
+ </svg>
186
+ </>;
181
187
  }
182
188
 
183
189
  render(App);
@@ -202,14 +208,16 @@ describe('SVG namespace handling', () => {
202
208
  });
203
209
 
204
210
  it('should handle namespace transitions with foreignObject', () => {
205
- component App() {
206
- <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
207
- <rect x="10" y="10" width="50" height="50" fill="red" />
208
- <foreignObject x="100" y="100" width="80" height="80">
209
- <div class="html-content">{'HTML inside SVG'}</div>
210
- </foreignObject>
211
- <circle cx="50" cy="150" r="20" fill="blue" />
212
- </svg>
211
+ function App() {
212
+ return <>
213
+ <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
214
+ <rect x="10" y="10" width="50" height="50" fill="red" />
215
+ <foreignObject x="100" y="100" width="80" height="80">
216
+ <div class="html-content">{'HTML inside SVG'}</div>
217
+ </foreignObject>
218
+ <circle cx="50" cy="150" r="20" fill="blue" />
219
+ </svg>
220
+ </>;
213
221
  }
214
222
 
215
223
  render(App);
@@ -232,21 +240,25 @@ describe('SVG namespace handling', () => {
232
240
  });
233
241
 
234
242
  it('should render SVG with children as svg elements', () => {
235
- component SVG({ children }: PropsWithChildren<{}>) {
236
- <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
237
- {children}
238
- </svg>
243
+ function SVG({ children }: PropsWithChildren<{}>) {
244
+ return <>
245
+ <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
246
+ {children}
247
+ </svg>
248
+ </>;
239
249
  }
240
250
 
241
- component App() {
242
- let isDiamond = true;
243
- <SVG>
244
- if (isDiamond) {
245
- <polygon points="0,0 30,0 15,10" />
246
- } else {
247
- <polygon points="0,0 30,0 15,10" />
248
- }
249
- </SVG>
251
+ function App() {
252
+ return <>
253
+ let isDiamond = true;
254
+ <SVG>
255
+ if (isDiamond) {
256
+ <polygon points="0,0 30,0 15,10" />
257
+ } else {
258
+ <polygon points="0,0 30,0 15,10" />
259
+ }
260
+ </SVG>
261
+ </>;
250
262
  }
251
263
 
252
264
  render(App);
@@ -259,18 +271,20 @@ describe('SVG namespace handling', () => {
259
271
  });
260
272
 
261
273
  it('should render SVG with props as svg elements', () => {
262
- component SVG({ Polygon }: PropsWithExtras<{ Polygon: Component }>) {
263
- <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
264
- <Polygon />
265
- </svg>
274
+ function SVG({ Polygon }: PropsWithExtras<{ Polygon: Component }>) {
275
+ return <>
276
+ <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
277
+ <Polygon />
278
+ </svg>
279
+ </>;
266
280
  }
267
281
 
268
- component App() {
269
- <SVG {Polygon} />
282
+ function App() {
283
+ return <><SVG {Polygon} /></>;
270
284
  }
271
285
 
272
- component Polygon() {
273
- <polygon points="0,0 30,0 15,10" />
286
+ function Polygon() {
287
+ return <><polygon points="0,0 30,0 15,10" /></>;
274
288
  }
275
289
 
276
290
  render(App);
@@ -283,17 +297,21 @@ describe('SVG namespace handling', () => {
283
297
  });
284
298
 
285
299
  it('should render SVG with children as dynamic elements', () => {
286
- component SVG({ children }: PropsWithChildren<{}>) {
287
- <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
288
- {children}
289
- </svg>
300
+ function SVG({ children }: PropsWithChildren<{}>) {
301
+ return <>
302
+ <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
303
+ {children}
304
+ </svg>
305
+ </>;
290
306
  }
291
307
 
292
- component App() {
293
- let &[dynTag] = track('polygon');
294
- <SVG>
295
- <@dynTag points="0,0 30,0 15,10" />
296
- </SVG>
308
+ function App() {
309
+ return <>
310
+ let &[dynTag] = track('polygon');
311
+ <SVG>
312
+ <@dynTag points="0,0 30,0 15,10" />
313
+ </SVG>
314
+ </>;
297
315
  }
298
316
 
299
317
  render(App);
@@ -306,21 +324,25 @@ describe('SVG namespace handling', () => {
306
324
  });
307
325
 
308
326
  it('should render SVG with children as dynamic components', () => {
309
- component SVG({ children }: PropsWithChildren<{}>) {
310
- <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
311
- {children}
312
- </svg>
327
+ function SVG({ children }: PropsWithChildren<{}>) {
328
+ return <>
329
+ <svg width={20} height={20} fill="blue" viewBox="0 0 30 10" preserveAspectRatio="none">
330
+ {children}
331
+ </svg>
332
+ </>;
313
333
  }
314
334
 
315
- component Polygon({ points }: PropsWithExtras<{ points: string }>) {
316
- <polygon {points} />
335
+ function Polygon({ points }: PropsWithExtras<{ points: string }>) {
336
+ return <><polygon {points} /></>;
317
337
  }
318
338
 
319
- component App() {
320
- let &[Component] = track(() => Polygon);
321
- <SVG>
322
- <@Component points="0,0 30,0 15,10" />
323
- </SVG>
339
+ function App() {
340
+ return <>
341
+ let &[Component] = track(() => Polygon);
342
+ <SVG>
343
+ <@Component points="0,0 30,0 15,10" />
344
+ </SVG>
345
+ </>;
324
346
  }
325
347
 
326
348
  render(App);
@@ -333,23 +355,29 @@ describe('SVG namespace handling', () => {
333
355
  });
334
356
 
335
357
  it('should render SVG as a dynamic top element with any dynamic children elements', () => {
336
- component SVG({ children }: PropsWithChildren<{}>) {
337
- let &[tag] = track('svg');
338
- <@tag width={100} height={50} fill="red" viewBox="0 0 30 10" preserveAspectRatio="none">
339
- {children}
340
- </@tag>
358
+ function SVG({ children }: PropsWithChildren<{}>) {
359
+ return <>
360
+ let &[tag] = track('svg');
361
+ <@tag width={100} height={50} fill="red" viewBox="0 0 30 10" preserveAspectRatio="none">
362
+ {children}
363
+ </@tag>
364
+ </>;
341
365
  }
342
366
 
343
- component Polygon({ points }: PropsWithExtras<{ points: string }>) {
344
- let &[dynTag] = track('polygon');
345
- <@dynTag {points} />
367
+ function Polygon({ points }: PropsWithExtras<{ points: string }>) {
368
+ return <>
369
+ let &[dynTag] = track('polygon');
370
+ <@dynTag {points} />
371
+ </>;
346
372
  }
347
373
 
348
- component App() {
349
- let &[Component] = track(() => Polygon);
350
- <SVG>
351
- <@Component points="0,0 30,0 15,10" />
352
- </SVG>
374
+ function App() {
375
+ return <>
376
+ let &[Component] = track(() => Polygon);
377
+ <SVG>
378
+ <@Component points="0,0 30,0 15,10" />
379
+ </SVG>
380
+ </>;
353
381
  }
354
382
 
355
383
  render(App);
@@ -362,42 +390,43 @@ describe('SVG namespace handling', () => {
362
390
  });
363
391
 
364
392
  it('should compare static vs dynamic SVG rendering (original problem case)', () => {
365
- component App() {
366
- const d = [
367
- 'm14 12 4 4 4-4',
368
- 'M18 16V7',
369
- 'm2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16',
370
- 'M3.304 13h6.392',
371
- ];
372
-
373
- <div class="container">
374
- <svg
375
- xmlns="http://www.w3.org/2000/svg"
376
- width="24"
377
- height="24"
378
- fill="none"
379
- stroke="currentColor"
380
- class="dynamic-svg"
381
- >
382
- for (const path of d) {
383
- <path d={path} />
384
- }
385
- </svg>
386
-
387
- <svg
388
- xmlns="http://www.w3.org/2000/svg"
389
- width="24"
390
- height="24"
391
- fill="none"
392
- stroke="currentColor"
393
- class="static-svg"
394
- >
395
- <path d="m14 12 4 4 4-4" />
396
- <path d="M18 16V7" />
397
- <path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
398
- <path d="M3.304 13h6.392" />
399
- </svg>
400
- </div>
393
+ function App() {
394
+ return <>
395
+ const d = [
396
+ 'm14 12 4 4 4-4',
397
+ 'M18 16V7',
398
+ 'm2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16',
399
+ 'M3.304 13h6.392',
400
+ ];
401
+ <div class="container">
402
+ <svg
403
+ xmlns="http://www.w3.org/2000/svg"
404
+ width="24"
405
+ height="24"
406
+ fill="none"
407
+ stroke="currentColor"
408
+ class="dynamic-svg"
409
+ >
410
+ for (const path of d) {
411
+ <path d={path} />
412
+ }
413
+ </svg>
414
+
415
+ <svg
416
+ xmlns="http://www.w3.org/2000/svg"
417
+ width="24"
418
+ height="24"
419
+ fill="none"
420
+ stroke="currentColor"
421
+ class="static-svg"
422
+ >
423
+ <path d="m14 12 4 4 4-4" />
424
+ <path d="M18 16V7" />
425
+ <path d="m2 16 4.039-9.69a.5.5 0 0 1 .923 0L11 16" />
426
+ <path d="M3.304 13h6.392" />
427
+ </svg>
428
+ </div>
429
+ </>;
401
430
  }
402
431
 
403
432
  render(App);
@@ -440,25 +469,26 @@ describe('SVG namespace handling', () => {
440
469
  });
441
470
 
442
471
  it('should render dynamic SVG elements dispatched via a switch inside a for loop', () => {
443
- component App() {
444
- const iconNodes: [string, Record<string, string>][] = [
445
- ['path', { d: 'm14 12 4 4 4-4' }],
446
- ['circle', { cx: '12', cy: '12', r: '4' }],
447
- ['path', { d: 'M18 16V7' }],
448
- ];
449
-
450
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
451
- for (const [tag, attrs] of iconNodes) {
452
- switch (tag) {
453
- case 'path':
454
- <path {...attrs} />
455
- break;
456
- case 'circle':
457
- <circle {...attrs} />
458
- break;
472
+ function App() {
473
+ return <>
474
+ const iconNodes: [string, Record<string, string>][] = [
475
+ ['path', { d: 'm14 12 4 4 4-4' }],
476
+ ['circle', { cx: '12', cy: '12', r: '4' }],
477
+ ['path', { d: 'M18 16V7' }],
478
+ ];
479
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
480
+ for (const [tag, attrs] of iconNodes) {
481
+ switch (tag) {
482
+ case 'path':
483
+ <path {...attrs} />
484
+ break;
485
+ case 'circle':
486
+ <circle {...attrs} />
487
+ break;
488
+ }
459
489
  }
460
- }
461
- </svg>
490
+ </svg>
491
+ </>;
462
492
  }
463
493
 
464
494
  render(App);