@alloy-js/core 0.23.0-dev.0 → 0.23.0-dev.10

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 (316) 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/AccessExpression.d.ts +78 -0
  8. package/dist/src/components/AccessExpression.d.ts.map +1 -0
  9. package/dist/src/components/AccessExpression.js +218 -0
  10. package/dist/src/components/AccessExpression.js.map +1 -0
  11. package/dist/src/components/AccessExpression.test.d.ts +2 -0
  12. package/dist/src/components/AccessExpression.test.d.ts.map +1 -0
  13. package/dist/src/components/AccessExpression.test.js +137 -0
  14. package/dist/src/components/AccessExpression.test.js.map +1 -0
  15. package/dist/src/components/AppendFile.d.ts.map +1 -1
  16. package/dist/src/components/AppendFile.js +14 -3
  17. package/dist/src/components/AppendFile.js.map +1 -1
  18. package/dist/src/components/Block.js +1 -1
  19. package/dist/src/components/Block.js.map +1 -1
  20. package/dist/src/components/Declaration.d.ts.map +1 -1
  21. package/dist/src/components/Declaration.js +2 -1
  22. package/dist/src/components/Declaration.js.map +1 -1
  23. package/dist/src/components/Prose.js +2 -2
  24. package/dist/src/components/Prose.js.map +1 -1
  25. package/dist/src/components/Scope.d.ts.map +1 -1
  26. package/dist/src/components/Scope.js +6 -1
  27. package/dist/src/components/Scope.js.map +1 -1
  28. package/dist/src/components/SourceDirectory.d.ts.map +1 -1
  29. package/dist/src/components/SourceDirectory.js +1 -2
  30. package/dist/src/components/SourceDirectory.js.map +1 -1
  31. package/dist/src/components/TemplateFile.d.ts.map +1 -1
  32. package/dist/src/components/TemplateFile.js +18 -3
  33. package/dist/src/components/TemplateFile.js.map +1 -1
  34. package/dist/src/components/index.d.ts +1 -0
  35. package/dist/src/components/index.d.ts.map +1 -1
  36. package/dist/src/components/index.js +1 -0
  37. package/dist/src/components/index.js.map +1 -1
  38. package/dist/src/content-slot.d.ts.map +1 -1
  39. package/dist/src/content-slot.js +7 -6
  40. package/dist/src/content-slot.js.map +1 -1
  41. package/dist/src/context.d.ts.map +1 -1
  42. package/dist/src/context.js +10 -3
  43. package/dist/src/context.js.map +1 -1
  44. package/dist/src/debug/cli.d.ts +6 -0
  45. package/dist/src/debug/cli.d.ts.map +1 -0
  46. package/dist/src/{debug.js → debug/cli.js} +78 -84
  47. package/dist/src/debug/cli.js.map +1 -0
  48. package/dist/src/debug/diagnostics.test.d.ts +2 -0
  49. package/dist/src/debug/diagnostics.test.d.ts.map +1 -0
  50. package/dist/src/debug/diagnostics.test.js +45 -0
  51. package/dist/src/debug/diagnostics.test.js.map +1 -0
  52. package/dist/src/debug/effects.d.ts +73 -0
  53. package/dist/src/debug/effects.d.ts.map +1 -0
  54. package/dist/src/debug/effects.js +228 -0
  55. package/dist/src/debug/effects.js.map +1 -0
  56. package/dist/src/debug/effects.test.d.ts +2 -0
  57. package/dist/src/debug/effects.test.d.ts.map +1 -0
  58. package/dist/src/debug/effects.test.js +84 -0
  59. package/dist/src/debug/effects.test.js.map +1 -0
  60. package/dist/src/debug/files.d.ts +14 -0
  61. package/dist/src/debug/files.d.ts.map +1 -0
  62. package/dist/src/debug/files.js +40 -0
  63. package/dist/src/debug/files.js.map +1 -0
  64. package/dist/src/debug/files.test.d.ts +2 -0
  65. package/dist/src/debug/files.test.d.ts.map +1 -0
  66. package/dist/src/debug/files.test.js +89 -0
  67. package/dist/src/debug/files.test.js.map +1 -0
  68. package/dist/src/debug/index.d.ts +61 -0
  69. package/dist/src/debug/index.d.ts.map +1 -0
  70. package/dist/src/debug/index.js +69 -0
  71. package/dist/src/debug/index.js.map +1 -0
  72. package/dist/src/debug/render.d.ts +57 -0
  73. package/dist/src/debug/render.d.ts.map +1 -0
  74. package/dist/src/debug/render.js +519 -0
  75. package/dist/src/debug/render.js.map +1 -0
  76. package/dist/src/debug/render.test.d.ts +2 -0
  77. package/dist/src/debug/render.test.d.ts.map +1 -0
  78. package/dist/src/debug/render.test.js +328 -0
  79. package/dist/src/debug/render.test.js.map +1 -0
  80. package/dist/src/debug/serialize.d.ts +9 -0
  81. package/dist/src/debug/serialize.d.ts.map +1 -0
  82. package/dist/src/debug/serialize.js +70 -0
  83. package/dist/src/debug/serialize.js.map +1 -0
  84. package/dist/src/debug/symbols.d.ts +15 -0
  85. package/dist/src/debug/symbols.d.ts.map +1 -0
  86. package/dist/src/debug/symbols.js +173 -0
  87. package/dist/src/debug/symbols.js.map +1 -0
  88. package/dist/src/debug/symbols.test.d.ts +2 -0
  89. package/dist/src/debug/symbols.test.d.ts.map +1 -0
  90. package/dist/src/debug/symbols.test.js +104 -0
  91. package/dist/src/debug/symbols.test.js.map +1 -0
  92. package/dist/src/debug/trace.d.ts +342 -0
  93. package/dist/src/debug/trace.d.ts.map +1 -0
  94. package/dist/src/debug/trace.js +443 -0
  95. package/dist/src/debug/trace.js.map +1 -0
  96. package/dist/src/devtools/devtools-protocol.d.ts +232 -0
  97. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -0
  98. package/dist/src/devtools/devtools-protocol.js +2 -0
  99. package/dist/src/devtools/devtools-protocol.js.map +1 -0
  100. package/dist/src/devtools/devtools-server.browser.d.ts +28 -0
  101. package/dist/src/devtools/devtools-server.browser.d.ts.map +1 -0
  102. package/dist/src/devtools/devtools-server.browser.js +36 -0
  103. package/dist/src/devtools/devtools-server.browser.js.map +1 -0
  104. package/dist/src/devtools/devtools-server.d.ts +72 -0
  105. package/dist/src/devtools/devtools-server.d.ts.map +1 -0
  106. package/dist/src/devtools/devtools-server.js +256 -0
  107. package/dist/src/devtools/devtools-server.js.map +1 -0
  108. package/dist/src/devtools/devtools-transport.d.ts +23 -0
  109. package/dist/src/devtools/devtools-transport.d.ts.map +1 -0
  110. package/dist/src/devtools/devtools-transport.js +114 -0
  111. package/dist/src/devtools/devtools-transport.js.map +1 -0
  112. package/dist/src/devtools-entry.browser.d.ts +4 -0
  113. package/dist/src/devtools-entry.browser.d.ts.map +1 -0
  114. package/dist/src/devtools-entry.browser.js +2 -0
  115. package/dist/src/devtools-entry.browser.js.map +1 -0
  116. package/dist/src/devtools-entry.d.ts +4 -0
  117. package/dist/src/devtools-entry.d.ts.map +1 -0
  118. package/dist/src/devtools-entry.js +2 -0
  119. package/dist/src/devtools-entry.js.map +1 -0
  120. package/dist/src/diagnostics.d.ts +34 -0
  121. package/dist/src/diagnostics.d.ts.map +1 -0
  122. package/dist/src/diagnostics.js +89 -0
  123. package/dist/src/diagnostics.js.map +1 -0
  124. package/dist/src/index.d.ts +3 -2
  125. package/dist/src/index.d.ts.map +1 -1
  126. package/dist/src/index.js +3 -2
  127. package/dist/src/index.js.map +1 -1
  128. package/dist/src/print-hook.d.ts +14 -0
  129. package/dist/src/print-hook.d.ts.map +1 -0
  130. package/dist/src/print-hook.js +10 -0
  131. package/dist/src/print-hook.js.map +1 -0
  132. package/dist/src/reactive-union-set.d.ts.map +1 -1
  133. package/dist/src/reactive-union-set.js +28 -3
  134. package/dist/src/reactive-union-set.js.map +1 -1
  135. package/dist/src/reactivity.d.ts +50 -8
  136. package/dist/src/reactivity.d.ts.map +1 -1
  137. package/dist/src/reactivity.js +225 -39
  138. package/dist/src/reactivity.js.map +1 -1
  139. package/dist/src/render-stack.d.ts +30 -0
  140. package/dist/src/render-stack.d.ts.map +1 -0
  141. package/dist/src/render-stack.js +251 -0
  142. package/dist/src/render-stack.js.map +1 -0
  143. package/dist/src/render.d.ts +9 -19
  144. package/dist/src/render.d.ts.map +1 -1
  145. package/dist/src/render.js +371 -159
  146. package/dist/src/render.js.map +1 -1
  147. package/dist/src/resource.d.ts.map +1 -1
  148. package/dist/src/resource.js +5 -0
  149. package/dist/src/resource.js.map +1 -1
  150. package/dist/src/runtime/component.d.ts +7 -1
  151. package/dist/src/runtime/component.d.ts.map +1 -1
  152. package/dist/src/runtime/component.js +4 -1
  153. package/dist/src/runtime/component.js.map +1 -1
  154. package/dist/src/scheduler.d.ts +8 -0
  155. package/dist/src/scheduler.d.ts.map +1 -1
  156. package/dist/src/scheduler.js +69 -3
  157. package/dist/src/scheduler.js.map +1 -1
  158. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  159. package/dist/src/symbols/basic-symbol.js +6 -1
  160. package/dist/src/symbols/basic-symbol.js.map +1 -1
  161. package/dist/src/symbols/decl.d.ts.map +1 -1
  162. package/dist/src/symbols/decl.js +5 -1
  163. package/dist/src/symbols/decl.js.map +1 -1
  164. package/dist/src/symbols/output-scope.d.ts +2 -1
  165. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  166. package/dist/src/symbols/output-scope.js +13 -8
  167. package/dist/src/symbols/output-scope.js.map +1 -1
  168. package/dist/src/symbols/output-symbol.d.ts +1 -0
  169. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  170. package/dist/src/symbols/output-symbol.js +25 -8
  171. package/dist/src/symbols/output-symbol.js.map +1 -1
  172. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  173. package/dist/src/symbols/symbol-flow.js +24 -8
  174. package/dist/src/symbols/symbol-flow.js.map +1 -1
  175. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  176. package/dist/src/symbols/symbol-slot.js +15 -0
  177. package/dist/src/symbols/symbol-slot.js.map +1 -1
  178. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  179. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  180. package/dist/src/symbols/symbol-slot.test.js +35 -0
  181. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  182. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  183. package/dist/src/symbols/symbol-table.js +6 -5
  184. package/dist/src/symbols/symbol-table.js.map +1 -1
  185. package/dist/src/trace.d.ts +2 -0
  186. package/dist/src/trace.d.ts.map +1 -0
  187. package/dist/src/trace.js +2 -0
  188. package/dist/src/trace.js.map +1 -0
  189. package/dist/src/tracer.d.ts +2 -228
  190. package/dist/src/tracer.d.ts.map +1 -1
  191. package/dist/src/tracer.js +5 -298
  192. package/dist/src/tracer.js.map +1 -1
  193. package/dist/src/utils.d.ts.map +1 -1
  194. package/dist/src/utils.js +7 -5
  195. package/dist/src/utils.js.map +1 -1
  196. package/dist/test/components/append-file.test.d.ts.map +1 -1
  197. package/dist/test/components/append-file.test.js +18 -10
  198. package/dist/test/components/append-file.test.js.map +1 -1
  199. package/dist/test/components/template-file.test.d.ts.map +1 -1
  200. package/dist/test/components/template-file.test.js +6 -4
  201. package/dist/test/components/template-file.test.js.map +1 -1
  202. package/dist/test/lazy-isempty.test.d.ts +2 -0
  203. package/dist/test/lazy-isempty.test.d.ts.map +1 -0
  204. package/dist/test/lazy-isempty.test.js +89 -0
  205. package/dist/test/lazy-isempty.test.js.map +1 -0
  206. package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
  207. package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
  208. package/dist/test/reactive-union-set-disposers.test.js +98 -0
  209. package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
  210. package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
  211. package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
  212. package/dist/test/reactivity/shallow-reactive.test.js +52 -0
  213. package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
  214. package/dist/test/rendering/basic.test.js +3 -0
  215. package/dist/test/rendering/basic.test.js.map +1 -1
  216. package/dist/test/rendering/print-render-stack.test.d.ts +2 -0
  217. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -0
  218. package/dist/test/rendering/print-render-stack.test.js +207 -0
  219. package/dist/test/rendering/print-render-stack.test.js.map +1 -0
  220. package/dist/test/scheduler-extended.test.d.ts +2 -0
  221. package/dist/test/scheduler-extended.test.d.ts.map +1 -0
  222. package/dist/test/scheduler-extended.test.js +96 -0
  223. package/dist/test/scheduler-extended.test.js.map +1 -0
  224. package/dist/test/scheduler.test.d.ts +2 -0
  225. package/dist/test/scheduler.test.d.ts.map +1 -0
  226. package/dist/test/scheduler.test.js +46 -0
  227. package/dist/test/scheduler.test.js.map +1 -0
  228. package/dist/testing/create-test-wrapper.d.ts +1 -1
  229. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  230. package/dist/testing/create-test-wrapper.js +1 -1
  231. package/dist/testing/create-test-wrapper.js.map +1 -1
  232. package/dist/testing/devtools-utils.d.ts +26 -0
  233. package/dist/testing/devtools-utils.d.ts.map +1 -0
  234. package/dist/testing/devtools-utils.js +140 -0
  235. package/dist/testing/devtools-utils.js.map +1 -0
  236. package/dist/testing/extend-expect.d.ts.map +1 -1
  237. package/dist/testing/extend-expect.js +63 -1
  238. package/dist/testing/extend-expect.js.map +1 -1
  239. package/dist/testing/render.d.ts +2 -2
  240. package/dist/testing/render.d.ts.map +1 -1
  241. package/dist/testing/render.js +2 -2
  242. package/dist/testing/render.js.map +1 -1
  243. package/dist/tsconfig.tsbuildinfo +1 -1
  244. package/package.json +21 -7
  245. package/scripts/copy-devtools-ui.mjs +26 -0
  246. package/src/binder.ts +71 -16
  247. package/src/components/AccessExpression.test.tsx +132 -0
  248. package/src/components/AccessExpression.tsx +344 -0
  249. package/src/components/AppendFile.tsx +14 -9
  250. package/src/components/Block.tsx +1 -1
  251. package/src/components/Declaration.tsx +2 -1
  252. package/src/components/Prose.tsx +1 -1
  253. package/src/components/Scope.tsx +6 -1
  254. package/src/components/SourceDirectory.tsx +1 -2
  255. package/src/components/TemplateFile.tsx +18 -9
  256. package/src/components/index.tsx +1 -0
  257. package/src/content-slot.tsx +7 -7
  258. package/src/context.ts +17 -6
  259. package/src/{debug.ts → debug/cli.ts} +112 -127
  260. package/src/debug/diagnostics.test.tsx +55 -0
  261. package/src/debug/effects.test.tsx +89 -0
  262. package/src/debug/effects.ts +317 -0
  263. package/src/debug/files.test.tsx +96 -0
  264. package/src/debug/files.ts +40 -0
  265. package/src/debug/index.ts +128 -0
  266. package/src/debug/render.test.tsx +379 -0
  267. package/src/debug/render.ts +639 -0
  268. package/src/debug/serialize.ts +85 -0
  269. package/src/debug/symbols.test.tsx +106 -0
  270. package/src/debug/symbols.ts +239 -0
  271. package/src/debug/trace.ts +312 -0
  272. package/src/devtools/devtools-protocol.ts +312 -0
  273. package/src/devtools/devtools-server.browser.ts +71 -0
  274. package/src/devtools/devtools-server.ts +290 -0
  275. package/src/devtools/devtools-transport.ts +154 -0
  276. package/src/devtools-entry.browser.ts +52 -0
  277. package/src/devtools-entry.ts +54 -0
  278. package/src/diagnostics.ts +141 -0
  279. package/src/index.ts +2 -7
  280. package/src/print-hook.ts +22 -0
  281. package/src/reactive-union-set.ts +85 -44
  282. package/src/reactivity.ts +301 -59
  283. package/src/render-stack.ts +294 -0
  284. package/src/render.ts +470 -216
  285. package/src/resource.ts +28 -19
  286. package/src/runtime/component.ts +11 -0
  287. package/src/scheduler.ts +80 -4
  288. package/src/symbols/basic-symbol.ts +6 -1
  289. package/src/symbols/decl.ts +5 -1
  290. package/src/symbols/output-scope.ts +21 -13
  291. package/src/symbols/output-symbol.ts +34 -14
  292. package/src/symbols/symbol-flow.ts +76 -39
  293. package/src/symbols/symbol-slot.test.tsx +41 -0
  294. package/src/symbols/symbol-slot.tsx +47 -20
  295. package/src/symbols/symbol-table.ts +6 -10
  296. package/src/trace.ts +1 -0
  297. package/src/tracer.ts +13 -242
  298. package/src/utils.tsx +24 -17
  299. package/temp/api.json +4187 -1603
  300. package/test/components/append-file.test.tsx +36 -29
  301. package/test/components/template-file.test.tsx +11 -11
  302. package/test/lazy-isempty.test.tsx +106 -0
  303. package/test/reactive-union-set-disposers.test.tsx +112 -0
  304. package/test/reactivity/shallow-reactive.test.tsx +56 -0
  305. package/test/rendering/basic.test.tsx +4 -0
  306. package/test/rendering/print-render-stack.test.tsx +244 -0
  307. package/test/scheduler-extended.test.tsx +122 -0
  308. package/test/scheduler.test.tsx +50 -0
  309. package/testing/create-test-wrapper.tsx +1 -1
  310. package/testing/devtools-utils.ts +203 -0
  311. package/testing/extend-expect.ts +89 -0
  312. package/testing/render.ts +2 -2
  313. package/testing/vitest.d.ts +9 -0
  314. package/dist/src/debug.d.ts +0 -15
  315. package/dist/src/debug.d.ts.map +0 -1
  316. 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
+ });