ripple 0.3.68 → 0.3.70

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 +126 -259
  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 -131
  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,8 +2,8 @@ import { create_ssr_stream } from 'ripple/server';
2
2
 
3
3
  describe('create_ssr_stream', () => {
4
4
  it('renders SSR HTML into the injected sink and exposes a web stream', async () => {
5
- component App() {
6
- <div>{'Hello, streaming SSR!'}</div>
5
+ function App() {
6
+ return <><div>{'Hello, streaming SSR!'}</div></>;
7
7
  }
8
8
 
9
9
  const { stream, sink } = create_ssr_stream();
@@ -16,8 +16,8 @@ describe('create_ssr_stream', () => {
16
16
  });
17
17
 
18
18
  it('closes the public stream when streaming finishes successfully', async () => {
19
- component App() {
20
- <p>{'stream closed'}</p>
19
+ function App() {
20
+ return <><p>{'stream closed'}</p></>;
21
21
  }
22
22
 
23
23
  const { stream, sink } = create_ssr_stream();
@@ -42,74 +42,3 @@ describe('create_ssr_stream', () => {
42
42
  expect(terminal_read.done).toBe(true);
43
43
  });
44
44
  });
45
-
46
- // import { renderToStream } from 'ripple/server';
47
-
48
- // describe('render to stream', () => {
49
- // test('renderToStream renders a simple component', async ({ expect }) => {
50
- // component Basic() {
51
- // <div>{'Hello, streaming SSR!'}</div>
52
- // }
53
-
54
- // const stream = renderToStream(Basic);
55
-
56
- // let result = '';
57
- // await new Promise((resolve) => {
58
- // stream.on('data', (chunk) => {
59
- // result += chunk.toString();
60
- // });
61
- // stream.on('end', resolve);
62
- // });
63
-
64
- // expect(result).toBe('<div>Hello, streaming SSR!</div>');
65
- // });
66
-
67
- // test('renderToStream handles async components', async ({ expect }) => {
68
- // component AsyncComponent() {
69
- // await new Promise((resolve) => setTimeout(resolve, 10));
70
- // <p>{'Async content loaded.'}</p>
71
- // }
72
-
73
- // const stream = renderToStream(AsyncComponent);
74
-
75
- // let result = '';
76
- // await new Promise((resolve) => {
77
- // stream.on('data', (chunk) => {
78
- // result += chunk.toString();
79
- // });
80
- // stream.on('end', resolve);
81
- // });
82
-
83
- // expect(result).toBe('<p>Async content loaded.</p>');
84
- // });
85
-
86
- // test('renderToStream handles await blocks with pending state', async ({ expect }) => {
87
- // component AwaitComponent() {
88
- // let data = 'initial';
89
- // await new Promise((resolve) => setTimeout(() => {
90
- // data = 'resolved';
91
- // resolve('');
92
- // }, 20));
93
- // try {
94
- // <div>
95
- // {'Data: '}
96
- // {data}
97
- // </div>
98
- // } pending {
99
- // <div>{'Loading...'}</div>
100
- // }
101
- // }
102
-
103
- // const stream = renderToStream(AwaitComponent);
104
-
105
- // let result = '';
106
- // await new Promise((resolve) => {
107
- // stream.on('data', (chunk) => {
108
- // result += chunk.toString();
109
- // });
110
- // stream.on('end', resolve);
111
- // });
112
-
113
- // expect(result).toBe('<!--[--><div>Loading...</div><div>Data: resolved</div><!--]-->');
114
- // });
115
- // });
@@ -1,21 +1,27 @@
1
1
  import { track } from 'ripple';
2
2
  import { compile } from '@tsrx/ripple';
3
3
 
4
- describe('{style} directive (server)', () => {
4
+ const external_styles = <style>
5
+ .external {
6
+ color: tomato;
7
+ }
8
+ </style>;
9
+
10
+ describe('style class maps (server)', () => {
5
11
  describe('basic usage with components', () => {
6
- it('passes scoped class to a child component via {style}', async () => {
7
- component Child({ className }: { className: string }) {
8
- <div class={className}>{'styled child'}</div>
12
+ it('passes scoped classes to a child component via a style expression', async () => {
13
+ function Child({ className }: { className: string }) {
14
+ return <><div class={className}>{'styled child'}</div></>;
9
15
  }
10
16
 
11
- component Parent() {
12
- <Child className={style 'highlight'} />
13
-
14
- <style>
17
+ function Parent() {
18
+ const styles = <style>
15
19
  .highlight {
16
20
  color: red;
17
21
  }
18
- </style>
22
+ </style>;
23
+
24
+ return <><Child className={styles.highlight} /></>;
19
25
  }
20
26
 
21
27
  const { body } = await render(Parent);
@@ -29,23 +35,25 @@ describe('{style} directive (server)', () => {
29
35
  expect(classes.some((cls: string) => cls === 'highlight')).toBe(true);
30
36
  });
31
37
 
32
- it('passes multiple {style} classes to a child component', async () => {
33
- component Child({ primary, secondary }: { primary: string; secondary: string }) {
34
- <div class={primary}>{'primary'}</div>
35
- <span class={secondary}>{'secondary'}</span>
38
+ it('passes multiple style expression classes to a child component', async () => {
39
+ function Child({ primary, secondary }: { primary: string; secondary: string }) {
40
+ return <>
41
+ <div class={primary}>{'primary'}</div>
42
+ <span class={secondary}>{'secondary'}</span>
43
+ </>;
36
44
  }
37
45
 
38
- component Parent() {
39
- <Child primary={style 'primary'} secondary={style 'secondary'} />
40
-
41
- <style>
46
+ function Parent() {
47
+ const styles = <style>
42
48
  .primary {
43
49
  color: blue;
44
50
  }
45
51
  .secondary {
46
52
  color: gray;
47
53
  }
48
- </style>
54
+ </style>;
55
+
56
+ return <><Child primary={styles.primary} secondary={styles.secondary} /></>;
49
57
  }
50
58
 
51
59
  const { body } = await render(Parent);
@@ -68,19 +76,19 @@ describe('{style} directive (server)', () => {
68
76
  });
69
77
 
70
78
  describe('parent styling applied to child', () => {
71
- it('allows parent to style child elements via {style} prop', async () => {
72
- component Button({ extraClass }: { extraClass?: string }) {
73
- <button class={extraClass ?? ''}>{'Click me'}</button>
79
+ it('allows parent to style child elements via a style expression prop', async () => {
80
+ function Button({ extraClass }: { extraClass?: string }) {
81
+ return <><button class={extraClass ?? ''}>{'Click me'}</button></>;
74
82
  }
75
83
 
76
- component App() {
77
- <Button extraClass={style 'fancy'} />
78
-
79
- <style>
84
+ function App() {
85
+ const styles = <style>
80
86
  .fancy {
81
87
  background: gold;
82
88
  }
83
- </style>
89
+ </style>;
90
+
91
+ return <><Button extraClass={styles.fancy} /></>;
84
92
  }
85
93
 
86
94
  const { body } = await render(App);
@@ -93,25 +101,26 @@ describe('{style} directive (server)', () => {
93
101
  expect(classes.some((cls: string) => cls === 'fancy')).toBe(true);
94
102
  });
95
103
 
96
- it('child can combine its own classes with parent {style} class', async () => {
97
- component Card({ className }: { className?: string }) {
98
- <div class={['card-base', className ?? '']}>{'card content'}</div>
99
-
100
- <style>
101
- .card-base {
102
- border: 1px solid black;
103
- }
104
- </style>
104
+ it('child can combine its own classes with a parent style expression class', async () => {
105
+ function Card({ className }: { className?: string }) {
106
+ return <>
107
+ <div class={['card-base', className ?? '']}>{'card content'}</div>
108
+ <style>
109
+ .card-base {
110
+ border: 1px solid black;
111
+ }
112
+ </style>
113
+ </>;
105
114
  }
106
115
 
107
- component App() {
108
- <Card className={style 'themed'} />
109
-
110
- <style>
116
+ function App() {
117
+ const styles = <style>
111
118
  .themed {
112
119
  background: purple;
113
120
  }
114
- </style>
121
+ </style>;
122
+
123
+ return <><Card className={styles.themed} /></>;
115
124
  }
116
125
 
117
126
  const { body } = await render(App);
@@ -124,56 +133,57 @@ describe('{style} directive (server)', () => {
124
133
  expect(classes.some((cls: string) => cls === 'themed')).toBe(true);
125
134
  });
126
135
 
127
- it(
128
- 'passes standalone class to child even when it also appears in descendant context',
129
- async () => {
130
- component Child({ cls }: { cls: string }) {
131
- <span class={cls}>{'text'}</span>
132
- }
136
+ it('passes a standalone class even when it also appears in descendant context', async () => {
137
+ function Child({ cls }: { cls: string }) {
138
+ return <><span class={cls}>{'text'}</span></>;
139
+ }
140
+
141
+ function App() {
142
+ const styles = <style>
143
+ .dual {
144
+ color: blue;
145
+ }
146
+ .parent .dual {
147
+ font-weight: bold;
148
+ }
149
+ </style>;
133
150
 
134
- component App() {
151
+ return <>
135
152
  <div class="parent">
136
- <Child cls={style 'dual'} />
153
+ <Child cls={styles.dual} />
137
154
  </div>
155
+ </>;
156
+ }
138
157
 
139
- <style>
140
- .dual {
141
- color: blue;
142
- }
143
- .parent .dual {
144
- font-weight: bold;
145
- }
146
- </style>
147
- }
148
-
149
- const { body } = await render(App);
150
- const { document } = parseHtml(body);
158
+ const { body } = await render(App);
159
+ const { document } = parseHtml(body);
151
160
 
152
- const span = document.querySelector('span');
153
- expect(span).toBeTruthy();
154
- const classes = Array.from(span.classList);
155
- expect(classes.some((cls: string) => cls.startsWith('tsrx-'))).toBe(true);
156
- expect(classes.some((cls: string) => cls === 'dual')).toBe(true);
157
- },
158
- );
161
+ const span = document.querySelector('span');
162
+ expect(span).toBeTruthy();
163
+ const classes = Array.from(span.classList);
164
+ expect(classes.some((cls: string) => cls.startsWith('tsrx-'))).toBe(true);
165
+ expect(classes.some((cls: string) => cls === 'dual')).toBe(true);
166
+ });
159
167
  });
160
168
 
161
- it('passes scoped class to a dynamic child component via {style}', async () => {
162
- component Child({ cls }: { cls: string }) {
163
- <span class={cls}>{'text'}</span>
169
+ it('passes scoped classes to a dynamic child component via a style expression', async () => {
170
+ function Child({ cls }: { cls: string }) {
171
+ return <><span class={cls}>{'text'}</span></>;
164
172
  }
165
173
 
166
- component Parent() {
167
- let dynamic = track(() => Child);
168
- <div class="wrapper">
169
- <@dynamic cls={style 'text'} />
170
- </div>
171
-
172
- <style>
174
+ function Parent() {
175
+ const styles = <style>
173
176
  .text {
174
177
  color: red;
175
178
  }
176
- </style>
179
+ </style>;
180
+
181
+ return <>
182
+ let dynamic = track(() => Child);
183
+ <div class="wrapper">
184
+ <@dynamic cls={styles.text} />
185
+ </div>
186
+ </>;
177
187
  }
178
188
 
179
189
  const { body } = await render(Parent);
@@ -186,34 +196,60 @@ describe('{style} directive (server)', () => {
186
196
  expect(classes.some((cls: string) => cls === 'text')).toBe(true);
187
197
  });
188
198
 
189
- it('preserves caller scoped hash through wrapper children', async () => {
190
- component Wrapper({ children }) {
191
- <div class="green">
192
- {'Wrapper'}
193
- {children}
194
- </div>
195
-
196
- <style>
197
- .green {
198
- color: green;
199
- }
200
- </style>
199
+ it('passes style expression classes declared outside the component', async () => {
200
+ function Child({ cls }: { cls: string }) {
201
+ return <><span class={cls}>{'text'}</span></>;
201
202
  }
202
203
 
203
- component Child() {
204
- <div class="red">{'Child'}</div>
204
+ function Parent() {
205
+ return <Child cls={external_styles.external} />;
206
+ }
205
207
 
206
- <style>
207
- .red {
208
- color: red;
209
- }
210
- </style>
208
+ const { body, css } = await render(Parent);
209
+ const { document } = parseHtml(body);
210
+
211
+ const span = document.querySelector('span');
212
+ expect(span).toBeTruthy();
213
+ const classes = Array.from(span.classList);
214
+ expect(classes.some((cls: string) => cls.startsWith('tsrx-'))).toBe(true);
215
+ expect(classes.some((cls: string) => cls === 'external')).toBe(true);
216
+
217
+ expect(css.size).toBeGreaterThan(0);
218
+ expect(Array.from(css).some((hash: string) => hash.startsWith('tsrx-'))).toBe(true);
219
+ });
220
+
221
+ it('preserves caller scoped hash through wrapper children', async () => {
222
+ function Wrapper({ children }) {
223
+ return <>
224
+ <div class="green">
225
+ {'Wrapper'}
226
+ {children}
227
+ </div>
228
+ <style>
229
+ .green {
230
+ color: green;
231
+ }
232
+ </style>
233
+ </>;
234
+ }
235
+
236
+ function Child() {
237
+ return <>
238
+ <div class="red">{'Child'}</div>
239
+ <style>
240
+ .red {
241
+ color: red;
242
+ }
243
+ </style>
244
+ </>;
211
245
  }
212
246
 
213
- component App() {
214
- <Wrapper>
215
- <Child />
216
- </Wrapper>
247
+ function App() {
248
+ return <>
249
+ <Wrapper>
250
+ <Child />
251
+ </Wrapper>
252
+ </>;
217
253
  }
218
254
 
219
255
  const { body } = await render(App);
@@ -232,22 +268,22 @@ describe('{style} directive (server)', () => {
232
268
  });
233
269
 
234
270
  it('applies caller scoped hash to slotted children through dynamic components', async () => {
235
- component Wrapper({ children }) {
236
- <section>{children}</section>
271
+ function Wrapper({ children }) {
272
+ return <><section>{children}</section></>;
237
273
  }
238
274
 
239
- component App() {
240
- const DynamicWrapper = track(() => Wrapper);
241
-
242
- <@DynamicWrapper>
243
- <div class="green">{'Slotted child'}</div>
244
- </@DynamicWrapper>
245
-
246
- <style>
247
- .green {
248
- color: green;
249
- }
250
- </style>
275
+ function App() {
276
+ return <>
277
+ const DynamicWrapper = track(() => Wrapper);
278
+ <@DynamicWrapper>
279
+ <div class="green">{'Slotted child'}</div>
280
+ </@DynamicWrapper>
281
+ <style>
282
+ .green {
283
+ color: green;
284
+ }
285
+ </style>
286
+ </>;
251
287
  }
252
288
 
253
289
  const { body } = await render(App);
@@ -261,40 +297,42 @@ describe('{style} directive (server)', () => {
261
297
  });
262
298
 
263
299
  describe('server compiler output', () => {
264
- it('inlines scoped class strings', () => {
300
+ it('emits style class maps', () => {
265
301
  const source = `
266
- component Child({ cls }: { cls: string }) {
302
+ function Child({ cls }: { cls: string }) { return <>
267
303
  <div class={cls}>{'text'}</div>
268
- }
269
- export component App() {
270
- <Child cls={style 'highlight'} />
271
-
272
- <style>
304
+ </>; }
305
+ export function App() {
306
+ const styles = <style>
273
307
  .highlight {
274
308
  color: red;
275
309
  }
276
- </style>
310
+ </style>;
311
+
312
+ return <>
313
+ <Child cls={styles.highlight} />
314
+ </>;
277
315
  }`;
278
316
  const { code } = compile(source, 'test.tsrx', { mode: 'server' });
279
317
 
280
318
  expect(code).toContain('highlight');
281
- expect(code).toMatch(/tsrx-[a-z0-9]+/);
319
+ expect(code).toMatch(/tsrx-[a-z0-9]+ highlight/);
282
320
  expect(code).toContain('register_css');
283
321
  });
284
322
 
285
323
  it('includes CSS hash in rendered HTML', async () => {
286
- component Child({ cls }: { cls: string }) {
287
- <div class={cls}>{'hello'}</div>
324
+ function Child({ cls }: { cls: string }) {
325
+ return <><div class={cls}>{'hello'}</div></>;
288
326
  }
289
327
 
290
- component App() {
291
- <Child cls={style 'styled'} />
292
-
293
- <style>
328
+ function App() {
329
+ const styles = <style>
294
330
  .styled {
295
331
  font-weight: bold;
296
332
  }
297
- </style>
333
+ </style>;
334
+
335
+ return <><Child cls={styles.styled} /></>;
298
336
  }
299
337
 
300
338
  const { body, css } = await render(App);