@alloy-js/core 0.24.0-dev.2 → 0.24.0-dev.6

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 (506) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/dev/src/components/AccessExpression.test.js +1 -1
  3. package/dist/dev/src/components/AccessExpression.test.js.map +1 -1
  4. package/dist/dev/src/components/Output.js +3 -2
  5. package/dist/dev/src/components/Output.js.map +1 -1
  6. package/dist/dev/src/components/SourceFile.js.map +1 -1
  7. package/dist/dev/src/content-slot.test.js +1 -1
  8. package/dist/dev/src/content-slot.test.js.map +1 -1
  9. package/dist/dev/src/context.js +30 -3
  10. package/dist/dev/src/context.js.map +1 -1
  11. package/dist/dev/src/debug/diagnostics.test.js +1 -1
  12. package/dist/dev/src/debug/diagnostics.test.js.map +1 -1
  13. package/dist/dev/src/debug/effects.test.js +1 -1
  14. package/dist/dev/src/debug/effects.test.js.map +1 -1
  15. package/dist/dev/src/debug/file-streaming.js +103 -0
  16. package/dist/dev/src/debug/file-streaming.js.map +1 -0
  17. package/dist/dev/src/debug/files.test.js +4 -5
  18. package/dist/dev/src/debug/files.test.js.map +1 -1
  19. package/dist/dev/src/debug/index.js +4 -6
  20. package/dist/dev/src/debug/index.js.map +1 -1
  21. package/dist/dev/src/debug/message-format.test.js +50 -52
  22. package/dist/dev/src/debug/message-format.test.js.map +1 -1
  23. package/dist/dev/src/debug/render-tree-orphans.test.js +13 -23
  24. package/dist/dev/src/debug/render-tree-orphans.test.js.map +1 -1
  25. package/dist/dev/src/debug/render.js +529 -352
  26. package/dist/dev/src/debug/render.js.map +1 -1
  27. package/dist/dev/src/debug/render.test.js +171 -92
  28. package/dist/dev/src/debug/render.test.js.map +1 -1
  29. package/dist/dev/src/debug/trace-writer.js +127 -15
  30. package/dist/dev/src/debug/trace-writer.js.map +1 -1
  31. package/dist/dev/src/debug/trace.js +0 -36
  32. package/dist/dev/src/debug/trace.js.map +1 -1
  33. package/dist/dev/src/devtools/devtools-server.js +55 -32
  34. package/dist/dev/src/devtools/devtools-server.js.map +1 -1
  35. package/dist/dev/src/devtools-entry.browser.js.map +1 -1
  36. package/dist/dev/src/devtools-entry.js.map +1 -1
  37. package/dist/dev/src/diagnostics.js +19 -1
  38. package/dist/dev/src/diagnostics.js.map +1 -1
  39. package/dist/dev/src/index.js +5 -2
  40. package/dist/dev/src/index.js.map +1 -1
  41. package/dist/dev/src/jsx-runtime.js +14 -8
  42. package/dist/dev/src/jsx-runtime.js.map +1 -1
  43. package/dist/dev/src/output-types.js +2 -0
  44. package/dist/dev/src/output-types.js.map +1 -0
  45. package/dist/dev/src/reactivity.js +155 -13
  46. package/dist/dev/src/reactivity.js.map +1 -1
  47. package/dist/dev/src/render/get-string-width.js +61 -0
  48. package/dist/dev/src/render/get-string-width.js.map +1 -0
  49. package/dist/dev/src/render/index.js +2 -0
  50. package/dist/dev/src/render/index.js.map +1 -0
  51. package/dist/dev/src/render/node-context.js +7 -0
  52. package/dist/dev/src/render/node-context.js.map +1 -0
  53. package/dist/dev/src/render/node.js +386 -0
  54. package/dist/dev/src/render/node.js.map +1 -0
  55. package/dist/dev/src/render/printer-support.js +180 -0
  56. package/dist/dev/src/render/printer-support.js.map +1 -0
  57. package/dist/dev/src/render/printer.js +797 -0
  58. package/dist/dev/src/render/printer.js.map +1 -0
  59. package/dist/dev/src/render-error.js +79 -0
  60. package/dist/dev/src/render-error.js.map +1 -0
  61. package/dist/dev/src/render-output.js +209 -0
  62. package/dist/dev/src/render-output.js.map +1 -0
  63. package/dist/dev/src/runtime/create-intrinsic.js +53 -0
  64. package/dist/dev/src/runtime/create-intrinsic.js.map +1 -0
  65. package/dist/dev/src/runtime/fragment.js +21 -0
  66. package/dist/dev/src/runtime/fragment.js.map +1 -0
  67. package/dist/dev/src/runtime/index.js +13 -0
  68. package/dist/dev/src/runtime/index.js.map +1 -0
  69. package/dist/dev/src/runtime/insert.js +453 -0
  70. package/dist/dev/src/runtime/insert.js.map +1 -0
  71. package/dist/dev/src/runtime/intrinsic.js +1 -11
  72. package/dist/dev/src/runtime/intrinsic.js.map +1 -1
  73. package/dist/dev/src/scheduler.js +38 -14
  74. package/dist/dev/src/scheduler.js.map +1 -1
  75. package/dist/dev/src/stc.js +2 -0
  76. package/dist/dev/src/stc.js.map +1 -1
  77. package/dist/dev/src/sti.js +1 -1
  78. package/dist/dev/src/sti.js.map +1 -1
  79. package/dist/dev/src/symbols/symbol-slot.test.js +1 -1
  80. package/dist/dev/src/symbols/symbol-slot.test.js.map +1 -1
  81. package/dist/dev/src/test-render.js +78 -0
  82. package/dist/dev/src/test-render.js.map +1 -0
  83. package/dist/dev/src/utils.js +47 -35
  84. package/dist/dev/src/utils.js.map +1 -1
  85. package/dist/dev/test/babel-e2e.test.js +218 -0
  86. package/dist/dev/test/babel-e2e.test.js.map +1 -0
  87. package/dist/dev/test/components/block.test.js +1 -1
  88. package/dist/dev/test/components/block.test.js.map +1 -1
  89. package/dist/dev/test/components/copy-file.test.js +7 -7
  90. package/dist/dev/test/components/copy-file.test.js.map +1 -1
  91. package/dist/dev/test/components/update-file.test.js +1 -1
  92. package/dist/dev/test/components/update-file.test.js.map +1 -1
  93. package/dist/dev/test/components/wrap.test.js +1 -1
  94. package/dist/dev/test/components/wrap.test.js.map +1 -1
  95. package/dist/dev/test/control-flow/match.test.js +1 -1
  96. package/dist/dev/test/control-flow/match.test.js.map +1 -1
  97. package/dist/dev/test/control-flow/show.test.js +1 -1
  98. package/dist/dev/test/control-flow/show.test.js.map +1 -1
  99. package/dist/dev/test/lazy-isempty.test.js +6 -6
  100. package/dist/dev/test/lazy-isempty.test.js.map +1 -1
  101. package/dist/dev/test/node.test.js +80 -0
  102. package/dist/dev/test/node.test.js.map +1 -0
  103. package/dist/dev/test/output-e2e.test.js +194 -0
  104. package/dist/dev/test/output-e2e.test.js.map +1 -0
  105. package/dist/dev/test/reactivity/circular-reactives.test.js +1 -1
  106. package/dist/dev/test/reactivity/circular-reactives.test.js.map +1 -1
  107. package/dist/dev/test/reactivity/cleanup.test.js +1 -1
  108. package/dist/dev/test/reactivity/cleanup.test.js.map +1 -1
  109. package/dist/dev/test/rendering/memoization.test.js +6 -1
  110. package/dist/dev/test/rendering/memoization.test.js.map +1 -1
  111. package/dist/dev/test/rendering/render-output-diagnostics.test.js +102 -0
  112. package/dist/dev/test/rendering/render-output-diagnostics.test.js.map +1 -0
  113. package/dist/dev/test/runtime.test.js +385 -0
  114. package/dist/dev/test/runtime.test.js.map +1 -0
  115. package/dist/dev/test/tree-test-utils.js +16 -0
  116. package/dist/dev/test/tree-test-utils.js.map +1 -0
  117. package/dist/dev/test/utils.test.js +1 -1
  118. package/dist/dev/test/utils.test.js.map +1 -1
  119. package/dist/dev/testing/devtools-utils.js +1 -1
  120. package/dist/dev/testing/devtools-utils.js.map +1 -1
  121. package/dist/dev/testing/extend-expect.js +7 -33
  122. package/dist/dev/testing/extend-expect.js.map +1 -1
  123. package/dist/dev/testing/render.js +7 -17
  124. package/dist/dev/testing/render.js.map +1 -1
  125. package/dist/devtools/index.html +17 -17
  126. package/dist/src/components/AccessExpression.test.js +1 -1
  127. package/dist/src/components/AccessExpression.test.js.map +1 -1
  128. package/dist/src/components/Output.d.ts +1 -1
  129. package/dist/src/components/Output.d.ts.map +1 -1
  130. package/dist/src/components/Output.js +2 -1
  131. package/dist/src/components/Output.js.map +1 -1
  132. package/dist/src/components/ReferenceOrContent.d.ts +1 -1
  133. package/dist/src/components/ReferenceOrContent.d.ts.map +1 -1
  134. package/dist/src/components/SourceFile.d.ts +1 -1
  135. package/dist/src/components/SourceFile.d.ts.map +1 -1
  136. package/dist/src/components/SourceFile.js.map +1 -1
  137. package/dist/src/content-slot.test.js +1 -1
  138. package/dist/src/content-slot.test.js.map +1 -1
  139. package/dist/src/context/format-options.d.ts +1 -1
  140. package/dist/src/context/format-options.d.ts.map +1 -1
  141. package/dist/src/context.d.ts +9 -1
  142. package/dist/src/context.d.ts.map +1 -1
  143. package/dist/src/context.js +30 -3
  144. package/dist/src/context.js.map +1 -1
  145. package/dist/src/debug/diagnostics.test.js +1 -1
  146. package/dist/src/debug/diagnostics.test.js.map +1 -1
  147. package/dist/src/debug/effects.test.js +1 -1
  148. package/dist/src/debug/effects.test.js.map +1 -1
  149. package/dist/src/debug/file-streaming.d.ts +22 -0
  150. package/dist/src/debug/file-streaming.d.ts.map +1 -0
  151. package/dist/src/debug/file-streaming.js +103 -0
  152. package/dist/src/debug/file-streaming.js.map +1 -0
  153. package/dist/src/debug/files.test.js +4 -5
  154. package/dist/src/debug/files.test.js.map +1 -1
  155. package/dist/src/debug/index.d.ts +5 -7
  156. package/dist/src/debug/index.d.ts.map +1 -1
  157. package/dist/src/debug/index.js +4 -6
  158. package/dist/src/debug/index.js.map +1 -1
  159. package/dist/src/debug/message-format.test.js +16 -18
  160. package/dist/src/debug/message-format.test.js.map +1 -1
  161. package/dist/src/debug/render-tree-orphans.test.js +8 -18
  162. package/dist/src/debug/render-tree-orphans.test.js.map +1 -1
  163. package/dist/src/debug/render.d.ts +71 -21
  164. package/dist/src/debug/render.d.ts.map +1 -1
  165. package/dist/src/debug/render.js +529 -352
  166. package/dist/src/debug/render.js.map +1 -1
  167. package/dist/src/debug/render.test.js +137 -74
  168. package/dist/src/debug/render.test.js.map +1 -1
  169. package/dist/src/debug/trace-writer.d.ts +6 -1
  170. package/dist/src/debug/trace-writer.d.ts.map +1 -1
  171. package/dist/src/debug/trace-writer.js +127 -15
  172. package/dist/src/debug/trace-writer.js.map +1 -1
  173. package/dist/src/debug/trace.d.ts +0 -36
  174. package/dist/src/debug/trace.d.ts.map +1 -1
  175. package/dist/src/debug/trace.js +0 -36
  176. package/dist/src/debug/trace.js.map +1 -1
  177. package/dist/src/devtools/devtools-protocol.d.ts +34 -1
  178. package/dist/src/devtools/devtools-protocol.d.ts.map +1 -1
  179. package/dist/src/devtools/devtools-server.d.ts.map +1 -1
  180. package/dist/src/devtools/devtools-server.js +55 -32
  181. package/dist/src/devtools/devtools-server.js.map +1 -1
  182. package/dist/src/devtools-entry.browser.d.ts +1 -1
  183. package/dist/src/devtools-entry.browser.d.ts.map +1 -1
  184. package/dist/src/devtools-entry.browser.js.map +1 -1
  185. package/dist/src/devtools-entry.d.ts +1 -1
  186. package/dist/src/devtools-entry.d.ts.map +1 -1
  187. package/dist/src/devtools-entry.js.map +1 -1
  188. package/dist/src/diagnostics.d.ts +4 -0
  189. package/dist/src/diagnostics.d.ts.map +1 -1
  190. package/dist/src/diagnostics.js +19 -1
  191. package/dist/src/diagnostics.js.map +1 -1
  192. package/dist/src/index.d.ts +5 -2
  193. package/dist/src/index.d.ts.map +1 -1
  194. package/dist/src/index.js +5 -2
  195. package/dist/src/index.js.map +1 -1
  196. package/dist/src/jsx-runtime.d.ts +13 -4
  197. package/dist/src/jsx-runtime.d.ts.map +1 -1
  198. package/dist/src/jsx-runtime.js +14 -8
  199. package/dist/src/jsx-runtime.js.map +1 -1
  200. package/dist/src/output-types.d.ts +40 -0
  201. package/dist/src/output-types.d.ts.map +1 -0
  202. package/dist/src/output-types.js +2 -0
  203. package/dist/src/output-types.js.map +1 -0
  204. package/dist/src/reactivity.d.ts +49 -18
  205. package/dist/src/reactivity.d.ts.map +1 -1
  206. package/dist/src/reactivity.js +155 -13
  207. package/dist/src/reactivity.js.map +1 -1
  208. package/dist/src/render/get-string-width.d.ts +19 -0
  209. package/dist/src/render/get-string-width.d.ts.map +1 -0
  210. package/dist/src/render/get-string-width.js +61 -0
  211. package/dist/src/render/get-string-width.js.map +1 -0
  212. package/dist/src/render/index.d.ts +2 -0
  213. package/dist/src/render/index.d.ts.map +1 -0
  214. package/dist/src/render/index.js +2 -0
  215. package/dist/src/render/index.js.map +1 -0
  216. package/dist/src/render/node-context.d.ts +5 -0
  217. package/dist/src/render/node-context.d.ts.map +1 -0
  218. package/dist/src/render/node-context.js +7 -0
  219. package/dist/src/render/node-context.js.map +1 -0
  220. package/dist/src/render/node.d.ts +146 -0
  221. package/dist/src/render/node.d.ts.map +1 -0
  222. package/dist/src/render/node.js +386 -0
  223. package/dist/src/render/node.js.map +1 -0
  224. package/dist/src/render/printer-support.d.ts +50 -0
  225. package/dist/src/render/printer-support.d.ts.map +1 -0
  226. package/dist/src/render/printer-support.js +180 -0
  227. package/dist/src/render/printer-support.js.map +1 -0
  228. package/dist/src/render/printer.d.ts +35 -0
  229. package/dist/src/render/printer.d.ts.map +1 -0
  230. package/dist/src/render/printer.js +797 -0
  231. package/dist/src/render/printer.js.map +1 -0
  232. package/dist/src/render-error.d.ts +4 -0
  233. package/dist/src/render-error.d.ts.map +1 -0
  234. package/dist/src/render-error.js +79 -0
  235. package/dist/src/render-error.js.map +1 -0
  236. package/dist/src/render-output.d.ts +42 -0
  237. package/dist/src/render-output.d.ts.map +1 -0
  238. package/dist/src/render-output.js +209 -0
  239. package/dist/src/render-output.js.map +1 -0
  240. package/dist/src/runtime/component.d.ts +2 -2
  241. package/dist/src/runtime/component.d.ts.map +1 -1
  242. package/dist/src/runtime/create-intrinsic.d.ts +28 -0
  243. package/dist/src/runtime/create-intrinsic.d.ts.map +1 -0
  244. package/dist/src/runtime/create-intrinsic.js +53 -0
  245. package/dist/src/runtime/create-intrinsic.js.map +1 -0
  246. package/dist/src/runtime/fragment.d.ts +16 -0
  247. package/dist/src/runtime/fragment.d.ts.map +1 -0
  248. package/dist/src/runtime/fragment.js +21 -0
  249. package/dist/src/runtime/fragment.js.map +1 -0
  250. package/dist/src/runtime/index.d.ts +12 -0
  251. package/dist/src/runtime/index.d.ts.map +1 -0
  252. package/dist/src/runtime/index.js +13 -0
  253. package/dist/src/runtime/index.js.map +1 -0
  254. package/dist/src/runtime/insert.d.ts +29 -0
  255. package/dist/src/runtime/insert.d.ts.map +1 -0
  256. package/dist/src/runtime/insert.js +453 -0
  257. package/dist/src/runtime/insert.js.map +1 -0
  258. package/dist/src/runtime/intrinsic.d.ts +12 -29
  259. package/dist/src/runtime/intrinsic.d.ts.map +1 -1
  260. package/dist/src/runtime/intrinsic.js +1 -11
  261. package/dist/src/runtime/intrinsic.js.map +1 -1
  262. package/dist/src/scheduler.d.ts.map +1 -1
  263. package/dist/src/scheduler.js +38 -14
  264. package/dist/src/scheduler.js.map +1 -1
  265. package/dist/src/stc.d.ts.map +1 -1
  266. package/dist/src/stc.js +2 -0
  267. package/dist/src/stc.js.map +1 -1
  268. package/dist/src/sti.d.ts +7 -6
  269. package/dist/src/sti.d.ts.map +1 -1
  270. package/dist/src/sti.js +1 -1
  271. package/dist/src/sti.js.map +1 -1
  272. package/dist/src/symbols/symbol-slot.test.js +1 -1
  273. package/dist/src/symbols/symbol-slot.test.js.map +1 -1
  274. package/dist/src/test-render.d.ts +31 -0
  275. package/dist/src/test-render.d.ts.map +1 -0
  276. package/dist/src/test-render.js +78 -0
  277. package/dist/src/test-render.js.map +1 -0
  278. package/dist/src/utils.d.ts +1 -1
  279. package/dist/src/utils.d.ts.map +1 -1
  280. package/dist/src/utils.js +40 -28
  281. package/dist/src/utils.js.map +1 -1
  282. package/dist/src/write-output.d.ts +1 -1
  283. package/dist/src/write-output.d.ts.map +1 -1
  284. package/dist/test/babel-e2e.test.d.ts +13 -0
  285. package/dist/test/babel-e2e.test.d.ts.map +1 -0
  286. package/dist/test/babel-e2e.test.js +218 -0
  287. package/dist/test/babel-e2e.test.js.map +1 -0
  288. package/dist/test/components/block.test.js +1 -1
  289. package/dist/test/components/block.test.js.map +1 -1
  290. package/dist/test/components/copy-file.test.d.ts.map +1 -1
  291. package/dist/test/components/copy-file.test.js +1 -1
  292. package/dist/test/components/copy-file.test.js.map +1 -1
  293. package/dist/test/components/update-file.test.js +1 -1
  294. package/dist/test/components/update-file.test.js.map +1 -1
  295. package/dist/test/components/wrap.test.js +1 -1
  296. package/dist/test/components/wrap.test.js.map +1 -1
  297. package/dist/test/control-flow/match.test.js +1 -1
  298. package/dist/test/control-flow/match.test.js.map +1 -1
  299. package/dist/test/control-flow/show.test.js +1 -1
  300. package/dist/test/control-flow/show.test.js.map +1 -1
  301. package/dist/test/lazy-isempty.test.js +6 -6
  302. package/dist/test/lazy-isempty.test.js.map +1 -1
  303. package/dist/test/node.test.d.ts +2 -0
  304. package/dist/test/node.test.d.ts.map +1 -0
  305. package/dist/test/node.test.js +80 -0
  306. package/dist/test/node.test.js.map +1 -0
  307. package/dist/test/output-e2e.test.d.ts +13 -0
  308. package/dist/test/output-e2e.test.d.ts.map +1 -0
  309. package/dist/test/output-e2e.test.js +194 -0
  310. package/dist/test/output-e2e.test.js.map +1 -0
  311. package/dist/test/reactivity/circular-reactives.test.js +1 -1
  312. package/dist/test/reactivity/circular-reactives.test.js.map +1 -1
  313. package/dist/test/reactivity/cleanup.test.js +1 -1
  314. package/dist/test/reactivity/cleanup.test.js.map +1 -1
  315. package/dist/test/rendering/memoization.test.js +6 -1
  316. package/dist/test/rendering/memoization.test.js.map +1 -1
  317. package/dist/test/rendering/render-output-diagnostics.test.d.ts +2 -0
  318. package/dist/test/rendering/render-output-diagnostics.test.d.ts.map +1 -0
  319. package/dist/test/rendering/render-output-diagnostics.test.js +82 -0
  320. package/dist/test/rendering/render-output-diagnostics.test.js.map +1 -0
  321. package/dist/test/runtime.test.d.ts +11 -0
  322. package/dist/test/runtime.test.d.ts.map +1 -0
  323. package/dist/test/runtime.test.js +385 -0
  324. package/dist/test/runtime.test.js.map +1 -0
  325. package/dist/test/tree-test-utils.d.ts +3 -0
  326. package/dist/test/tree-test-utils.d.ts.map +1 -0
  327. package/dist/test/tree-test-utils.js +16 -0
  328. package/dist/test/tree-test-utils.js.map +1 -0
  329. package/dist/test/utils.test.js +1 -1
  330. package/dist/test/utils.test.js.map +1 -1
  331. package/dist/testing/devtools-utils.d.ts.map +1 -1
  332. package/dist/testing/devtools-utils.js +1 -1
  333. package/dist/testing/devtools-utils.js.map +1 -1
  334. package/dist/testing/extend-expect.d.ts.map +1 -1
  335. package/dist/testing/extend-expect.js +7 -33
  336. package/dist/testing/extend-expect.js.map +1 -1
  337. package/dist/testing/render.d.ts +7 -9
  338. package/dist/testing/render.d.ts.map +1 -1
  339. package/dist/testing/render.js +7 -17
  340. package/dist/testing/render.js.map +1 -1
  341. package/dist/tsconfig.tsbuildinfo +1 -1
  342. package/docs/api/components/Output.md +0 -3
  343. package/docs/api/components/SourceFile.md +0 -3
  344. package/docs/api/functions/createComment.md +18 -0
  345. package/docs/api/functions/createElement.md +19 -0
  346. package/docs/api/functions/createFragment.md +17 -0
  347. package/docs/api/functions/createTextNode.md +18 -0
  348. package/docs/api/functions/emitDiagnosticForTree.md +19 -0
  349. package/docs/api/functions/ensureIsEmpty.md +1 -1
  350. package/docs/api/functions/getContextForNode.md +18 -0
  351. package/docs/api/functions/getContextForRenderNode.md +4 -4
  352. package/docs/api/functions/getDiagnosticsForTree.md +7 -5
  353. package/docs/api/functions/getRegisteredDiagnosticsForTree.md +18 -0
  354. package/docs/api/functions/index.md +17 -12
  355. package/docs/api/functions/isCustomContext.md +4 -4
  356. package/docs/api/functions/notifyContentState.md +6 -0
  357. package/docs/api/functions/printTree.md +6 -16
  358. package/docs/api/functions/registerDiagnosticsForTree.md +19 -0
  359. package/docs/api/functions/render.md +1 -2
  360. package/docs/api/functions/renderAsync.md +1 -2
  361. package/docs/api/functions/renderTree.md +8 -5
  362. package/docs/api/functions/reportDiagnosticsForTree.md +18 -0
  363. package/docs/api/functions/runInContext.md +28 -0
  364. package/docs/api/functions/sourceFilesForTree.md +6 -16
  365. package/docs/api/index.md +3 -3
  366. package/docs/api/testing/functions/index.md +1 -1
  367. package/docs/api/testing/functions/renderToString.md +1 -1
  368. package/docs/api/types/AlloyNode.md +22 -0
  369. package/docs/api/types/Child.md +1 -1
  370. package/docs/api/types/CommentNode.md +15 -0
  371. package/docs/api/types/Context.md +13 -15
  372. package/docs/api/types/ElementNode.md +18 -0
  373. package/docs/api/types/FragmentNode.md +12 -0
  374. package/docs/api/types/Insertable.md +7 -0
  375. package/docs/api/types/NodeType.md +5 -0
  376. package/docs/api/types/OutputDirectory.md +0 -50
  377. package/docs/api/types/PrintTreeOptions.md +0 -1
  378. package/docs/api/types/RenderTreeOptions.md +7 -0
  379. package/docs/api/types/StiComponentCreator.md +4 -4
  380. package/docs/api/types/StiSignature.md +1 -1
  381. package/docs/api/types/TextNode.md +16 -0
  382. package/docs/api/types/index.md +10 -28
  383. package/docs/api/variables/COMMENT_NODE.md +5 -0
  384. package/docs/api/variables/ELEMENT_NODE.md +11 -0
  385. package/docs/api/variables/FRAGMENT_NODE.md +5 -0
  386. package/docs/api/variables/TEXT_NODE.md +5 -0
  387. package/docs/api/variables/index.md +4 -2
  388. package/docs/formatting.md +1 -1
  389. package/docs/rendering.md +4 -4
  390. package/package.json +6 -6
  391. package/src/components/AccessExpression.test.tsx +1 -1
  392. package/src/components/Output.tsx +2 -1
  393. package/src/components/SourceFile.tsx +1 -1
  394. package/src/content-slot.test.tsx +1 -1
  395. package/src/context/format-options.ts +1 -1
  396. package/src/context.ts +37 -4
  397. package/src/debug/diagnostics.test.tsx +1 -1
  398. package/src/debug/effects.test.tsx +1 -1
  399. package/src/debug/file-streaming.ts +115 -0
  400. package/src/debug/files.test.tsx +15 -11
  401. package/src/debug/index.ts +11 -11
  402. package/src/debug/message-format.test.tsx +32 -19
  403. package/src/debug/render-tree-orphans.test.tsx +10 -19
  404. package/src/debug/render.test.tsx +206 -78
  405. package/src/debug/render.ts +642 -495
  406. package/src/debug/trace-writer.ts +168 -14
  407. package/src/debug/trace.ts +0 -20
  408. package/src/devtools/devtools-protocol.ts +43 -0
  409. package/src/devtools/devtools-server.ts +57 -32
  410. package/src/devtools-entry.browser.ts +5 -0
  411. package/src/devtools-entry.ts +5 -0
  412. package/src/diagnostics.ts +31 -0
  413. package/src/index.ts +66 -2
  414. package/src/jsx-runtime.ts +16 -14
  415. package/src/output-types.ts +47 -0
  416. package/src/reactivity.ts +186 -40
  417. package/src/render/get-string-width.ts +201 -0
  418. package/src/render/index.ts +1 -0
  419. package/src/render/node-context.ts +14 -0
  420. package/src/render/node.ts +442 -0
  421. package/src/render/printer-support.ts +209 -0
  422. package/src/render/printer.ts +817 -0
  423. package/src/render-error.ts +98 -0
  424. package/src/render-output.ts +243 -0
  425. package/src/runtime/component.ts +2 -2
  426. package/src/runtime/create-intrinsic.ts +56 -0
  427. package/src/runtime/fragment.ts +22 -0
  428. package/src/runtime/index.ts +12 -0
  429. package/src/runtime/insert.ts +569 -0
  430. package/src/runtime/intrinsic.ts +14 -70
  431. package/src/scheduler.ts +40 -25
  432. package/src/stc.ts +3 -0
  433. package/src/sti.ts +17 -20
  434. package/src/symbols/symbol-slot.test.tsx +1 -1
  435. package/src/test-render.ts +103 -0
  436. package/src/utils.tsx +55 -37
  437. package/src/write-output.ts +1 -1
  438. package/temp/api-testing.json +390 -14
  439. package/temp/api.json +4320 -4144
  440. package/test/babel-e2e.test.ts +224 -0
  441. package/test/components/block.test.tsx +1 -1
  442. package/test/components/copy-file.test.tsx +2 -1
  443. package/test/components/update-file.test.tsx +1 -1
  444. package/test/components/wrap.test.tsx +1 -1
  445. package/test/control-flow/match.test.tsx +1 -1
  446. package/test/control-flow/show.test.tsx +1 -1
  447. package/test/lazy-isempty.test.tsx +6 -6
  448. package/test/node.test.ts +90 -0
  449. package/test/output-e2e.test.ts +198 -0
  450. package/test/reactivity/circular-reactives.test.tsx +1 -1
  451. package/test/reactivity/cleanup.test.tsx +1 -1
  452. package/test/rendering/memoization.test.tsx +6 -1
  453. package/test/rendering/render-output-diagnostics.test.tsx +120 -0
  454. package/test/runtime.test.ts +448 -0
  455. package/test/tree-test-utils.ts +23 -0
  456. package/test/utils.test.tsx +1 -1
  457. package/testing/devtools-utils.ts +2 -0
  458. package/testing/extend-expect.ts +8 -46
  459. package/testing/render.ts +17 -21
  460. package/dist/dev/src/print-hook.js +0 -10
  461. package/dist/dev/src/print-hook.js.map +0 -1
  462. package/dist/dev/src/render.js +0 -872
  463. package/dist/dev/src/render.js.map +0 -1
  464. package/dist/src/print-hook.d.ts +0 -14
  465. package/dist/src/print-hook.d.ts.map +0 -1
  466. package/dist/src/print-hook.js +0 -10
  467. package/dist/src/print-hook.js.map +0 -1
  468. package/dist/src/render.d.ts +0 -155
  469. package/dist/src/render.d.ts.map +0 -1
  470. package/dist/src/render.js +0 -872
  471. package/dist/src/render.js.map +0 -1
  472. package/docs/api/functions/createIntrinsic.md +0 -19
  473. package/docs/api/functions/createRenderTreeHook.md +0 -19
  474. package/docs/api/functions/getElementCache.md +0 -17
  475. package/docs/api/functions/isIntrinsicElement.md +0 -18
  476. package/docs/api/functions/isPrintHook.md +0 -18
  477. package/docs/api/types/AlignIntrinsicElement.md +0 -5
  478. package/docs/api/types/BrIntrinsicElement.md +0 -5
  479. package/docs/api/types/BreakParentIntrinsicElement.md +0 -5
  480. package/docs/api/types/DedentIntrinsicElement.md +0 -5
  481. package/docs/api/types/DedentToRootIntrinsicElement.md +0 -5
  482. package/docs/api/types/ElementCache.md +0 -5
  483. package/docs/api/types/ElementCacheKey.md +0 -5
  484. package/docs/api/types/FillIntrinsicElement.md +0 -5
  485. package/docs/api/types/GroupIntrinsicElement.md +0 -5
  486. package/docs/api/types/HardlineIntrinsicElement.md +0 -5
  487. package/docs/api/types/HbrIntrinsicElement.md +0 -5
  488. package/docs/api/types/IfBreakIntrinsicElement.md +0 -5
  489. package/docs/api/types/IndentIfBreakIntrinsicElement.md +0 -5
  490. package/docs/api/types/IndentIntrinsicElement.md +0 -5
  491. package/docs/api/types/IntrinsicElement.md +0 -5
  492. package/docs/api/types/IntrinsicElementBase.md +0 -9
  493. package/docs/api/types/LbrIntrinsicElement.md +0 -5
  494. package/docs/api/types/LineIntrinsicElement.md +0 -5
  495. package/docs/api/types/LineSuffixBoundaryIntrinsicElement.md +0 -5
  496. package/docs/api/types/LineSuffixIntrinsicElement.md +0 -5
  497. package/docs/api/types/LiterallineIntrinsicElement.md +0 -5
  498. package/docs/api/types/MarkAsRootIntrinsicElement.md +0 -5
  499. package/docs/api/types/PrintHook.md +0 -10
  500. package/docs/api/types/RenderedTextTree.md +0 -5
  501. package/docs/api/types/SbrIntrinsicElement.md +0 -5
  502. package/docs/api/types/SoftlineIntrinsicElement.md +0 -5
  503. package/docs/api/variables/intrinsicElementKey.md +0 -5
  504. package/docs/api/variables/printHookTag.md +0 -7
  505. package/src/print-hook.ts +0 -22
  506. package/src/render.ts +0 -1154
@@ -0,0 +1,817 @@
1
+ /**
2
+ * Direct AlloyNode tree printer.
3
+ *
4
+ * The printer walks the DOM-like AlloyNode tree directly, dispatching on
5
+ * element `localName` and node type. Work items on the command stack carry
6
+ * AlloyNode references or small synthetic frames for constructs that have no
7
+ * node counterpart, such as fill continuation state, `ifBreak` alternate
8
+ * branches, and split text segments.
9
+ *
10
+ * Correctness invariants:
11
+ *
12
+ * - `fits` is continuation-aware (passes `restCommands`).
13
+ * - `breakParent` propagation runs as a prepass over the AlloyNode
14
+ * tree, including into `lineSuffix` content and both branches of
15
+ * `ifBreak` (matching Prettier's `traverseDoc`). Hardline / hbr /
16
+ * literalline / lbr / breakParent / TextNode-with-newline all
17
+ * trigger the propagation.
18
+ * - `lineSuffix` collects deferred `{ind, mode, frame}` commands.
19
+ * - `groupModeMap` carries forward-ref semantics: in `fits`,
20
+ * unknown groupId behaves as flat; during printing, unknown
21
+ * groupId yields no branch on `if-break` / `indent-if-break`.
22
+ * - `fill` uses `mustBeFlat=true` and a separate
23
+ * `firstAndSecondContentFits` measurement; comments are skipped
24
+ * when collecting the parts list.
25
+ * - `trim` rolls back trailing spaces / tabs from the `out` buffer.
26
+ * - String width is Prettier's CJK / east-asian / emoji-aware
27
+ * `getStringWidth` (vendored in `get-string-width.ts`).
28
+ */
29
+
30
+ import {
31
+ AlloyNode,
32
+ COMMENT_NODE,
33
+ ElementNode,
34
+ FRAGMENT_NODE,
35
+ TEXT_NODE,
36
+ TextNode,
37
+ } from "./node.js";
38
+ import {
39
+ CURSOR_PLACEHOLDER,
40
+ getStringWidth,
41
+ Indent,
42
+ joinOut,
43
+ makeAlign,
44
+ makeIndent,
45
+ Mode,
46
+ MODE_BREAK,
47
+ MODE_FLAT,
48
+ PrintNodeOptions,
49
+ rootIndent,
50
+ textHasNewline,
51
+ textWidth,
52
+ trimOut,
53
+ } from "./printer-support.js";
54
+
55
+ // #region Frame types
56
+
57
+ const SYNTH = Symbol("alloy:synthetic-frame");
58
+
59
+ type SynthFrame = {
60
+ [k in typeof SYNTH]: true;
61
+ } &
62
+ // Fill remaining state: re-print `node` but starting at `offset`.
63
+ (| { kind: "fill"; node: ElementNode; offset: number }
64
+ // Synthesize an inline list of nodes (used for fill's
65
+ // [content, whitespace, secondContent] measurement and for
66
+ // text-node newline expansion). Items are popped right-to-left.
67
+ | { kind: "list"; items: Frame[] }
68
+ // Synthesize a `line` with the given attributes (used for
69
+ // `line-suffix-boundary` flushing a hardline-without-break).
70
+ | { kind: "line"; hard?: boolean; soft?: boolean; literal?: boolean }
71
+ // Synthesize an `indent` wrapping the given AlloyNode contents
72
+ // (used by `indent-if-break` negate path).
73
+ | { kind: "indent"; node: ElementNode }
74
+ );
75
+
76
+ type Frame = AlloyNode | string | SynthFrame;
77
+
78
+ interface Cmd {
79
+ ind: Indent;
80
+ mode: Mode;
81
+ frame: Frame;
82
+ }
83
+
84
+ // #endregion
85
+
86
+ // #region Group broken predicate
87
+
88
+ /**
89
+ * O(1) replacement for Prettier's `propagateBreaks` prepass — the
90
+ * `_breakCount` field on `ElementNode` is maintained eagerly during
91
+ * insertion (see `propagateBreakDelta` in `node.ts`), so deciding
92
+ * whether a `group` is broken is just a field read plus the
93
+ * `shouldBreak` data flag.
94
+ */
95
+ function isGroupBroken(el: ElementNode): boolean {
96
+ if (el._breakCount > 0) return true;
97
+ const data = el.data as { shouldBreak?: boolean } | undefined;
98
+ return data?.shouldBreak === true;
99
+ }
100
+
101
+ // #endregion
102
+
103
+ // #region fits
104
+
105
+ function fits(
106
+ next: Cmd,
107
+ restCommands: Cmd[],
108
+ width: number,
109
+ hasLineSuffix: boolean,
110
+ groupModeMap: Record<symbol, Mode>,
111
+ mustBeFlat: boolean,
112
+ ): boolean {
113
+ if (width === Number.POSITIVE_INFINITY) return true;
114
+ let restIdx = restCommands.length;
115
+ const cmds: Cmd[] = [next];
116
+ const out: (string | symbol)[] = [];
117
+ const dummyInd = next.ind;
118
+ while (width >= 0) {
119
+ if (cmds.length === 0) {
120
+ if (restIdx === 0) return true;
121
+ cmds.push(restCommands[--restIdx]);
122
+ continue;
123
+ }
124
+ const cmd = cmds.pop()!;
125
+ const mode = cmd.mode;
126
+ const frame = cmd.frame;
127
+
128
+ if (typeof frame === "string") {
129
+ out.push(frame);
130
+ width -= getStringWidth(frame);
131
+ continue;
132
+ }
133
+
134
+ if (isSynth(frame)) {
135
+ switch (frame.kind) {
136
+ case "fill": {
137
+ const parts = collectChildren(frame.node);
138
+ for (let i = parts.length - 1; i >= frame.offset; i--) {
139
+ cmds.push({ ind: cmd.ind, mode, frame: parts[i] });
140
+ }
141
+ break;
142
+ }
143
+ case "list": {
144
+ for (let i = frame.items.length - 1; i >= 0; i--) {
145
+ cmds.push({ ind: cmd.ind, mode, frame: frame.items[i] });
146
+ }
147
+ break;
148
+ }
149
+ case "line": {
150
+ if (mode === MODE_BREAK || frame.hard) return true;
151
+ if (!frame.soft) {
152
+ out.push(" ");
153
+ width--;
154
+ }
155
+ break;
156
+ }
157
+ case "indent": {
158
+ // Synthesized indent wrapper — same effect on width as
159
+ // descending into the wrapped node's children.
160
+ for (
161
+ let c = frame.node.lastChild;
162
+ c !== null;
163
+ c = c.previousSibling
164
+ ) {
165
+ if (c.nodeType === COMMENT_NODE) continue;
166
+ cmds.push({ ind: cmd.ind, mode, frame: c });
167
+ }
168
+ break;
169
+ }
170
+ }
171
+ continue;
172
+ }
173
+
174
+ // AlloyNode
175
+ const node = frame as AlloyNode;
176
+ const nt = node.nodeType;
177
+ if (nt === TEXT_NODE) {
178
+ const tn = node as TextNode;
179
+ const text = tn.data;
180
+ if (text === "") continue;
181
+ if (!textHasNewline(tn)) {
182
+ out.push(text);
183
+ width -= textWidth(tn);
184
+ continue;
185
+ }
186
+ // Multi-line text: on the first newline encountered (in MODE_FLAT
187
+ // we treat it as forcing break — fits returns true if mode is
188
+ // BREAK, since reaching a hard line means content fits in the
189
+ // remaining buffer).
190
+ // Push reversed segments.
191
+ const items = expandMultilineText(text);
192
+ for (let i = items.length - 1; i >= 0; i--) {
193
+ cmds.push({ ind: cmd.ind, mode, frame: items[i] });
194
+ }
195
+ continue;
196
+ }
197
+ if (nt === COMMENT_NODE) continue;
198
+ if (nt === FRAGMENT_NODE) {
199
+ for (let c = node.lastChild; c !== null; c = c.previousSibling) {
200
+ if (c.nodeType === COMMENT_NODE) continue;
201
+ cmds.push({ ind: cmd.ind, mode, frame: c });
202
+ }
203
+ continue;
204
+ }
205
+ // ELEMENT_NODE
206
+ const el = node as ElementNode;
207
+ const ln = el.localName;
208
+ switch (ln) {
209
+ case "line":
210
+ case "br":
211
+ if (mode === MODE_BREAK) return true;
212
+ out.push(" ");
213
+ width--;
214
+ break;
215
+ case "softline":
216
+ case "sbr":
217
+ if (mode === MODE_BREAK) return true;
218
+ break;
219
+ case "hardline":
220
+ case "hbr":
221
+ case "literalline":
222
+ case "lbr":
223
+ return true;
224
+ case "breakParent":
225
+ break;
226
+ case "trim":
227
+ width += trimOut(out);
228
+ break;
229
+ case "lineSuffix":
230
+ hasLineSuffix = true;
231
+ break;
232
+ case "lineSuffixBoundary":
233
+ if (hasLineSuffix) return false;
234
+ break;
235
+ case "cursor":
236
+ // Width-neutral marker.
237
+ break;
238
+ case "group": {
239
+ const broken = isGroupBroken(el);
240
+ if (mustBeFlat && broken) return false;
241
+ const groupMode: Mode = broken ? MODE_BREAK : mode;
242
+ // alloy doesn't use expandedStates — descend into children.
243
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
244
+ if (c.nodeType === COMMENT_NODE) continue;
245
+ cmds.push({ ind: cmd.ind, mode: groupMode, frame: c });
246
+ }
247
+ break;
248
+ }
249
+ case "ifBreak": {
250
+ const data = el.data as
251
+ | { flatNode?: ElementNode; flatText?: string; groupId?: symbol }
252
+ | undefined;
253
+ const groupMode: Mode =
254
+ data?.groupId ? groupModeMap[data.groupId] || MODE_FLAT : mode;
255
+ if (groupMode === MODE_BREAK) {
256
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
257
+ if (c.nodeType === COMMENT_NODE) continue;
258
+ cmds.push({ ind: cmd.ind, mode, frame: c });
259
+ }
260
+ } else {
261
+ if (data?.flatNode !== undefined) {
262
+ cmds.push({ ind: cmd.ind, mode, frame: data.flatNode });
263
+ } else if (data?.flatText !== undefined && data.flatText !== "") {
264
+ cmds.push({ ind: cmd.ind, mode, frame: data.flatText });
265
+ }
266
+ }
267
+ break;
268
+ }
269
+ case "indentIfBreak": {
270
+ const data = el.data as
271
+ | { groupId?: symbol; negate?: boolean }
272
+ | undefined;
273
+ const groupMode: Mode =
274
+ data?.groupId ? groupModeMap[data.groupId] || MODE_FLAT : mode;
275
+ // For width measurement, indent vs no-indent doesn't matter (no
276
+ // newlines emitted in the candidate group while measuring flat,
277
+ // and on hardline we return true). Just descend.
278
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
279
+ if (c.nodeType === COMMENT_NODE) continue;
280
+ cmds.push({ ind: cmd.ind, mode, frame: c });
281
+ }
282
+ // Suppress unused warning in case the linter complains.
283
+ void groupMode;
284
+ break;
285
+ }
286
+ case "fill":
287
+ case "indent":
288
+ case "align":
289
+ case "dedent":
290
+ case "dedentToRoot":
291
+ case "markAsRoot":
292
+ case "label":
293
+ default:
294
+ // Transparent for fits: descend into children.
295
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
296
+ if (c.nodeType === COMMENT_NODE) continue;
297
+ cmds.push({ ind: cmd.ind, mode, frame: c });
298
+ }
299
+ break;
300
+ }
301
+ }
302
+ void dummyInd;
303
+ return false;
304
+ }
305
+
306
+ // #endregion
307
+
308
+ // #region Helpers
309
+
310
+ function isSynth(f: Frame): f is SynthFrame {
311
+ return typeof f === "object" && f !== null && (f as any)[SYNTH] === true;
312
+ }
313
+
314
+ /**
315
+ * Collect the non-comment children of an element into an array
316
+ * (in order). Used for `fill` part indexing — `fill` requires random
317
+ * access on the parts list (offset, length), which a linked list
318
+ * doesn't provide cheaply. Cached per ElementNode to amortize across
319
+ * the multiple fits-measurements `fill` performs.
320
+ */
321
+ const fillPartsCache = new WeakMap<ElementNode, AlloyNode[]>();
322
+
323
+ function collectChildren(el: ElementNode): AlloyNode[] {
324
+ const cached = fillPartsCache.get(el);
325
+ if (cached !== undefined) return cached;
326
+ const out: AlloyNode[] = [];
327
+ for (let c = el.firstChild; c !== null; c = c.nextSibling) {
328
+ if (c.nodeType === COMMENT_NODE) continue;
329
+ out.push(c);
330
+ }
331
+ fillPartsCache.set(el, out);
332
+ return out;
333
+ }
334
+
335
+ /**
336
+ * Expand a TextNode's `data` containing newlines into a flat list of
337
+ * frames matching `convertText` in `to-doc.ts` — segment strings
338
+ * separated by synthetic hardlines. Empty segments are dropped, just
339
+ * like Prettier's `[hardline, "x", hardline, "y"]` (no empty strings).
340
+ */
341
+ function expandMultilineText(text: string): Frame[] {
342
+ const parts = text.split(/\r?\n/);
343
+ const out: Frame[] = [];
344
+ const synthHardline: SynthFrame = { [SYNTH]: true, kind: "line", hard: true };
345
+ for (let i = 0; i < parts.length; i++) {
346
+ if (i > 0) out.push(synthHardline);
347
+ if (parts[i] !== "") out.push(parts[i]);
348
+ }
349
+ return out;
350
+ }
351
+
352
+ // #endregion
353
+
354
+ // #region Main entry point
355
+
356
+ export interface PrintNodeResult {
357
+ formatted: string;
358
+ }
359
+
360
+ export function printNodeToString(
361
+ root: AlloyNode,
362
+ options: PrintNodeOptions,
363
+ ): PrintNodeResult {
364
+ // Group "broken" status is maintained eagerly during render via
365
+ // `propagateBreakDelta` in node.ts (which keeps `el._breakCount`
366
+ // accurate). The `isGroupBroken` helper below reads it in O(1) so we
367
+ // no longer need the per-print `propagateBreaks` walk over the tree.
368
+
369
+ const groupModeMap: Record<symbol, Mode> = {};
370
+ const width = options.printWidth;
371
+ const newLine = "\n";
372
+ let pos = 0;
373
+ const cmds: Cmd[] = [{ ind: rootIndent(), mode: MODE_BREAK, frame: root }];
374
+ const out: (string | symbol)[] = [];
375
+ let shouldRemeasure = false;
376
+ const lineSuffix: Cmd[] = [];
377
+
378
+ while (cmds.length > 0) {
379
+ const cmd = cmds.pop()!;
380
+ const ind = cmd.ind;
381
+ const mode = cmd.mode;
382
+ const frame = cmd.frame;
383
+
384
+ // ---- string ----
385
+ if (typeof frame === "string") {
386
+ out.push(frame);
387
+ if (cmds.length > 0) pos += getStringWidth(frame);
388
+ continue;
389
+ }
390
+
391
+ // ---- synthetic ----
392
+ if (isSynth(frame)) {
393
+ switch (frame.kind) {
394
+ case "fill":
395
+ handleFill(frame.node, frame.offset, ind, mode);
396
+ break;
397
+ case "list": {
398
+ for (let i = frame.items.length - 1; i >= 0; i--) {
399
+ cmds.push({ ind, mode, frame: frame.items[i] });
400
+ }
401
+ break;
402
+ }
403
+ case "line":
404
+ handleLine(!!frame.hard, !!frame.soft, !!frame.literal, ind, mode);
405
+ break;
406
+ case "indent":
407
+ cmds.push({
408
+ ind: makeIndent(ind, options),
409
+ mode,
410
+ frame: {
411
+ [SYNTH]: true,
412
+ kind: "list",
413
+ items: collectChildren(frame.node),
414
+ },
415
+ });
416
+ break;
417
+ }
418
+ // After-pop line-suffix flush check.
419
+ if (cmds.length === 0 && lineSuffix.length > 0) {
420
+ cmds.push(...lineSuffix.reverse());
421
+ lineSuffix.length = 0;
422
+ }
423
+ continue;
424
+ }
425
+
426
+ // ---- AlloyNode ----
427
+ const node = frame as AlloyNode;
428
+ const nt = node.nodeType;
429
+
430
+ if (nt === TEXT_NODE) {
431
+ const tn = node as TextNode;
432
+ const text = tn.data;
433
+ if (text === "") {
434
+ // empty text: nothing
435
+ } else if (!textHasNewline(tn)) {
436
+ out.push(text);
437
+ if (cmds.length > 0) pos += textWidth(tn);
438
+ } else {
439
+ // multiline: expand into segments + synthetic hardlines.
440
+ const items = expandMultilineText(text);
441
+ for (let i = items.length - 1; i >= 0; i--) {
442
+ cmds.push({ ind, mode, frame: items[i] });
443
+ }
444
+ }
445
+ } else if (nt === COMMENT_NODE) {
446
+ // skip
447
+ } else if (nt === FRAGMENT_NODE) {
448
+ for (let c = node.lastChild; c !== null; c = c.previousSibling) {
449
+ if (c.nodeType === COMMENT_NODE) continue;
450
+ cmds.push({ ind, mode, frame: c });
451
+ }
452
+ } else {
453
+ // ELEMENT_NODE
454
+ const el = node as ElementNode;
455
+ handleElement(el, ind, mode);
456
+ }
457
+
458
+ if (cmds.length === 0 && lineSuffix.length > 0) {
459
+ cmds.push(...lineSuffix.reverse());
460
+ lineSuffix.length = 0;
461
+ }
462
+ }
463
+
464
+ return { formatted: joinOut(out) };
465
+
466
+ // #region Closures
467
+
468
+ function pushChildrenReversed(
469
+ el: ElementNode,
470
+ ind: Indent,
471
+ mode: Mode,
472
+ ): void {
473
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
474
+ if (c.nodeType === COMMENT_NODE) continue;
475
+ cmds.push({ ind, mode, frame: c });
476
+ }
477
+ }
478
+
479
+ function handleElement(el: ElementNode, ind: Indent, mode: Mode): void {
480
+ const ln = el.localName;
481
+ switch (ln) {
482
+ case "line":
483
+ case "br":
484
+ handleLine(false, false, false, ind, mode);
485
+ return;
486
+ case "softline":
487
+ case "sbr":
488
+ handleLine(false, true, false, ind, mode);
489
+ return;
490
+ case "hardline":
491
+ case "hbr":
492
+ handleLine(true, false, false, ind, mode);
493
+ return;
494
+ case "literalline":
495
+ case "lbr":
496
+ handleLine(true, false, true, ind, mode);
497
+ return;
498
+ case "breakParent":
499
+ return;
500
+ case "trim":
501
+ pos -= trimOut(out);
502
+ return;
503
+ case "cursor":
504
+ out.push(CURSOR_PLACEHOLDER);
505
+ return;
506
+ case "indent":
507
+ pushChildrenReversed(el, makeIndent(ind, options), mode);
508
+ return;
509
+ case "dedent":
510
+ pushChildrenReversed(el, makeAlign(ind, -1, options), mode);
511
+ return;
512
+ case "dedentToRoot":
513
+ pushChildrenReversed(
514
+ el,
515
+ makeAlign(ind, Number.NEGATIVE_INFINITY, options),
516
+ mode,
517
+ );
518
+ return;
519
+ case "markAsRoot":
520
+ pushChildrenReversed(
521
+ el,
522
+ makeAlign(ind, { type: "root" }, options),
523
+ mode,
524
+ );
525
+ return;
526
+ case "align": {
527
+ const data = el.data as
528
+ | {
529
+ count?: number | string;
530
+ width?: number | string;
531
+ string?: string;
532
+ }
533
+ | undefined;
534
+ const widthOrString = data?.count ?? data?.width ?? data?.string ?? 0;
535
+ pushChildrenReversed(el, makeAlign(ind, widthOrString, options), mode);
536
+ return;
537
+ }
538
+ case "label":
539
+ pushChildrenReversed(el, ind, mode);
540
+ return;
541
+ case "lineSuffix": {
542
+ // Defer contents as a single list frame; capture current ind and mode.
543
+ lineSuffix.push({
544
+ ind,
545
+ mode,
546
+ frame: {
547
+ [SYNTH]: true,
548
+ kind: "list",
549
+ items: collectChildren(el),
550
+ },
551
+ });
552
+ return;
553
+ }
554
+ case "lineSuffixBoundary":
555
+ if (lineSuffix.length > 0) {
556
+ // Synthesize hardlineWithoutBreakParent.
557
+ cmds.push({
558
+ ind,
559
+ mode,
560
+ frame: { [SYNTH]: true, kind: "line", hard: true },
561
+ });
562
+ }
563
+ return;
564
+ case "group":
565
+ handleGroup(el, ind, mode);
566
+ return;
567
+ case "ifBreak":
568
+ handleIfBreak(el, ind, mode);
569
+ return;
570
+ case "indentIfBreak":
571
+ handleIndentIfBreak(el, ind, mode);
572
+ return;
573
+ case "fill":
574
+ handleFillElement(el, ind, mode);
575
+ return;
576
+ default:
577
+ // Unknown: transparent container.
578
+ pushChildrenReversed(el, ind, mode);
579
+ return;
580
+ }
581
+ }
582
+
583
+ function handleGroup(el: ElementNode, ind: Indent, mode: Mode): void {
584
+ const data = el.data as { shouldBreak?: boolean; id?: symbol } | undefined;
585
+ const broken = isGroupBroken(el);
586
+ const groupId = data?.id;
587
+
588
+ switch (mode) {
589
+ case MODE_FLAT: {
590
+ if (!shouldRemeasure) {
591
+ // Stay flat (or step into break if shouldBreak).
592
+ const m: Mode = broken ? MODE_BREAK : MODE_FLAT;
593
+ pushChildrenReversedAtMode(el, ind, m);
594
+ break;
595
+ }
596
+ }
597
+ // fallthrough
598
+ // eslint-disable-next-line no-fallthrough
599
+ case MODE_BREAK: {
600
+ shouldRemeasure = false;
601
+ // Build a "candidate" cmd that pushes the group's children flat.
602
+ // We measure via fits using a synthetic single frame: the
603
+ // element itself with mode=FLAT. But fits descends into children
604
+ // since group dispatches to them. To match prettier semantics
605
+ // exactly (fits treats group as: if mustBeFlat&&broken→false,
606
+ // else descend into contents at groupMode), passing the group
607
+ // itself works.
608
+ const next: Cmd = { ind, mode: MODE_FLAT, frame: el };
609
+ const rem = width - pos;
610
+ const hasLS = lineSuffix.length > 0;
611
+ if (!broken && fits(next, cmds, rem, hasLS, groupModeMap, false)) {
612
+ // Push children flat.
613
+ pushChildrenReversedAtMode(el, ind, MODE_FLAT);
614
+ } else {
615
+ // Push children broken.
616
+ pushChildrenReversedAtMode(el, ind, MODE_BREAK);
617
+ }
618
+ break;
619
+ }
620
+ }
621
+ if (groupId) {
622
+ groupModeMap[groupId] =
623
+ cmds.length > 0 ? cmds[cmds.length - 1].mode : mode;
624
+ }
625
+ }
626
+
627
+ function pushChildrenReversedAtMode(
628
+ el: ElementNode,
629
+ ind: Indent,
630
+ mode: Mode,
631
+ ): void {
632
+ for (let c = el.lastChild; c !== null; c = c.previousSibling) {
633
+ if (c.nodeType === COMMENT_NODE) continue;
634
+ cmds.push({ ind, mode, frame: c });
635
+ }
636
+ }
637
+
638
+ function handleIfBreak(el: ElementNode, ind: Indent, mode: Mode): void {
639
+ const data = el.data as
640
+ | { flatNode?: ElementNode; flatText?: string; groupId?: symbol }
641
+ | undefined;
642
+ const groupMode: Mode | undefined =
643
+ data?.groupId ? groupModeMap[data.groupId] : mode;
644
+ if (groupMode === MODE_BREAK) {
645
+ // Push children (the breakContents).
646
+ pushChildrenReversed(el, ind, mode);
647
+ } else if (groupMode === MODE_FLAT) {
648
+ if (data?.flatNode !== undefined) {
649
+ cmds.push({ ind, mode, frame: data.flatNode });
650
+ } else if (data?.flatText !== undefined && data.flatText !== "") {
651
+ cmds.push({ ind, mode, frame: data.flatText });
652
+ }
653
+ }
654
+ // groupMode === undefined: emit nothing (matches prettier).
655
+ }
656
+
657
+ function handleIndentIfBreak(el: ElementNode, ind: Indent, mode: Mode): void {
658
+ const data = el.data as { groupId?: symbol; negate?: boolean } | undefined;
659
+ const groupMode: Mode | undefined =
660
+ data?.groupId ? groupModeMap[data.groupId] : mode;
661
+ const negate = !!data?.negate;
662
+ if (groupMode === MODE_BREAK) {
663
+ if (negate) {
664
+ // breakContents = original children (no indent).
665
+ pushChildrenReversed(el, ind, mode);
666
+ } else {
667
+ // breakContents = indent(children).
668
+ pushChildrenReversed(el, makeIndent(ind, options), mode);
669
+ }
670
+ } else if (groupMode === MODE_FLAT) {
671
+ if (negate) {
672
+ // flatContents = indent(children).
673
+ pushChildrenReversed(el, makeIndent(ind, options), mode);
674
+ } else {
675
+ // flatContents = original children.
676
+ pushChildrenReversed(el, ind, mode);
677
+ }
678
+ }
679
+ }
680
+
681
+ function handleFillElement(el: ElementNode, ind: Indent, mode: Mode): void {
682
+ handleFill(el, 0, ind, mode);
683
+ }
684
+
685
+ function handleFill(
686
+ el: ElementNode,
687
+ offset: number,
688
+ indArg?: Indent,
689
+ modeArg?: Mode,
690
+ ): void {
691
+ const ind = indArg ?? rootIndent();
692
+ const mode = modeArg ?? MODE_BREAK;
693
+ const parts = collectChildren(el);
694
+ const rem = width - pos;
695
+ const length = parts.length - offset;
696
+ if (length === 0) return;
697
+ const content = parts[offset + 0];
698
+ const whitespace = parts[offset + 1];
699
+ const contentFlatCmd: Cmd = { ind, mode: MODE_FLAT, frame: content };
700
+ const contentBreakCmd: Cmd = { ind, mode: MODE_BREAK, frame: content };
701
+ const contentFits = fits(
702
+ contentFlatCmd,
703
+ [],
704
+ rem,
705
+ lineSuffix.length > 0,
706
+ groupModeMap,
707
+ true,
708
+ );
709
+ if (length === 1) {
710
+ if (contentFits) cmds.push(contentFlatCmd);
711
+ else cmds.push(contentBreakCmd);
712
+ return;
713
+ }
714
+ const whitespaceFlatCmd: Cmd = {
715
+ ind,
716
+ mode: MODE_FLAT,
717
+ frame: whitespace,
718
+ };
719
+ const whitespaceBreakCmd: Cmd = {
720
+ ind,
721
+ mode: MODE_BREAK,
722
+ frame: whitespace,
723
+ };
724
+ if (length === 2) {
725
+ if (contentFits) cmds.push(whitespaceFlatCmd, contentFlatCmd);
726
+ else cmds.push(whitespaceBreakCmd, contentBreakCmd);
727
+ return;
728
+ }
729
+ const secondContent = parts[offset + 2];
730
+ const remainingCmd: Cmd = {
731
+ ind,
732
+ mode,
733
+ frame: { [SYNTH]: true, kind: "fill", node: el, offset: offset + 2 },
734
+ };
735
+ const firstAndSecondListFrame: SynthFrame = {
736
+ [SYNTH]: true,
737
+ kind: "list",
738
+ items: [content, whitespace, secondContent],
739
+ };
740
+ const firstAndSecondContentFits = fits(
741
+ { ind, mode: MODE_FLAT, frame: firstAndSecondListFrame },
742
+ [],
743
+ rem,
744
+ lineSuffix.length > 0,
745
+ groupModeMap,
746
+ true,
747
+ );
748
+ if (firstAndSecondContentFits) {
749
+ cmds.push(remainingCmd, whitespaceFlatCmd, contentFlatCmd);
750
+ } else if (contentFits) {
751
+ cmds.push(remainingCmd, whitespaceBreakCmd, contentFlatCmd);
752
+ } else {
753
+ cmds.push(remainingCmd, whitespaceBreakCmd, contentBreakCmd);
754
+ }
755
+ }
756
+
757
+ function handleLine(
758
+ hard: boolean,
759
+ soft: boolean,
760
+ literal: boolean,
761
+ ind: Indent,
762
+ mode: Mode,
763
+ ): void {
764
+ switch (mode) {
765
+ case MODE_FLAT:
766
+ if (!hard) {
767
+ if (!soft) {
768
+ out.push(" ");
769
+ pos += 1;
770
+ }
771
+ break;
772
+ } else {
773
+ shouldRemeasure = true;
774
+ }
775
+ // fallthrough
776
+ // eslint-disable-next-line no-fallthrough
777
+ case MODE_BREAK:
778
+ if (lineSuffix.length > 0) {
779
+ // Re-push the line as a synthetic frame and then the suffix.
780
+ cmds.push({
781
+ ind,
782
+ mode,
783
+ frame: {
784
+ [SYNTH]: true,
785
+ kind: "line",
786
+ hard,
787
+ soft,
788
+ literal,
789
+ },
790
+ });
791
+ for (let i = lineSuffix.length - 1; i >= 0; i--) {
792
+ cmds.push(lineSuffix[i]);
793
+ }
794
+ lineSuffix.length = 0;
795
+ break;
796
+ }
797
+ if (literal) {
798
+ if (ind.root) {
799
+ out.push(newLine, ind.root.value);
800
+ pos = ind.root.length;
801
+ } else {
802
+ out.push(newLine);
803
+ pos = 0;
804
+ }
805
+ } else {
806
+ pos -= trimOut(out);
807
+ out.push(newLine + ind.value);
808
+ pos = ind.length;
809
+ }
810
+ break;
811
+ }
812
+ }
813
+
814
+ // #endregion
815
+ }
816
+
817
+ // #endregion