@alloy-js/core 0.23.0-dev.1 → 0.23.0-dev.8

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 (263) hide show
  1. package/CHANGELOG.md +0 -22
  2. package/dist/devtools/index.html +68 -0
  3. package/dist/src/binder.d.ts +2 -0
  4. package/dist/src/binder.d.ts.map +1 -1
  5. package/dist/src/binder.js +55 -12
  6. package/dist/src/binder.js.map +1 -1
  7. package/dist/src/components/AppendFile.d.ts.map +1 -1
  8. package/dist/src/components/AppendFile.js +14 -3
  9. package/dist/src/components/AppendFile.js.map +1 -1
  10. package/dist/src/components/Block.js +1 -1
  11. package/dist/src/components/Block.js.map +1 -1
  12. package/dist/src/components/Declaration.d.ts.map +1 -1
  13. package/dist/src/components/Declaration.js +2 -1
  14. package/dist/src/components/Declaration.js.map +1 -1
  15. package/dist/src/components/Scope.d.ts.map +1 -1
  16. package/dist/src/components/Scope.js +4 -1
  17. package/dist/src/components/Scope.js.map +1 -1
  18. package/dist/src/components/TemplateFile.d.ts.map +1 -1
  19. package/dist/src/components/TemplateFile.js +18 -3
  20. package/dist/src/components/TemplateFile.js.map +1 -1
  21. package/dist/src/content-slot.d.ts.map +1 -1
  22. package/dist/src/content-slot.js +6 -5
  23. package/dist/src/content-slot.js.map +1 -1
  24. package/dist/src/context.d.ts.map +1 -1
  25. package/dist/src/context.js +8 -1
  26. package/dist/src/context.js.map +1 -1
  27. package/dist/src/debug/cli.d.ts +6 -0
  28. package/dist/src/debug/cli.d.ts.map +1 -0
  29. package/dist/src/{debug.js → debug/cli.js} +79 -82
  30. package/dist/src/debug/cli.js.map +1 -0
  31. package/dist/src/debug/diagnostics.test.d.ts +2 -0
  32. package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
  33. package/dist/src/debug/diagnostics.test.js +45 -0
  34. package/dist/src/debug/diagnostics.test.js.map +1 -0
  35. package/dist/src/debug/effects.d.ts +69 -0
  36. package/dist/src/debug/effects.d.ts.map +1 -0
  37. package/dist/src/debug/effects.js +228 -0
  38. package/dist/src/debug/effects.js.map +1 -0
  39. package/dist/src/debug/effects.test.d.ts +2 -0
  40. package/dist/src/debug/effects.test.d.ts.map +1 -0
  41. package/dist/src/debug/effects.test.js +86 -0
  42. package/dist/src/debug/effects.test.js.map +1 -0
  43. package/dist/src/debug/files.d.ts +14 -0
  44. package/dist/src/debug/files.d.ts.map +1 -0
  45. package/dist/src/debug/files.js +40 -0
  46. package/dist/src/debug/files.js.map +1 -0
  47. package/dist/src/debug/files.test.d.ts +2 -0
  48. package/dist/src/debug/files.test.d.ts.map +1 -0
  49. package/dist/src/debug/files.test.js +89 -0
  50. package/dist/src/debug/files.test.js.map +1 -0
  51. package/dist/src/debug/index.d.ts +60 -0
  52. package/dist/src/debug/index.d.ts.map +1 -0
  53. package/dist/src/debug/index.js +68 -0
  54. package/dist/src/debug/index.js.map +1 -0
  55. package/dist/src/debug/render.d.ts +57 -0
  56. package/dist/src/debug/render.d.ts.map +1 -0
  57. package/dist/src/debug/render.js +519 -0
  58. package/dist/src/debug/render.js.map +1 -0
  59. package/dist/src/debug/render.test.d.ts +2 -0
  60. package/dist/src/debug/render.test.d.ts.map +1 -0
  61. package/dist/src/debug/render.test.js +328 -0
  62. package/dist/src/debug/render.test.js.map +1 -0
  63. package/dist/src/debug/serialize.d.ts +9 -0
  64. package/dist/src/debug/serialize.d.ts.map +1 -0
  65. package/dist/src/debug/serialize.js +70 -0
  66. package/dist/src/debug/serialize.js.map +1 -0
  67. package/dist/src/debug/symbols.d.ts +9 -0
  68. package/dist/src/debug/symbols.d.ts.map +1 -0
  69. package/dist/src/debug/symbols.js +164 -0
  70. package/dist/src/debug/symbols.js.map +1 -0
  71. package/dist/src/debug/symbols.test.d.ts +2 -0
  72. package/dist/src/debug/symbols.test.d.ts.map +1 -0
  73. package/dist/src/debug/symbols.test.js +104 -0
  74. package/dist/src/debug/symbols.test.js.map +1 -0
  75. package/dist/src/debug/trace.d.ts +342 -0
  76. package/dist/src/debug/trace.d.ts.map +1 -0
  77. package/dist/src/debug/trace.js +443 -0
  78. package/dist/src/debug/trace.js.map +1 -0
  79. package/dist/src/devtools/devtools-protocol.d.ts +232 -0
  80. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
  81. package/dist/src/devtools/devtools-protocol.js +2 -0
  82. package/dist/src/devtools/devtools-protocol.js.map +1 -0
  83. package/dist/src/devtools/devtools-server.browser.d.ts +28 -0
  84. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
  85. package/dist/src/devtools/devtools-server.browser.js +36 -0
  86. package/dist/src/devtools/devtools-server.browser.js.map +1 -0
  87. package/dist/src/devtools/devtools-server.d.ts +72 -0
  88. package/dist/src/devtools/devtools-server.d.ts.map +1 -0
  89. package/dist/src/devtools/devtools-server.js +256 -0
  90. package/dist/src/devtools/devtools-server.js.map +1 -0
  91. package/dist/src/devtools/devtools-transport.d.ts +23 -0
  92. package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
  93. package/dist/src/devtools/devtools-transport.js +114 -0
  94. package/dist/src/devtools/devtools-transport.js.map +1 -0
  95. package/dist/src/devtools-entry.browser.d.ts +4 -0
  96. package/dist/src/devtools-entry.browser.d.ts.map +1 -0
  97. package/dist/src/devtools-entry.browser.js +2 -0
  98. package/dist/src/devtools-entry.browser.js.map +1 -0
  99. package/dist/src/devtools-entry.d.ts +4 -0
  100. package/dist/src/devtools-entry.d.ts.map +1 -0
  101. package/dist/src/devtools-entry.js +2 -0
  102. package/dist/src/devtools-entry.js.map +1 -0
  103. package/dist/src/diagnostics.d.ts +34 -0
  104. package/dist/src/diagnostics.d.ts.map +1 -0
  105. package/dist/src/diagnostics.js +89 -0
  106. package/dist/src/diagnostics.js.map +1 -0
  107. package/dist/src/index.d.ts +3 -2
  108. package/dist/src/index.d.ts.map +1 -1
  109. package/dist/src/index.js +3 -2
  110. package/dist/src/index.js.map +1 -1
  111. package/dist/src/print-hook.d.ts +14 -0
  112. package/dist/src/print-hook.d.ts.map +1 -0
  113. package/dist/src/print-hook.js +10 -0
  114. package/dist/src/print-hook.js.map +1 -0
  115. package/dist/src/reactive-union-set.d.ts.map +1 -1
  116. package/dist/src/reactive-union-set.js +15 -0
  117. package/dist/src/reactive-union-set.js.map +1 -1
  118. package/dist/src/reactivity.d.ts +17 -3
  119. package/dist/src/reactivity.d.ts.map +1 -1
  120. package/dist/src/reactivity.js +162 -14
  121. package/dist/src/reactivity.js.map +1 -1
  122. package/dist/src/render-stack.d.ts +17 -1
  123. package/dist/src/render-stack.d.ts.map +1 -1
  124. package/dist/src/render-stack.js +57 -1
  125. package/dist/src/render-stack.js.map +1 -1
  126. package/dist/src/render.d.ts +8 -15
  127. package/dist/src/render.d.ts.map +1 -1
  128. package/dist/src/render.js +362 -103
  129. package/dist/src/render.js.map +1 -1
  130. package/dist/src/resource.d.ts.map +1 -1
  131. package/dist/src/resource.js +5 -0
  132. package/dist/src/resource.js.map +1 -1
  133. package/dist/src/scheduler.d.ts +3 -0
  134. package/dist/src/scheduler.d.ts.map +1 -1
  135. package/dist/src/scheduler.js +45 -2
  136. package/dist/src/scheduler.js.map +1 -1
  137. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  138. package/dist/src/symbols/basic-symbol.js +6 -1
  139. package/dist/src/symbols/basic-symbol.js.map +1 -1
  140. package/dist/src/symbols/decl.d.ts.map +1 -1
  141. package/dist/src/symbols/decl.js +5 -1
  142. package/dist/src/symbols/decl.js.map +1 -1
  143. package/dist/src/symbols/output-scope.d.ts +2 -1
  144. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  145. package/dist/src/symbols/output-scope.js +13 -8
  146. package/dist/src/symbols/output-scope.js.map +1 -1
  147. package/dist/src/symbols/output-symbol.d.ts +1 -0
  148. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  149. package/dist/src/symbols/output-symbol.js +23 -6
  150. package/dist/src/symbols/output-symbol.js.map +1 -1
  151. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  152. package/dist/src/symbols/symbol-flow.js +22 -6
  153. package/dist/src/symbols/symbol-flow.js.map +1 -1
  154. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  155. package/dist/src/symbols/symbol-slot.js +15 -0
  156. package/dist/src/symbols/symbol-slot.js.map +1 -1
  157. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  158. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  159. package/dist/src/symbols/symbol-slot.test.js +35 -0
  160. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  161. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  162. package/dist/src/symbols/symbol-table.js +6 -5
  163. package/dist/src/symbols/symbol-table.js.map +1 -1
  164. package/dist/src/trace.d.ts +2 -0
  165. package/dist/src/trace.d.ts.map +1 -0
  166. package/dist/src/trace.js +2 -0
  167. package/dist/src/trace.js.map +1 -0
  168. package/dist/src/tracer.d.ts +2 -228
  169. package/dist/src/tracer.d.ts.map +1 -1
  170. package/dist/src/tracer.js +5 -298
  171. package/dist/src/tracer.js.map +1 -1
  172. package/dist/src/utils.d.ts.map +1 -1
  173. package/dist/src/utils.js +5 -0
  174. package/dist/src/utils.js.map +1 -1
  175. package/dist/test/components/append-file.test.d.ts.map +1 -1
  176. package/dist/test/components/append-file.test.js +18 -10
  177. package/dist/test/components/append-file.test.js.map +1 -1
  178. package/dist/test/components/template-file.test.d.ts.map +1 -1
  179. package/dist/test/components/template-file.test.js +6 -4
  180. package/dist/test/components/template-file.test.js.map +1 -1
  181. package/dist/test/rendering/basic.test.js +3 -0
  182. package/dist/test/rendering/basic.test.js.map +1 -1
  183. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -1
  184. package/dist/test/rendering/print-render-stack.test.js +91 -98
  185. package/dist/test/rendering/print-render-stack.test.js.map +1 -1
  186. package/dist/testing/create-test-wrapper.d.ts +1 -1
  187. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  188. package/dist/testing/create-test-wrapper.js +1 -1
  189. package/dist/testing/create-test-wrapper.js.map +1 -1
  190. package/dist/testing/devtools-utils.d.ts +26 -0
  191. package/dist/testing/devtools-utils.d.ts.map +1 -0
  192. package/dist/testing/devtools-utils.js +140 -0
  193. package/dist/testing/devtools-utils.js.map +1 -0
  194. package/dist/testing/extend-expect.d.ts.map +1 -1
  195. package/dist/testing/extend-expect.js +63 -1
  196. package/dist/testing/extend-expect.js.map +1 -1
  197. package/dist/testing/render.d.ts +2 -2
  198. package/dist/testing/render.d.ts.map +1 -1
  199. package/dist/testing/render.js +2 -2
  200. package/dist/testing/render.js.map +1 -1
  201. package/dist/tsconfig.tsbuildinfo +1 -1
  202. package/package.json +21 -7
  203. package/scripts/copy-devtools-ui.mjs +26 -0
  204. package/src/binder.ts +71 -16
  205. package/src/components/AppendFile.tsx +14 -9
  206. package/src/components/Block.tsx +1 -1
  207. package/src/components/Declaration.tsx +2 -1
  208. package/src/components/Scope.tsx +4 -1
  209. package/src/components/TemplateFile.tsx +18 -9
  210. package/src/content-slot.tsx +6 -6
  211. package/src/context.ts +15 -4
  212. package/src/{debug.ts → debug/cli.ts} +114 -125
  213. package/src/debug/diagnostics.test.tsx +55 -0
  214. package/src/debug/effects.test.tsx +96 -0
  215. package/src/debug/effects.ts +313 -0
  216. package/src/debug/files.test.tsx +96 -0
  217. package/src/debug/files.ts +40 -0
  218. package/src/debug/index.ts +126 -0
  219. package/src/debug/render.test.tsx +379 -0
  220. package/src/debug/render.ts +639 -0
  221. package/src/debug/serialize.ts +85 -0
  222. package/src/debug/symbols.test.tsx +106 -0
  223. package/src/debug/symbols.ts +230 -0
  224. package/src/debug/trace.ts +312 -0
  225. package/src/devtools/devtools-protocol.ts +312 -0
  226. package/src/devtools/devtools-server.browser.ts +71 -0
  227. package/src/devtools/devtools-server.ts +290 -0
  228. package/src/devtools/devtools-transport.ts +154 -0
  229. package/src/devtools-entry.browser.ts +52 -0
  230. package/src/devtools-entry.ts +54 -0
  231. package/src/diagnostics.ts +141 -0
  232. package/src/index.ts +2 -6
  233. package/src/print-hook.ts +22 -0
  234. package/src/reactive-union-set.ts +71 -41
  235. package/src/reactivity.ts +206 -23
  236. package/src/render-stack.ts +68 -1
  237. package/src/render.ts +464 -157
  238. package/src/resource.ts +28 -19
  239. package/src/scheduler.ts +55 -3
  240. package/src/symbols/basic-symbol.ts +6 -1
  241. package/src/symbols/decl.ts +5 -1
  242. package/src/symbols/output-scope.ts +21 -12
  243. package/src/symbols/output-symbol.ts +33 -12
  244. package/src/symbols/symbol-flow.ts +68 -37
  245. package/src/symbols/symbol-slot.test.tsx +41 -0
  246. package/src/symbols/symbol-slot.tsx +47 -20
  247. package/src/symbols/symbol-table.ts +6 -10
  248. package/src/trace.ts +1 -0
  249. package/src/tracer.ts +13 -242
  250. package/src/utils.tsx +22 -13
  251. package/temp/api.json +1675 -162
  252. package/test/components/append-file.test.tsx +36 -29
  253. package/test/components/template-file.test.tsx +11 -11
  254. package/test/rendering/basic.test.tsx +4 -0
  255. package/test/rendering/print-render-stack.test.tsx +52 -43
  256. package/testing/create-test-wrapper.tsx +1 -1
  257. package/testing/devtools-utils.ts +203 -0
  258. package/testing/extend-expect.ts +89 -0
  259. package/testing/render.ts +2 -2
  260. package/testing/vitest.d.ts +9 -0
  261. package/dist/src/debug.d.ts +0 -14
  262. package/dist/src/debug.d.ts.map +0 -1
  263. package/dist/src/debug.js.map +0 -1
@@ -0,0 +1,379 @@
1
+ import { afterEach, beforeEach, expect, it } from "vitest";
2
+ import WebSocket from "ws";
3
+ import {
4
+ createMessageCollector,
5
+ filterRenderTreeMessages,
6
+ type DevtoolsMessage,
7
+ } from "../../testing/devtools-utils.js";
8
+ import { For } from "../components/For.jsx";
9
+ import { Output } from "../components/Output.jsx";
10
+ import { Show } from "../components/Show.jsx";
11
+ import {
12
+ enableDevtools,
13
+ resetDevtoolsServerForTests,
14
+ } from "../devtools/devtools-server.js";
15
+ import { ref } from "../reactivity.js";
16
+ import { renderAsync } from "../render.js";
17
+ import { flushJobsAsync } from "../scheduler.js";
18
+
19
+ let socket: WebSocket | undefined;
20
+
21
+ beforeEach(async () => {
22
+ const server = await enableDevtools({ port: 0 });
23
+ socket = new WebSocket(`ws://127.0.0.1:${server.port}`);
24
+
25
+ await new Promise<void>((resolve, reject) => {
26
+ socket?.once("open", resolve);
27
+ socket?.once("error", reject);
28
+ });
29
+ });
30
+
31
+ afterEach(async () => {
32
+ if (socket) {
33
+ socket.close();
34
+ socket = undefined;
35
+ }
36
+
37
+ await resetDevtoolsServerForTests();
38
+ });
39
+
40
+ it("emits render:complete on successful render", async () => {
41
+ const collector = createMessageCollector(socket!);
42
+
43
+ await renderAsync(<Output />);
44
+
45
+ const messages = await collector.waitForRender();
46
+
47
+ expect(messages.at(-1)).toMatchObject({ type: "render:complete" });
48
+ collector.stop();
49
+ });
50
+
51
+ it("emits render:error on render failure", async () => {
52
+ function Boom() {
53
+ throw new Error("Boom");
54
+ }
55
+
56
+ const collector = createMessageCollector(socket!);
57
+
58
+ await expect(
59
+ renderAsync(
60
+ <Output>
61
+ <Boom />
62
+ </Output>,
63
+ ),
64
+ ).rejects.toThrow("Boom");
65
+
66
+ const messages = await collector.waitForRender();
67
+ const renderMessages = messages.filter((m: DevtoolsMessage) =>
68
+ m.type.startsWith("render:"),
69
+ );
70
+ expect(renderMessages.at(-1)).toMatchObject({
71
+ type: "render:error",
72
+ name: expect.any(String),
73
+ message: expect.any(String),
74
+ componentStack: expect.any(Array),
75
+ });
76
+ collector.stop();
77
+ });
78
+
79
+ it("sends render tree messages during render", async () => {
80
+ function Foo() {
81
+ return (
82
+ <>
83
+ Hello
84
+ <br />
85
+ {() => "World"}
86
+ </>
87
+ );
88
+ }
89
+
90
+ const collector = createMessageCollector(socket!);
91
+
92
+ await renderAsync(
93
+ <Output>
94
+ <Foo />
95
+ </Output>,
96
+ );
97
+
98
+ const messages = await collector.waitForRender();
99
+ const renderMessages = filterRenderTreeMessages(messages);
100
+ collector.stop();
101
+
102
+ expect(renderMessages[0]).toMatchObject({ type: "render:reset" });
103
+ expect(renderMessages[1]).toMatchObject({
104
+ type: "render:nodeAdded",
105
+ parentId: null,
106
+ node: {},
107
+ });
108
+ expect(renderMessages[2]).toMatchObject({
109
+ type: "render:nodeAdded",
110
+ node: {
111
+ name: "Output",
112
+ },
113
+ });
114
+ expect(renderMessages[3]).toMatchObject({
115
+ type: "render:nodeAdded",
116
+ node: {
117
+ name: "Context Binder",
118
+ },
119
+ });
120
+ expect(renderMessages[4]).toMatchObject({
121
+ type: "render:nodeAdded",
122
+ node: {},
123
+ });
124
+ expect(renderMessages[5]).toMatchObject({
125
+ type: "render:nodeAdded",
126
+ node: {
127
+ name: "Context FormatOptions.*",
128
+ },
129
+ });
130
+ expect(renderMessages[6]).toMatchObject({
131
+ type: "render:nodeAdded",
132
+ node: {},
133
+ });
134
+ expect(renderMessages[7]).toMatchObject({
135
+ type: "render:nodeAdded",
136
+ node: {
137
+ name: "SourceDirectory",
138
+ },
139
+ });
140
+ expect(renderMessages[8]).toMatchObject({
141
+ type: "render:nodeAdded",
142
+ node: {
143
+ name: "Context SourceDirectory",
144
+ },
145
+ });
146
+ expect(renderMessages[9]).toMatchObject({
147
+ type: "render:nodeAdded",
148
+ node: {},
149
+ });
150
+ expect(renderMessages[10]).toMatchObject({
151
+ type: "render:nodeAdded",
152
+ node: {
153
+ name: "Foo",
154
+ },
155
+ });
156
+ expect(renderMessages[11]).toMatchObject({
157
+ type: "render:nodeAdded",
158
+ node: { value: "Hello" },
159
+ });
160
+ expect(renderMessages[12]).toMatchObject({
161
+ type: "render:nodeAdded",
162
+ node: { name: "br" },
163
+ });
164
+ expect(renderMessages[13]).toMatchObject({
165
+ type: "render:nodeAdded",
166
+ node: {},
167
+ });
168
+ expect(renderMessages[14]).toMatchObject({
169
+ type: "render:nodeAdded",
170
+ node: { value: "World" },
171
+ });
172
+ });
173
+
174
+ it("rerenders when devtools requests rerender", async () => {
175
+ let renderCount = 0;
176
+
177
+ function Display() {
178
+ renderCount += 1;
179
+ return "Hi";
180
+ }
181
+
182
+ const collector = createMessageCollector(socket!);
183
+
184
+ await renderAsync(
185
+ <Output>
186
+ <Display />
187
+ </Output>,
188
+ );
189
+
190
+ const messages = await collector.waitForRender();
191
+ const renderMessages = filterRenderTreeMessages(messages);
192
+ const displayNode = renderMessages.find(
193
+ (message: DevtoolsMessage) =>
194
+ message.type === "render:nodeAdded" &&
195
+ (message as { node?: { name?: string } }).node?.name === "Display",
196
+ ) as { node?: { id?: number } } | undefined;
197
+
198
+ expect(renderCount).toBe(1);
199
+ expect(displayNode?.node?.id).toEqual(expect.any(Number));
200
+
201
+ socket!.send(
202
+ JSON.stringify({ type: "render:rerender", id: displayNode!.node!.id }),
203
+ );
204
+
205
+ await collector.waitForFlush();
206
+
207
+ expect(renderCount).toBe(2);
208
+ collector.stop();
209
+ });
210
+
211
+ it("sends render tree messages during render with For component", async () => {
212
+ const collector = createMessageCollector(socket!);
213
+ function Display(props: any) {
214
+ return <>item {props.item}</>;
215
+ }
216
+ await renderAsync(
217
+ <Output>
218
+ <For each={["a", "b"]}>{(item) => <Display item={item} />}</For>
219
+ </Output>,
220
+ );
221
+
222
+ const messages = await collector.waitForRender();
223
+ const renderMessages = filterRenderTreeMessages(messages);
224
+ collector.stop();
225
+
226
+ expect(renderMessages[0]).toMatchObject({ type: "render:reset" });
227
+ expect(renderMessages).toEqual(
228
+ expect.arrayContaining([
229
+ expect.objectContaining({
230
+ type: "render:nodeAdded",
231
+ node: expect.objectContaining({ name: "For" }),
232
+ }),
233
+ expect.objectContaining({
234
+ type: "render:nodeAdded",
235
+ node: expect.objectContaining({ value: "a" }),
236
+ }),
237
+ expect.objectContaining({
238
+ type: "render:nodeAdded",
239
+ node: expect.objectContaining({ value: "b" }),
240
+ }),
241
+ ]),
242
+ );
243
+ });
244
+
245
+ it("emits nodeUpdated when component props change", async () => {
246
+ const count = ref(1);
247
+ const collector = createMessageCollector(socket!);
248
+
249
+ function Counter(props: { value: number }) {
250
+ return `Count: ${props.value}`;
251
+ }
252
+
253
+ await renderAsync(
254
+ <Output>
255
+ <Counter value={count.value} />
256
+ </Output>,
257
+ );
258
+
259
+ await collector.waitForRender();
260
+
261
+ count.value += 1;
262
+ await flushJobsAsync();
263
+
264
+ const updateMessages = await collector.waitForFlush();
265
+ const updateRenderMessages = filterRenderTreeMessages(updateMessages);
266
+
267
+ const nodeUpdated = updateRenderMessages.filter(
268
+ (m: DevtoolsMessage) => m.type === "render:nodeUpdated",
269
+ );
270
+
271
+ expect(nodeUpdated[0]).toMatchObject({
272
+ type: "render:nodeUpdated",
273
+ id: expect.any(Number),
274
+ });
275
+ collector.stop();
276
+ });
277
+
278
+ it("emits nodeRemoved when conditional content disappears", async () => {
279
+ const show = ref(true);
280
+ const collector = createMessageCollector(socket!);
281
+
282
+ function Maybe() {
283
+ return <Show when={show.value}>hi</Show>;
284
+ }
285
+
286
+ await renderAsync(
287
+ <Output>
288
+ <Maybe />
289
+ </Output>,
290
+ );
291
+
292
+ await collector.waitForRender();
293
+
294
+ show.value = false;
295
+ await flushJobsAsync();
296
+
297
+ const updateMessages = await collector.waitForFlush();
298
+ const updateRenderMessages = filterRenderTreeMessages(updateMessages);
299
+
300
+ const removed = updateRenderMessages.filter(
301
+ (m: DevtoolsMessage) => m.type === "render:nodeRemoved",
302
+ );
303
+
304
+ expect(removed[0]).toMatchObject({
305
+ type: "render:nodeRemoved",
306
+ parentId: expect.any(Number),
307
+ id: expect.any(Number),
308
+ });
309
+ collector.stop();
310
+ });
311
+
312
+ it("emits proper events when items are added/removed in For component", async () => {
313
+ const items = ref(["a", "b"]);
314
+ const collector = createMessageCollector(socket!);
315
+
316
+ function Display(props: any) {
317
+ return <>item {props.item}</>;
318
+ }
319
+ await renderAsync(
320
+ <Output>
321
+ <For each={items}>{(item) => <Display item={item} />}</For>
322
+ </Output>,
323
+ );
324
+
325
+ const originalMessages = await collector.waitForRender();
326
+
327
+ // Track all nodes that are currently in the tree
328
+ const activeNodes = new Map<
329
+ number,
330
+ { parentId: number | null; kind: string; name?: string }
331
+ >();
332
+
333
+ function processMessages(messages: any[]) {
334
+ for (const msg of messages) {
335
+ if (msg.type === "render:nodeAdded") {
336
+ const nodeId = msg.node.id;
337
+ const parentId = msg.parentId;
338
+
339
+ // Root node has null parent, otherwise parent must exist
340
+ if (parentId !== null && !activeNodes.has(parentId)) {
341
+ throw new Error(
342
+ `Node ${nodeId} (${msg.node.kind}${msg.node.name ? `: ${msg.node.name}` : ""}) ` +
343
+ `added with parent ${parentId} but parent is not in active nodes. ` +
344
+ `Active nodes: ${[...activeNodes.keys()].join(", ")}`,
345
+ );
346
+ }
347
+
348
+ activeNodes.set(nodeId, {
349
+ parentId,
350
+ kind: msg.node.kind,
351
+ name: msg.node.name,
352
+ });
353
+ } else if (msg.type === "render:nodeRemoved") {
354
+ const nodeId = msg.id;
355
+ if (!activeNodes.has(nodeId)) {
356
+ throw new Error(
357
+ `Node ${nodeId} removed but was not in active nodes. ` +
358
+ `Active nodes: ${[...activeNodes.keys()].join(", ")}`,
359
+ );
360
+ }
361
+ activeNodes.delete(nodeId);
362
+ }
363
+ }
364
+ }
365
+
366
+ // Process initial render
367
+ processMessages(filterRenderTreeMessages(originalMessages));
368
+
369
+ // Mutate the list
370
+ items.value.push("c");
371
+ items.value.unshift("0");
372
+ await flushJobsAsync();
373
+
374
+ const updateMessages = await collector.waitForFlush();
375
+ const updateRenderMessages = filterRenderTreeMessages(updateMessages);
376
+
377
+ // Process update - this will throw if parent invariant is violated
378
+ processMessages(updateRenderMessages);
379
+ });