@alloy-js/core 0.23.0-dev.1 → 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 (310) 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} +79 -82
  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 +18 -1
  140. package/dist/src/render-stack.d.ts.map +1 -1
  141. package/dist/src/render-stack.js +61 -1
  142. package/dist/src/render-stack.js.map +1 -1
  143. package/dist/src/render.d.ts +8 -15
  144. package/dist/src/render.d.ts.map +1 -1
  145. package/dist/src/render.js +370 -109
  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/scheduler.d.ts +8 -0
  151. package/dist/src/scheduler.d.ts.map +1 -1
  152. package/dist/src/scheduler.js +69 -3
  153. package/dist/src/scheduler.js.map +1 -1
  154. package/dist/src/symbols/basic-symbol.d.ts.map +1 -1
  155. package/dist/src/symbols/basic-symbol.js +6 -1
  156. package/dist/src/symbols/basic-symbol.js.map +1 -1
  157. package/dist/src/symbols/decl.d.ts.map +1 -1
  158. package/dist/src/symbols/decl.js +5 -1
  159. package/dist/src/symbols/decl.js.map +1 -1
  160. package/dist/src/symbols/output-scope.d.ts +2 -1
  161. package/dist/src/symbols/output-scope.d.ts.map +1 -1
  162. package/dist/src/symbols/output-scope.js +13 -8
  163. package/dist/src/symbols/output-scope.js.map +1 -1
  164. package/dist/src/symbols/output-symbol.d.ts +1 -0
  165. package/dist/src/symbols/output-symbol.d.ts.map +1 -1
  166. package/dist/src/symbols/output-symbol.js +25 -8
  167. package/dist/src/symbols/output-symbol.js.map +1 -1
  168. package/dist/src/symbols/symbol-flow.d.ts.map +1 -1
  169. package/dist/src/symbols/symbol-flow.js +24 -8
  170. package/dist/src/symbols/symbol-flow.js.map +1 -1
  171. package/dist/src/symbols/symbol-slot.d.ts.map +1 -1
  172. package/dist/src/symbols/symbol-slot.js +15 -0
  173. package/dist/src/symbols/symbol-slot.js.map +1 -1
  174. package/dist/src/symbols/symbol-slot.test.d.ts +2 -0
  175. package/dist/src/symbols/symbol-slot.test.d.ts.map +1 -0
  176. package/dist/src/symbols/symbol-slot.test.js +35 -0
  177. package/dist/src/symbols/symbol-slot.test.js.map +1 -0
  178. package/dist/src/symbols/symbol-table.d.ts.map +1 -1
  179. package/dist/src/symbols/symbol-table.js +6 -5
  180. package/dist/src/symbols/symbol-table.js.map +1 -1
  181. package/dist/src/trace.d.ts +2 -0
  182. package/dist/src/trace.d.ts.map +1 -0
  183. package/dist/src/trace.js +2 -0
  184. package/dist/src/trace.js.map +1 -0
  185. package/dist/src/tracer.d.ts +2 -228
  186. package/dist/src/tracer.d.ts.map +1 -1
  187. package/dist/src/tracer.js +5 -298
  188. package/dist/src/tracer.js.map +1 -1
  189. package/dist/src/utils.d.ts.map +1 -1
  190. package/dist/src/utils.js +7 -5
  191. package/dist/src/utils.js.map +1 -1
  192. package/dist/test/components/append-file.test.d.ts.map +1 -1
  193. package/dist/test/components/append-file.test.js +18 -10
  194. package/dist/test/components/append-file.test.js.map +1 -1
  195. package/dist/test/components/template-file.test.d.ts.map +1 -1
  196. package/dist/test/components/template-file.test.js +6 -4
  197. package/dist/test/components/template-file.test.js.map +1 -1
  198. package/dist/test/lazy-isempty.test.d.ts +2 -0
  199. package/dist/test/lazy-isempty.test.d.ts.map +1 -0
  200. package/dist/test/lazy-isempty.test.js +89 -0
  201. package/dist/test/lazy-isempty.test.js.map +1 -0
  202. package/dist/test/reactive-union-set-disposers.test.d.ts +2 -0
  203. package/dist/test/reactive-union-set-disposers.test.d.ts.map +1 -0
  204. package/dist/test/reactive-union-set-disposers.test.js +98 -0
  205. package/dist/test/reactive-union-set-disposers.test.js.map +1 -0
  206. package/dist/test/reactivity/shallow-reactive.test.d.ts +2 -0
  207. package/dist/test/reactivity/shallow-reactive.test.d.ts.map +1 -0
  208. package/dist/test/reactivity/shallow-reactive.test.js +52 -0
  209. package/dist/test/reactivity/shallow-reactive.test.js.map +1 -0
  210. package/dist/test/rendering/basic.test.js +3 -0
  211. package/dist/test/rendering/basic.test.js.map +1 -1
  212. package/dist/test/rendering/print-render-stack.test.d.ts.map +1 -1
  213. package/dist/test/rendering/print-render-stack.test.js +91 -98
  214. package/dist/test/rendering/print-render-stack.test.js.map +1 -1
  215. package/dist/test/scheduler-extended.test.d.ts +2 -0
  216. package/dist/test/scheduler-extended.test.d.ts.map +1 -0
  217. package/dist/test/scheduler-extended.test.js +96 -0
  218. package/dist/test/scheduler-extended.test.js.map +1 -0
  219. package/dist/test/scheduler.test.d.ts +2 -0
  220. package/dist/test/scheduler.test.d.ts.map +1 -0
  221. package/dist/test/scheduler.test.js +46 -0
  222. package/dist/test/scheduler.test.js.map +1 -0
  223. package/dist/testing/create-test-wrapper.d.ts +1 -1
  224. package/dist/testing/create-test-wrapper.d.ts.map +1 -1
  225. package/dist/testing/create-test-wrapper.js +1 -1
  226. package/dist/testing/create-test-wrapper.js.map +1 -1
  227. package/dist/testing/devtools-utils.d.ts +26 -0
  228. package/dist/testing/devtools-utils.d.ts.map +1 -0
  229. package/dist/testing/devtools-utils.js +140 -0
  230. package/dist/testing/devtools-utils.js.map +1 -0
  231. package/dist/testing/extend-expect.d.ts.map +1 -1
  232. package/dist/testing/extend-expect.js +63 -1
  233. package/dist/testing/extend-expect.js.map +1 -1
  234. package/dist/testing/render.d.ts +2 -2
  235. package/dist/testing/render.d.ts.map +1 -1
  236. package/dist/testing/render.js +2 -2
  237. package/dist/testing/render.js.map +1 -1
  238. package/dist/tsconfig.tsbuildinfo +1 -1
  239. package/package.json +21 -7
  240. package/scripts/copy-devtools-ui.mjs +26 -0
  241. package/src/binder.ts +71 -16
  242. package/src/components/AccessExpression.test.tsx +132 -0
  243. package/src/components/AccessExpression.tsx +344 -0
  244. package/src/components/AppendFile.tsx +14 -9
  245. package/src/components/Block.tsx +1 -1
  246. package/src/components/Declaration.tsx +2 -1
  247. package/src/components/Prose.tsx +1 -1
  248. package/src/components/Scope.tsx +6 -1
  249. package/src/components/SourceDirectory.tsx +1 -2
  250. package/src/components/TemplateFile.tsx +18 -9
  251. package/src/components/index.tsx +1 -0
  252. package/src/content-slot.tsx +7 -7
  253. package/src/context.ts +17 -6
  254. package/src/{debug.ts → debug/cli.ts} +114 -125
  255. package/src/debug/diagnostics.test.tsx +55 -0
  256. package/src/debug/effects.test.tsx +89 -0
  257. package/src/debug/effects.ts +317 -0
  258. package/src/debug/files.test.tsx +96 -0
  259. package/src/debug/files.ts +40 -0
  260. package/src/debug/index.ts +128 -0
  261. package/src/debug/render.test.tsx +379 -0
  262. package/src/debug/render.ts +639 -0
  263. package/src/debug/serialize.ts +85 -0
  264. package/src/debug/symbols.test.tsx +106 -0
  265. package/src/debug/symbols.ts +239 -0
  266. package/src/debug/trace.ts +312 -0
  267. package/src/devtools/devtools-protocol.ts +312 -0
  268. package/src/devtools/devtools-server.browser.ts +71 -0
  269. package/src/devtools/devtools-server.ts +290 -0
  270. package/src/devtools/devtools-transport.ts +154 -0
  271. package/src/devtools-entry.browser.ts +52 -0
  272. package/src/devtools-entry.ts +54 -0
  273. package/src/diagnostics.ts +141 -0
  274. package/src/index.ts +2 -7
  275. package/src/print-hook.ts +22 -0
  276. package/src/reactive-union-set.ts +85 -44
  277. package/src/reactivity.ts +301 -59
  278. package/src/render-stack.ts +73 -1
  279. package/src/render.ts +470 -161
  280. package/src/resource.ts +28 -19
  281. package/src/scheduler.ts +80 -4
  282. package/src/symbols/basic-symbol.ts +6 -1
  283. package/src/symbols/decl.ts +5 -1
  284. package/src/symbols/output-scope.ts +21 -13
  285. package/src/symbols/output-symbol.ts +34 -14
  286. package/src/symbols/symbol-flow.ts +76 -39
  287. package/src/symbols/symbol-slot.test.tsx +41 -0
  288. package/src/symbols/symbol-slot.tsx +47 -20
  289. package/src/symbols/symbol-table.ts +6 -10
  290. package/src/trace.ts +1 -0
  291. package/src/tracer.ts +13 -242
  292. package/src/utils.tsx +24 -17
  293. package/temp/api.json +5658 -3095
  294. package/test/components/append-file.test.tsx +36 -29
  295. package/test/components/template-file.test.tsx +11 -11
  296. package/test/lazy-isempty.test.tsx +106 -0
  297. package/test/reactive-union-set-disposers.test.tsx +112 -0
  298. package/test/reactivity/shallow-reactive.test.tsx +56 -0
  299. package/test/rendering/basic.test.tsx +4 -0
  300. package/test/rendering/print-render-stack.test.tsx +52 -43
  301. package/test/scheduler-extended.test.tsx +122 -0
  302. package/test/scheduler.test.tsx +50 -0
  303. package/testing/create-test-wrapper.tsx +1 -1
  304. package/testing/devtools-utils.ts +203 -0
  305. package/testing/extend-expect.ts +89 -0
  306. package/testing/render.ts +2 -2
  307. package/testing/vitest.d.ts +9 -0
  308. package/dist/src/debug.d.ts +0 -14
  309. package/dist/src/debug.d.ts.map +0 -1
  310. package/dist/src/debug.js.map +0 -1
@@ -0,0 +1,96 @@
1
+ import { afterEach, beforeEach, expect, it } from "vitest";
2
+ import WebSocket from "ws";
3
+ import {
4
+ createMessageCollector,
5
+ type DevtoolsMessage,
6
+ } from "../../testing/devtools-utils.js";
7
+ import { Output } from "../components/Output.jsx";
8
+ import { Show } from "../components/Show.jsx";
9
+ import { SourceDirectory } from "../components/SourceDirectory.jsx";
10
+ import { SourceFile } from "../components/SourceFile.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 file and directory add/update/remove messages", async () => {
41
+ const show = ref(true);
42
+ const collector = createMessageCollector(socket!);
43
+
44
+ await renderAsync(
45
+ <Output>
46
+ <Show when={show.value}>
47
+ <SourceDirectory path="src">
48
+ <SourceFile path="index.ts" filetype="ts">
49
+ {"export const value = 1;"}
50
+ </SourceFile>
51
+ </SourceDirectory>
52
+ </Show>
53
+ </Output>,
54
+ );
55
+
56
+ const initialMessages = await collector.waitForRender();
57
+ const initialFiles = initialMessages.filter((m: DevtoolsMessage) =>
58
+ m.type.startsWith("files:"),
59
+ );
60
+ expect(initialFiles[0]).toMatchObject({
61
+ path: "./",
62
+ });
63
+ expect(initialFiles[1]).toMatchObject({
64
+ type: "files:directoryAdded",
65
+ path: "src",
66
+ });
67
+ expect(initialFiles[2]).toMatchObject({
68
+ type: "files:fileAdded",
69
+ path: "src/index.ts",
70
+ filetype: "ts",
71
+ });
72
+ expect(initialFiles[3]).toMatchObject({
73
+ type: "files:fileUpdated",
74
+ path: "src/index.ts",
75
+ filetype: "ts",
76
+ contents: expect.any(String),
77
+ });
78
+
79
+ show.value = false;
80
+ await flushJobsAsync();
81
+
82
+ const updateMessages = await collector.waitForFlush();
83
+ const updateFiles = updateMessages.filter((m: DevtoolsMessage) =>
84
+ m.type.startsWith("files:"),
85
+ );
86
+ collector.stop();
87
+
88
+ expect(updateFiles[0]).toMatchObject({
89
+ type: "files:fileRemoved",
90
+ path: "src/index.ts",
91
+ });
92
+ expect(updateFiles[1]).toMatchObject({
93
+ type: "files:directoryRemoved",
94
+ path: "src",
95
+ });
96
+ });
@@ -0,0 +1,40 @@
1
+ import { emitDevtoolsMessage, isDevtoolsEnabled } from "./trace.js";
2
+
3
+ export interface FileUpdateInfo {
4
+ path: string;
5
+ filetype: string;
6
+ contents: string;
7
+ }
8
+
9
+ const fileContentCache = new Map<string, string>();
10
+
11
+ /** Record a directory being added to the output. */
12
+ export function recordDirectory(path: string) {
13
+ if (!isDevtoolsEnabled()) return;
14
+ emitDevtoolsMessage({ type: "files:directoryAdded", path });
15
+ }
16
+
17
+ /** Record a file being added to the output. */
18
+ export function recordFile(path: string, filetype: string) {
19
+ if (!isDevtoolsEnabled()) return;
20
+ emitDevtoolsMessage({ type: "files:fileAdded", path, filetype });
21
+ }
22
+
23
+ /** Notify devtools that a file's contents have changed. De-duplicates by content. */
24
+ export function updated(info: FileUpdateInfo) {
25
+ const previous = fileContentCache.get(info.path);
26
+ if (previous === info.contents) return;
27
+ fileContentCache.set(info.path, info.contents);
28
+
29
+ emitDevtoolsMessage({
30
+ type: "files:fileUpdated",
31
+ path: info.path,
32
+ filetype: info.filetype,
33
+ contents: info.contents,
34
+ });
35
+ }
36
+
37
+ /** Clear all cached file state. Called when a new render begins. */
38
+ export function reset() {
39
+ fileContentCache.clear();
40
+ }
@@ -0,0 +1,128 @@
1
+ import {
2
+ assertDevtoolsConnectedForSyncRender,
3
+ initDevtoolsIfEnabled,
4
+ isDevtoolsEnabled,
5
+ } from "../devtools/devtools-server.js";
6
+ import {
7
+ debugContext,
8
+ debugRender,
9
+ debugStack,
10
+ debugTree,
11
+ debugWatch,
12
+ } from "./cli.js";
13
+ import {
14
+ ensureRef,
15
+ register,
16
+ registerRef,
17
+ reset,
18
+ track,
19
+ trigger,
20
+ update,
21
+ } from "./effects.js";
22
+ import {
23
+ recordDirectory,
24
+ recordFile,
25
+ reset as resetFiles,
26
+ updated,
27
+ } from "./files.js";
28
+ import {
29
+ appendCustomContext,
30
+ appendFragmentChild,
31
+ appendPrintHook,
32
+ appendTextNode,
33
+ beginComponent,
34
+ complete,
35
+ error,
36
+ flushJobsComplete,
37
+ initialize,
38
+ prepareMemoNode,
39
+ } from "./render.js";
40
+ import {
41
+ registerScope,
42
+ registerSymbol,
43
+ relocateScope,
44
+ reset as resetSymbols,
45
+ unregisterScope,
46
+ unregisterSymbol,
47
+ } from "./symbols.js";
48
+ import { trace, type TracePhaseInfo } from "./trace.js";
49
+
50
+ export { captureSourceLocation } from "./effects.js";
51
+ export type {
52
+ EffectDebugInfo,
53
+ EffectEdgeDebugInfo,
54
+ RefDebugInfo,
55
+ } from "./effects.js";
56
+ export { getRenderNodeId, type RenderTreeNodeInfo } from "./render.js";
57
+ export type {
58
+ BeginComponentOptions,
59
+ ComponentDebugSession,
60
+ RenderErrorInfo,
61
+ RenderErrorStackEntry,
62
+ } from "./render.js";
63
+ export {
64
+ isConsoleTraceEnabled,
65
+ isDevtoolsEnabled,
66
+ trace,
67
+ TracePhase,
68
+ type TracePhaseInfo,
69
+ } from "./trace.js";
70
+
71
+ /** The full debug runtime interface, derived from the `debug` object implementation. */
72
+ export type DebugRuntime = typeof debug;
73
+
74
+ export const debug = {
75
+ component: {
76
+ stack: debugStack,
77
+ tree: debugTree,
78
+ watch: debugWatch,
79
+ render: debugRender,
80
+ context: debugContext,
81
+ },
82
+ effect: {
83
+ register,
84
+ update,
85
+ registerRef,
86
+ ensureRef,
87
+ track,
88
+ trigger,
89
+ reset,
90
+ },
91
+ render: {
92
+ initialize,
93
+ appendTextNode,
94
+ appendCustomContext,
95
+ appendPrintHook,
96
+ appendFragmentChild,
97
+ beginComponent,
98
+ prepareMemoNode,
99
+ error,
100
+ complete,
101
+ flushJobsComplete,
102
+ },
103
+ files: {
104
+ recordDirectory,
105
+ recordFile,
106
+ updated,
107
+ reset: resetFiles,
108
+ },
109
+ symbols: {
110
+ registerScope,
111
+ relocateScope,
112
+ unregisterScope,
113
+ registerSymbol,
114
+ unregisterSymbol,
115
+ reset: resetSymbols,
116
+ },
117
+ trace(phase: TracePhaseInfo, cb: () => string, triggerIds?: Set<number>) {
118
+ trace(phase, cb, triggerIds ?? new Set());
119
+ },
120
+ async prepare(): Promise<void> {
121
+ await initDevtoolsIfEnabled();
122
+ },
123
+ assertReadyForSyncRender(): void {
124
+ if (isDevtoolsEnabled()) {
125
+ assertDevtoolsConnectedForSyncRender();
126
+ }
127
+ },
128
+ };
@@ -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
+ });