@scaffold-ui-kit/preview 0.0.0-stage → 2.3.0

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 (350) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1868 -2
  3. package/application/host/bootstrap.ts +43 -0
  4. package/application/host/main.ts +31 -0
  5. package/application/host/shell-host.ts +47 -0
  6. package/application/index.html +12 -0
  7. package/application/lib/base-url.ts +15 -0
  8. package/application/lib/no-code-block.ts +7 -0
  9. package/application/lib/no-setup.ts +3 -0
  10. package/application/lib/no-views.ts +6 -0
  11. package/application/preview/code/code.html +12 -0
  12. package/application/preview/code/main.ts +89 -0
  13. package/application/preview/globals.ts +254 -0
  14. package/application/preview/html/main.ts +93 -0
  15. package/application/preview/html/preview.html +12 -0
  16. package/application/preview/react/main.tsx +146 -0
  17. package/application/preview/react/preview.html +12 -0
  18. package/application/preview/svelte/Preview.svelte +104 -0
  19. package/application/preview/svelte/main.ts +13 -0
  20. package/application/preview/svelte/preview.html +12 -0
  21. package/application/preview/svelte/story-props.ts +172 -0
  22. package/application/preview/view/main.ts +124 -0
  23. package/application/preview/view/view.html +12 -0
  24. package/application/preview/vue/main.ts +145 -0
  25. package/application/preview/vue/preview.html +12 -0
  26. package/application/shells/charcell/components/Application/index.css +37 -0
  27. package/application/shells/charcell/components/Application/index.tsx +65 -0
  28. package/application/shells/charcell/components/Bar/index.css +68 -0
  29. package/application/shells/charcell/components/Bar/index.tsx +60 -0
  30. package/application/shells/charcell/components/CodePane/index.css +60 -0
  31. package/application/shells/charcell/components/CodePane/index.tsx +113 -0
  32. package/application/shells/charcell/components/ComponentStage/index.css +26 -0
  33. package/application/shells/charcell/components/ComponentStage/index.tsx +110 -0
  34. package/application/shells/charcell/components/Control/index.css +20 -0
  35. package/application/shells/charcell/components/Control/index.tsx +118 -0
  36. package/application/shells/charcell/components/CopyLink/index.tsx +35 -0
  37. package/application/shells/charcell/components/Description/index.css +6 -0
  38. package/application/shells/charcell/components/Description/index.tsx +24 -0
  39. package/application/shells/charcell/components/EditorAddDialog/index.css +9 -0
  40. package/application/shells/charcell/components/EditorAddDialog/index.tsx +158 -0
  41. package/application/shells/charcell/components/EditorPanel/index.css +48 -0
  42. package/application/shells/charcell/components/EditorPanel/index.tsx +496 -0
  43. package/application/shells/charcell/components/EventsRow/index.css +42 -0
  44. package/application/shells/charcell/components/EventsRow/index.tsx +68 -0
  45. package/application/shells/charcell/components/HeadingRow/index.css +11 -0
  46. package/application/shells/charcell/components/HeadingRow/index.tsx +16 -0
  47. package/application/shells/charcell/components/Hint/index.css +16 -0
  48. package/application/shells/charcell/components/Hint/index.tsx +81 -0
  49. package/application/shells/charcell/components/HtmlWarnings/index.tsx +45 -0
  50. package/application/shells/charcell/components/KeyHints/index.css +27 -0
  51. package/application/shells/charcell/components/KeyHints/index.tsx +106 -0
  52. package/application/shells/charcell/components/KitCodeBlock/index.css +55 -0
  53. package/application/shells/charcell/components/KitCodeBlock/index.tsx +18 -0
  54. package/application/shells/charcell/components/Markdown/index.tsx +16 -0
  55. package/application/shells/charcell/components/Navigation/index.css +13 -0
  56. package/application/shells/charcell/components/Navigation/index.tsx +98 -0
  57. package/application/shells/charcell/components/NavigationSplit/index.css +16 -0
  58. package/application/shells/charcell/components/NavigationSplit/index.tsx +82 -0
  59. package/application/shells/charcell/components/OptionControls/index.tsx +93 -0
  60. package/application/shells/charcell/components/PaneStatus/index.css +7 -0
  61. package/application/shells/charcell/components/PaneStatus/index.tsx +47 -0
  62. package/application/shells/charcell/components/PaneWidth/index.tsx +45 -0
  63. package/application/shells/charcell/components/PreviewGrid/index.css +39 -0
  64. package/application/shells/charcell/components/PreviewGrid/index.tsx +344 -0
  65. package/application/shells/charcell/components/PreviewPane/index.css +127 -0
  66. package/application/shells/charcell/components/PreviewPane/index.tsx +182 -0
  67. package/application/shells/charcell/components/PromptBar/index.tsx +133 -0
  68. package/application/shells/charcell/components/ScenarioRow/index.css +36 -0
  69. package/application/shells/charcell/components/ScenarioRow/index.tsx +71 -0
  70. package/application/shells/charcell/components/Select/index.tsx +48 -0
  71. package/application/shells/charcell/components/SlotOutlet/index.css +7 -0
  72. package/application/shells/charcell/components/SlotOutlet/index.tsx +25 -0
  73. package/application/shells/charcell/components/StageBar/index.css +7 -0
  74. package/application/shells/charcell/components/StageBar/index.tsx +109 -0
  75. package/application/shells/charcell/components/StageSplit/index.css +11 -0
  76. package/application/shells/charcell/components/StageSplit/index.tsx +99 -0
  77. package/application/shells/charcell/components/StatusBar/index.tsx +30 -0
  78. package/application/shells/charcell/components/ViewControls/index.css +15 -0
  79. package/application/shells/charcell/components/ViewControls/index.tsx +58 -0
  80. package/application/shells/charcell/components/ViewHeading/index.css +13 -0
  81. package/application/shells/charcell/components/ViewHeading/index.tsx +8 -0
  82. package/application/shells/charcell/components/ViewStage/index.css +9 -0
  83. package/application/shells/charcell/components/ViewStage/index.tsx +25 -0
  84. package/application/shells/charcell/components/WrapToggle/index.tsx +27 -0
  85. package/application/shells/charcell/configuration.ts +45 -0
  86. package/application/shells/charcell/controller-context.ts +27 -0
  87. package/application/shells/charcell/fit-labels.ts +42 -0
  88. package/application/shells/charcell/html-warnings.ts +18 -0
  89. package/application/shells/charcell/index.tsx +82 -0
  90. package/application/shells/charcell/page-loading.html +57 -0
  91. package/application/shells/charcell/placement.ts +24 -0
  92. package/application/shells/charcell/shell-options.ts +23 -0
  93. package/application/shells/charcell/shell.css +37 -0
  94. package/application/shells/charcell/shell.tsx +25 -0
  95. package/application/shells/charcell/split-reset.ts +47 -0
  96. package/application/shells/charcell/stacked-rows.ts +117 -0
  97. package/application/shells/charcell/stage-toggles.ts +93 -0
  98. package/application/types/manifest.d.ts +6 -0
  99. package/application/types/preview-configuration.d.ts +10 -0
  100. package/application/types/preview-shell.d.ts +7 -0
  101. package/application/types/react-registry.d.ts +15 -0
  102. package/application/types/vue-registry.d.ts +6 -0
  103. package/dist/build.d.ts +24 -0
  104. package/dist/build.js +34 -0
  105. package/dist/check.d.ts +48 -0
  106. package/dist/check.js +781 -0
  107. package/dist/cli.d.ts +2 -0
  108. package/dist/cli.js +88 -0
  109. package/dist/context.d.ts +179 -0
  110. package/dist/context.js +783 -0
  111. package/dist/controller/catalog.d.ts +339 -0
  112. package/dist/controller/catalog.js +367 -0
  113. package/dist/controller/create-preview-controller.d.ts +532 -0
  114. package/dist/controller/create-preview-controller.js +1707 -0
  115. package/dist/controller/editor-tree.d.ts +161 -0
  116. package/dist/controller/editor-tree.js +373 -0
  117. package/dist/controller/html-source.d.ts +62 -0
  118. package/dist/controller/html-source.js +61 -0
  119. package/dist/controller/index.d.ts +9 -0
  120. package/dist/controller/index.js +12 -0
  121. package/dist/controller/persistence.d.ts +191 -0
  122. package/dist/controller/persistence.js +315 -0
  123. package/dist/controller/regeneration.d.ts +30 -0
  124. package/dist/controller/regeneration.js +27 -0
  125. package/dist/controller/source.d.ts +80 -0
  126. package/dist/controller/source.js +236 -0
  127. package/dist/controller/store.d.ts +27 -0
  128. package/dist/controller/store.js +40 -0
  129. package/dist/declarations/application/shells/charcell/components/Application/index.d.ts +2 -0
  130. package/dist/declarations/application/shells/charcell/components/Bar/index.d.ts +11 -0
  131. package/dist/declarations/application/shells/charcell/components/CodePane/index.d.ts +5 -0
  132. package/dist/declarations/application/shells/charcell/components/ComponentStage/index.d.ts +2 -0
  133. package/dist/declarations/application/shells/charcell/components/Control/index.d.ts +9 -0
  134. package/dist/declarations/application/shells/charcell/components/CopyLink/index.d.ts +1 -0
  135. package/dist/declarations/application/shells/charcell/components/Description/index.d.ts +5 -0
  136. package/dist/declarations/application/shells/charcell/components/EditorAddDialog/index.d.ts +6 -0
  137. package/dist/declarations/application/shells/charcell/components/EditorPanel/index.d.ts +2 -0
  138. package/dist/declarations/application/shells/charcell/components/EventsRow/index.d.ts +4 -0
  139. package/dist/declarations/application/shells/charcell/components/HeadingRow/index.d.ts +6 -0
  140. package/dist/declarations/application/shells/charcell/components/Hint/index.d.ts +11 -0
  141. package/dist/declarations/application/shells/charcell/components/HtmlWarnings/index.d.ts +5 -0
  142. package/dist/declarations/application/shells/charcell/components/KeyHints/index.d.ts +2 -0
  143. package/dist/declarations/application/shells/charcell/components/KitCodeBlock/index.d.ts +5 -0
  144. package/dist/declarations/application/shells/charcell/components/Markdown/index.d.ts +3 -0
  145. package/dist/declarations/application/shells/charcell/components/Navigation/index.d.ts +8 -0
  146. package/dist/declarations/application/shells/charcell/components/NavigationSplit/index.d.ts +7 -0
  147. package/dist/declarations/application/shells/charcell/components/OptionControls/index.d.ts +7 -0
  148. package/dist/declarations/application/shells/charcell/components/PaneStatus/index.d.ts +2 -0
  149. package/dist/declarations/application/shells/charcell/components/PaneWidth/index.d.ts +4 -0
  150. package/dist/declarations/application/shells/charcell/components/PreviewGrid/index.d.ts +7 -0
  151. package/dist/declarations/application/shells/charcell/components/PreviewPane/index.d.ts +9 -0
  152. package/dist/declarations/application/shells/charcell/components/PromptBar/index.d.ts +1 -0
  153. package/dist/declarations/application/shells/charcell/components/ScenarioRow/index.d.ts +2 -0
  154. package/dist/declarations/application/shells/charcell/components/Select/index.d.ts +16 -0
  155. package/dist/declarations/application/shells/charcell/components/SlotOutlet/index.d.ts +5 -0
  156. package/dist/declarations/application/shells/charcell/components/StageBar/index.d.ts +2 -0
  157. package/dist/declarations/application/shells/charcell/components/StageSplit/index.d.ts +17 -0
  158. package/dist/declarations/application/shells/charcell/components/StatusBar/index.d.ts +1 -0
  159. package/dist/declarations/application/shells/charcell/components/ViewControls/index.d.ts +9 -0
  160. package/dist/declarations/application/shells/charcell/components/ViewHeading/index.d.ts +4 -0
  161. package/dist/declarations/application/shells/charcell/components/ViewStage/index.d.ts +4 -0
  162. package/dist/declarations/application/shells/charcell/components/WrapToggle/index.d.ts +1 -0
  163. package/dist/declarations/application/shells/charcell/configuration.d.ts +31 -0
  164. package/dist/declarations/application/shells/charcell/controller-context.d.ts +9 -0
  165. package/dist/declarations/application/shells/charcell/fit-labels.d.ts +18 -0
  166. package/dist/declarations/application/shells/charcell/html-warnings.d.ts +5 -0
  167. package/dist/declarations/application/shells/charcell/index.d.ts +39 -0
  168. package/dist/declarations/application/shells/charcell/placement.d.ts +7 -0
  169. package/dist/declarations/application/shells/charcell/shell-options.d.ts +15 -0
  170. package/dist/declarations/application/shells/charcell/shell.d.ts +9 -0
  171. package/dist/declarations/application/shells/charcell/split-reset.d.ts +30 -0
  172. package/dist/declarations/application/shells/charcell/stacked-rows.d.ts +58 -0
  173. package/dist/declarations/application/shells/charcell/stage-toggles.d.ts +28 -0
  174. package/dist/declarations/src/controller/catalog.d.ts +339 -0
  175. package/dist/declarations/src/controller/create-preview-controller.d.ts +532 -0
  176. package/dist/declarations/src/controller/editor-tree.d.ts +161 -0
  177. package/dist/declarations/src/controller/html-source.d.ts +62 -0
  178. package/dist/declarations/src/controller/index.d.ts +9 -0
  179. package/dist/declarations/src/controller/persistence.d.ts +191 -0
  180. package/dist/declarations/src/controller/regeneration.d.ts +30 -0
  181. package/dist/declarations/src/controller/source.d.ts +80 -0
  182. package/dist/declarations/src/controller/store.d.ts +27 -0
  183. package/dist/declarations/src/emit/common.d.ts +103 -0
  184. package/dist/declarations/src/emit/manifest.d.ts +138 -0
  185. package/dist/declarations/src/emit/source.d.ts +87 -0
  186. package/dist/declarations/src/lib/jsx-roots.d.ts +22 -0
  187. package/dist/declarations/src/lib/module-of-source.d.ts +18 -0
  188. package/dist/declarations/src/lib/naming.d.ts +29 -0
  189. package/dist/declarations/src/lib/react-spelling.d.ts +68 -0
  190. package/dist/declarations/src/lib/scenario-story.d.ts +41 -0
  191. package/dist/declarations/src/lib/slot-fill.d.ts +15 -0
  192. package/dist/declarations/src/lib/types.d.ts +741 -0
  193. package/dist/declarations/src/options.d.ts +224 -0
  194. package/dist/declarations/src/protocol.d.ts +541 -0
  195. package/dist/declarations/src/react-view.d.ts +22 -0
  196. package/dist/declarations/src/react.d.ts +17 -0
  197. package/dist/declarations/src/shell.d.ts +99 -0
  198. package/dist/declarations/src/views.d.ts +152 -0
  199. package/dist/development-server.d.ts +121 -0
  200. package/dist/development-server.js +331 -0
  201. package/dist/emit/common.d.ts +103 -0
  202. package/dist/emit/common.js +170 -0
  203. package/dist/emit/html-source.d.ts +46 -0
  204. package/dist/emit/html-source.js +322 -0
  205. package/dist/emit/manifest.d.ts +138 -0
  206. package/dist/emit/manifest.js +74 -0
  207. package/dist/emit/react.d.ts +16 -0
  208. package/dist/emit/react.js +210 -0
  209. package/dist/emit/scratch.d.ts +40 -0
  210. package/dist/emit/scratch.js +116 -0
  211. package/dist/emit/source.d.ts +87 -0
  212. package/dist/emit/source.js +789 -0
  213. package/dist/emit/svelte.d.ts +9 -0
  214. package/dist/emit/svelte.js +177 -0
  215. package/dist/emit/vue.d.ts +2 -0
  216. package/dist/emit/vue.js +152 -0
  217. package/dist/enumerate.d.ts +2 -0
  218. package/dist/enumerate.js +162 -0
  219. package/dist/generate.d.ts +43 -0
  220. package/dist/generate.js +302 -0
  221. package/dist/html-stylesheet.d.ts +43 -0
  222. package/dist/html-stylesheet.js +145 -0
  223. package/dist/index.d.ts +18 -0
  224. package/dist/index.js +21 -0
  225. package/dist/landing.d.ts +16 -0
  226. package/dist/landing.js +45 -0
  227. package/dist/lib/exported-types.d.ts +42 -0
  228. package/dist/lib/exported-types.js +86 -0
  229. package/dist/lib/html-attributes.d.ts +2 -0
  230. package/dist/lib/html-attributes.js +111 -0
  231. package/dist/lib/html-warnings.d.ts +19 -0
  232. package/dist/lib/html-warnings.js +79 -0
  233. package/dist/lib/jsx-roots.d.ts +22 -0
  234. package/dist/lib/jsx-roots.js +243 -0
  235. package/dist/lib/module-of-source.d.ts +18 -0
  236. package/dist/lib/module-of-source.js +24 -0
  237. package/dist/lib/naming.d.ts +29 -0
  238. package/dist/lib/naming.js +56 -0
  239. package/dist/lib/parse-type.d.ts +32 -0
  240. package/dist/lib/parse-type.js +103 -0
  241. package/dist/lib/react-spelling.d.ts +68 -0
  242. package/dist/lib/react-spelling.js +197 -0
  243. package/dist/lib/scenario-story.d.ts +41 -0
  244. package/dist/lib/scenario-story.js +84 -0
  245. package/dist/lib/slot-fill.d.ts +15 -0
  246. package/dist/lib/slot-fill.js +92 -0
  247. package/dist/lib/template-files.d.ts +14 -0
  248. package/dist/lib/template-files.js +25 -0
  249. package/dist/lib/types.d.ts +741 -0
  250. package/dist/lib/types.js +13 -0
  251. package/dist/lib/void-elements.d.ts +7 -0
  252. package/dist/lib/void-elements.js +13 -0
  253. package/dist/load.d.ts +37 -0
  254. package/dist/load.js +82 -0
  255. package/dist/model.d.ts +44 -0
  256. package/dist/model.js +464 -0
  257. package/dist/options.d.ts +224 -0
  258. package/dist/options.js +223 -0
  259. package/dist/prop-types.d.ts +26 -0
  260. package/dist/prop-types.js +102 -0
  261. package/dist/props-from.d.ts +84 -0
  262. package/dist/props-from.js +510 -0
  263. package/dist/protocol.d.ts +541 -0
  264. package/dist/protocol.js +819 -0
  265. package/dist/react-view.d.ts +22 -0
  266. package/dist/react-view.js +36 -0
  267. package/dist/react.d.ts +17 -0
  268. package/dist/react.js +23 -0
  269. package/dist/sass-importers.d.ts +26 -0
  270. package/dist/sass-importers.js +32 -0
  271. package/dist/shell-acceptance.d.ts +21 -0
  272. package/dist/shell-acceptance.js +37 -0
  273. package/dist/shell-page.d.ts +36 -0
  274. package/dist/shell-page.js +90 -0
  275. package/dist/shell-peers.d.ts +38 -0
  276. package/dist/shell-peers.js +61 -0
  277. package/dist/shell.d.ts +99 -0
  278. package/dist/shell.js +52 -0
  279. package/dist/shells/charcell/page.d.ts +8 -0
  280. package/dist/shells/charcell/page.js +69 -0
  281. package/dist/story-source.d.ts +113 -0
  282. package/dist/story-source.js +271 -0
  283. package/dist/story.d.ts +5 -0
  284. package/dist/story.js +28 -0
  285. package/dist/testing.d.ts +218 -0
  286. package/dist/testing.js +284 -0
  287. package/dist/views.d.ts +152 -0
  288. package/dist/views.js +7 -0
  289. package/dist/vite-configuration.d.ts +114 -0
  290. package/dist/vite-configuration.js +578 -0
  291. package/package.json +123 -3
  292. package/src/build.ts +51 -0
  293. package/src/check.ts +1062 -0
  294. package/src/cli.ts +123 -0
  295. package/src/context.ts +1168 -0
  296. package/src/controller/catalog.ts +719 -0
  297. package/src/controller/create-preview-controller.ts +2683 -0
  298. package/src/controller/editor-tree.ts +530 -0
  299. package/src/controller/html-source.ts +114 -0
  300. package/src/controller/index.ts +105 -0
  301. package/src/controller/persistence.ts +403 -0
  302. package/src/controller/regeneration.ts +55 -0
  303. package/src/controller/source.ts +333 -0
  304. package/src/controller/store.ts +55 -0
  305. package/src/development-server.ts +442 -0
  306. package/src/emit/common.ts +201 -0
  307. package/src/emit/html-source.ts +402 -0
  308. package/src/emit/manifest.ts +241 -0
  309. package/src/emit/react.ts +263 -0
  310. package/src/emit/scratch.ts +175 -0
  311. package/src/emit/source.ts +1094 -0
  312. package/src/emit/svelte.ts +230 -0
  313. package/src/emit/vue.ts +194 -0
  314. package/src/enumerate.ts +220 -0
  315. package/src/generate.ts +443 -0
  316. package/src/html-stylesheet.ts +173 -0
  317. package/src/index.ts +60 -0
  318. package/src/landing.ts +58 -0
  319. package/src/lib/exported-types.ts +114 -0
  320. package/src/lib/html-attributes.ts +113 -0
  321. package/src/lib/html-warnings.ts +81 -0
  322. package/src/lib/jsx-roots.ts +238 -0
  323. package/src/lib/module-of-source.ts +26 -0
  324. package/src/lib/naming.ts +65 -0
  325. package/src/lib/parse-type.ts +143 -0
  326. package/src/lib/react-spelling.ts +235 -0
  327. package/src/lib/scenario-story.ts +97 -0
  328. package/src/lib/slot-fill.ts +102 -0
  329. package/src/lib/template-files.ts +31 -0
  330. package/src/lib/types.ts +778 -0
  331. package/src/lib/void-elements.ts +15 -0
  332. package/src/load.ts +144 -0
  333. package/src/model.ts +568 -0
  334. package/src/options.ts +435 -0
  335. package/src/prop-types.ts +133 -0
  336. package/src/props-from.ts +678 -0
  337. package/src/protocol.ts +1135 -0
  338. package/src/react-view.ts +45 -0
  339. package/src/react.ts +34 -0
  340. package/src/sass-importers.ts +45 -0
  341. package/src/shell-acceptance.ts +45 -0
  342. package/src/shell-page.ts +133 -0
  343. package/src/shell-peers.ts +70 -0
  344. package/src/shell.ts +194 -0
  345. package/src/shells/charcell/page.ts +82 -0
  346. package/src/story-source.ts +391 -0
  347. package/src/story.ts +42 -0
  348. package/src/testing.ts +497 -0
  349. package/src/views.ts +183 -0
  350. package/src/vite-configuration.ts +675 -0
@@ -0,0 +1,678 @@
1
+ // Read a manual component's props from its React driver's props interface, so the
2
+ // preview cannot drift from the code. The read is syntactic - source files parsed,
3
+ // no program or type checker - so it costs milliseconds per component. It takes
4
+ // the own members of the interface or object type, or of each operand of an
5
+ // intersection that is one, resolving type aliases and interfaces declared in the
6
+ // same file or imported from another (a relative path, a tsconfig path, or the
7
+ // kit's own package name), re-exports included. It also reads a few type forms a
8
+ // driver uses to build on a template component's props:
9
+ //
10
+ // - `ComponentProps<typeof X>` (or `ComponentPropsWithRef`,
11
+ // `ComponentPropsWithoutRef`), where `X` names one of the kit's template
12
+ // components: that template's props, and `children` when it has a default slot;
13
+ // - `Omit<T, 'a' | 'b'>` over a readable `T`: `T`'s members without those names;
14
+ // - a generic type alias given arguments: its body with the arguments in place;
15
+ // - a conditional type that always holds (`T extends unknown ? A : never`, the
16
+ // idiom that applies `A` to each member of a union): its true branch.
17
+ //
18
+ // Whatever stays opaque (a function, an object, a utility type over a native
19
+ // element) becomes a hidden prop the check lists, or contributes nothing.
20
+
21
+ import { existsSync, readFileSync } from 'node:fs';
22
+ import { dirname, resolve } from 'node:path';
23
+ import ts from 'typescript';
24
+ import { parseType } from './lib/parse-type.js';
25
+ import type {
26
+ EnumeratedComponent,
27
+ ManualComponentSpecification,
28
+ } from './lib/types.js';
29
+
30
+ /** The props read from one `propsFrom` interface. */
31
+ export interface PropsFromResult {
32
+ props: EnumeratedComponent['props'];
33
+ /** Whether the interface declares a non-function `children` (the default slot;
34
+ * a render-function `children` is a scoped slot instead). */
35
+ hasChildren: boolean;
36
+ /** Whether that default slot exposes values to its content (a scoped
37
+ * slot): a template component's whose default slot does, read through
38
+ * `ComponentProps<typeof X>`. */
39
+ childrenScoped: boolean;
40
+ /** Members whose type is not a control. */
41
+ hiddenProps: string[];
42
+ /** Every own member name, `children` included. */
43
+ memberNames: string[];
44
+ }
45
+
46
+ /** Splits `'<file>#<InterfaceName>'`, failing on any other shape. */
47
+ function parseReference(reference: string): { file: string; name: string } {
48
+ const [file, name, ...rest] = reference.split('#');
49
+ if (!file || !name || rest.length > 0) {
50
+ throw new Error(
51
+ `propsFrom '${reference}' must be '<file relative to the kit root>#<InterfaceName>'`
52
+ );
53
+ }
54
+ return { file, name };
55
+ }
56
+
57
+ /** A type declaration found by name: an interface or a type alias, in its file. */
58
+ type Declaration =
59
+ | { kind: 'interface'; node: ts.InterfaceDeclaration; file: ts.SourceFile }
60
+ | { kind: 'alias'; node: ts.TypeAliasDeclaration; file: ts.SourceFile };
61
+
62
+ /** A member with the file it was declared in, for reading its type's text; or
63
+ * a prop a template component declares, already in preview form. */
64
+ type LocatedMember =
65
+ | { kind: 'member'; member: ts.TypeElement; file: ts.SourceFile }
66
+ | {
67
+ kind: 'template';
68
+ name: string;
69
+ definition: EnumeratedComponent['props'][string];
70
+ }
71
+ | { kind: 'template-children'; scoped: boolean };
72
+
73
+ /** A type argument bound to a generic alias's parameter, with the file and the
74
+ * bindings it is read in. */
75
+ interface BoundArgument {
76
+ node: ts.TypeNode;
77
+ file: ts.SourceFile;
78
+ bindings: ReadonlyMap<string, BoundArgument>;
79
+ }
80
+
81
+ /** What a type is read against: the declarations, the template components a
82
+ * `ComponentProps<typeof X>` can name, the generic bindings in scope, and the
83
+ * declarations already on this path (a guard against a cycle). */
84
+ interface ReadContext {
85
+ resolver: DeclarationResolver;
86
+ templates: readonly EnumeratedComponent[];
87
+ bindings: ReadonlyMap<string, BoundArgument>;
88
+ seen: ReadonlySet<string>;
89
+ }
90
+
91
+ /** The React utility types that take a component's props from `typeof X`. */
92
+ const COMPONENT_PROPS_TYPES = new Set([
93
+ 'ComponentProps',
94
+ 'ComponentPropsWithRef',
95
+ 'ComponentPropsWithoutRef',
96
+ ]);
97
+
98
+ /** Finds type declarations by name across a kit's source files, following
99
+ * imports and re-exports with TypeScript's module resolution and the kit's
100
+ * tsconfig. Parses each file once. */
101
+ export class DeclarationResolver {
102
+ private readonly files = new Map<string, ts.SourceFile>();
103
+ private readonly options: ts.CompilerOptions;
104
+
105
+ constructor(kitRoot: string) {
106
+ const configurationPath = ts.findConfigFile(kitRoot, ts.sys.fileExists);
107
+ const parsed = configurationPath
108
+ ? ts.parseJsonConfigFileContent(
109
+ ts.readConfigFile(configurationPath, ts.sys.readFile).config,
110
+ ts.sys,
111
+ dirname(configurationPath)
112
+ )
113
+ : undefined;
114
+ this.options = {
115
+ ...(parsed?.options ?? {}),
116
+ // Bundler resolution reads package `exports`, so a kit importing its own
117
+ // package by name resolves to its source.
118
+ moduleResolution:
119
+ parsed?.options.moduleResolution ?? ts.ModuleResolutionKind.Bundler,
120
+ module: parsed?.options.module ?? ts.ModuleKind.ESNext,
121
+ };
122
+ }
123
+
124
+ /** Parses source that is not on disk, as if it were the file at `path`, so
125
+ * its imports resolve from there. Not cached. */
126
+ virtual(path: string, text: string): ts.SourceFile {
127
+ return ts.createSourceFile(
128
+ path,
129
+ text,
130
+ ts.ScriptTarget.Latest,
131
+ true,
132
+ ts.ScriptKind.TS
133
+ );
134
+ }
135
+
136
+ file(path: string): ts.SourceFile {
137
+ let file = this.files.get(path);
138
+ if (!file) {
139
+ file = ts.createSourceFile(
140
+ path,
141
+ readFileSync(path, 'utf8'),
142
+ ts.ScriptTarget.Latest,
143
+ true,
144
+ ts.ScriptKind.TSX
145
+ );
146
+ this.files.set(path, file);
147
+ }
148
+ return file;
149
+ }
150
+
151
+ private resolveModule(
152
+ specifier: string,
153
+ from: ts.SourceFile
154
+ ): ts.SourceFile | undefined {
155
+ const resolved = ts.resolveModuleName(
156
+ specifier,
157
+ from.fileName,
158
+ this.options,
159
+ ts.sys
160
+ ).resolvedModule;
161
+ return resolved && existsSync(resolved.resolvedFileName)
162
+ ? this.file(resolved.resolvedFileName)
163
+ : undefined;
164
+ }
165
+
166
+ /** The declaration `name` refers to inside `file`: declared there, or imported. */
167
+ find(
168
+ name: string,
169
+ file: ts.SourceFile,
170
+ seen: Set<string> = new Set()
171
+ ): Declaration | undefined {
172
+ const key = `${file.fileName}#${name}`;
173
+ if (seen.has(key)) return undefined;
174
+ seen.add(key);
175
+ for (const statement of file.statements) {
176
+ if (
177
+ ts.isInterfaceDeclaration(statement) &&
178
+ statement.name.text === name
179
+ ) {
180
+ return { kind: 'interface', node: statement, file };
181
+ }
182
+ if (
183
+ ts.isTypeAliasDeclaration(statement) &&
184
+ statement.name.text === name
185
+ ) {
186
+ return { kind: 'alias', node: statement, file };
187
+ }
188
+ }
189
+ for (const statement of file.statements) {
190
+ if (
191
+ ts.isImportDeclaration(statement) &&
192
+ ts.isStringLiteral(statement.moduleSpecifier)
193
+ ) {
194
+ const bindings = statement.importClause?.namedBindings;
195
+ if (!bindings || !ts.isNamedImports(bindings)) continue;
196
+ for (const element of bindings.elements) {
197
+ if (element.name.text !== name) continue;
198
+ const target = this.resolveModule(
199
+ statement.moduleSpecifier.text,
200
+ file
201
+ );
202
+ return target
203
+ ? this.findExport(
204
+ (element.propertyName ?? element.name).text,
205
+ target,
206
+ seen
207
+ )
208
+ : undefined;
209
+ }
210
+ }
211
+ }
212
+ return undefined;
213
+ }
214
+
215
+ /** The declaration a module exports as `name`, through re-exports. */
216
+ private findExport(
217
+ name: string,
218
+ file: ts.SourceFile,
219
+ seen: Set<string>
220
+ ): Declaration | undefined {
221
+ const local = this.find(name, file, seen);
222
+ if (local) return local;
223
+ for (const statement of file.statements) {
224
+ if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier)
225
+ continue;
226
+ if (!ts.isStringLiteral(statement.moduleSpecifier)) continue;
227
+ const clause = statement.exportClause;
228
+ let exported: string | undefined;
229
+ if (!clause) exported = name;
230
+ else if (ts.isNamedExports(clause)) {
231
+ const element = clause.elements.find(
232
+ (candidate) => candidate.name.text === name
233
+ );
234
+ exported = element
235
+ ? (element.propertyName ?? element.name).text
236
+ : undefined;
237
+ }
238
+ if (exported === undefined) continue;
239
+ const target = this.resolveModule(statement.moduleSpecifier.text, file);
240
+ const found = target
241
+ ? this.findExport(exported, target, seen)
242
+ : undefined;
243
+ if (found) return found;
244
+ }
245
+ return undefined;
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Reads the own members of the interface (or object type alias, or intersection
251
+ * of them) `propsFrom` names into preview prop definitions.
252
+ *
253
+ * @param reference - `'<file relative to the kit root>#<InterfaceName>'`
254
+ * @param kitRoot - The kit root the file resolves against
255
+ * @param templates - The kit's template components, which a
256
+ * `ComponentProps<typeof X>` in the type can name
257
+ * @returns The props, the default-slot flag, and the hidden and member names
258
+ */
259
+ export function readPropsFrom(
260
+ reference: string,
261
+ kitRoot: string,
262
+ templates: readonly EnumeratedComponent[] = []
263
+ ): PropsFromResult {
264
+ const { file, name } = parseReference(reference);
265
+ const path = resolve(kitRoot, file);
266
+ if (!existsSync(path))
267
+ throw new Error(`propsFrom '${reference}': no file at ${path}`);
268
+ const resolver = new DeclarationResolver(kitRoot);
269
+ const source = resolver.file(path);
270
+ const declaration = resolver.find(name, source);
271
+ const located = declaration
272
+ ? membersOf(declaration, {
273
+ resolver,
274
+ templates,
275
+ bindings: new Map(),
276
+ seen: new Set(),
277
+ })
278
+ : undefined;
279
+ if (!located) {
280
+ throw new Error(
281
+ `propsFrom '${reference}': no interface or object type named ${name} in ${file}`
282
+ );
283
+ }
284
+
285
+ // Expands aliases one member at a time, so a union of aliases reads as the
286
+ // union of their members; anything else keeps its source text.
287
+ const typeText = (
288
+ node: ts.TypeNode,
289
+ file: ts.SourceFile,
290
+ seen: Set<string> = new Set()
291
+ ): string => {
292
+ if (ts.isUnionTypeNode(node))
293
+ return node.types
294
+ .map((member) => typeText(member, file, seen))
295
+ .join(' | ');
296
+ if (ts.isParenthesizedTypeNode(node))
297
+ return typeText(node.type, file, seen);
298
+ if (
299
+ ts.isTypeReferenceNode(node) &&
300
+ ts.isIdentifier(node.typeName) &&
301
+ !node.typeArguments
302
+ ) {
303
+ const found = resolver.find(node.typeName.text, file);
304
+ const key = found ? `${found.file.fileName}#${node.typeName.text}` : '';
305
+ if (found?.kind === 'alias' && !seen.has(key)) {
306
+ return typeText(found.node.type, found.file, new Set([...seen, key]));
307
+ }
308
+ }
309
+ return node.getText(file);
310
+ };
311
+
312
+ // Later operands of an intersection win a shared name, as the type would.
313
+ const byName = new Map<string, LocatedMember>();
314
+ for (const entry of located) {
315
+ const memberName = locatedName(entry);
316
+ if (memberName !== undefined) byName.set(memberName, entry);
317
+ }
318
+
319
+ const result: PropsFromResult = {
320
+ props: {},
321
+ hasChildren: false,
322
+ childrenScoped: false,
323
+ hiddenProps: [],
324
+ memberNames: [],
325
+ };
326
+ for (const [memberName, entry] of byName) {
327
+ if (entry.kind === 'template-children') {
328
+ result.memberNames.push('children');
329
+ result.hasChildren = true;
330
+ result.childrenScoped = entry.scoped;
331
+ continue;
332
+ }
333
+ if (entry.kind === 'template') {
334
+ result.memberNames.push(memberName);
335
+ result.props[memberName] = entry.definition;
336
+ if (parseType(entry.definition.type).kind === 'complex')
337
+ result.hiddenProps.push(memberName);
338
+ continue;
339
+ }
340
+ const { member, file: memberFile } = entry;
341
+ if (!ts.isPropertySignature(member) && !ts.isMethodSignature(member))
342
+ continue;
343
+ result.memberNames.push(memberName);
344
+ if (memberName === 'children') {
345
+ const childrenType =
346
+ ts.isPropertySignature(member) && member.type ? member.type : undefined;
347
+ result.hasChildren =
348
+ childrenType !== undefined && !ts.isFunctionTypeNode(childrenType);
349
+ result.childrenScoped = false;
350
+ continue;
351
+ }
352
+ // A method signature is a function prop: never a control.
353
+ const type =
354
+ ts.isPropertySignature(member) && member.type
355
+ ? typeText(member.type, memberFile)
356
+ : '(...) => unknown';
357
+ result.props[memberName] = {
358
+ type,
359
+ ...(member.questionToken ? {} : { required: true }),
360
+ };
361
+ if (parseType(type).kind === 'complex') result.hiddenProps.push(memberName);
362
+ }
363
+ return result;
364
+ }
365
+
366
+ /** The name a located member declares, when it has a plain one. */
367
+ function locatedName(entry: LocatedMember): string | undefined {
368
+ if (entry.kind === 'template') return entry.name;
369
+ if (entry.kind === 'template-children') return 'children';
370
+ const { name } = entry.member;
371
+ return name && (ts.isIdentifier(name) || ts.isStringLiteral(name))
372
+ ? name.text
373
+ : undefined;
374
+ }
375
+
376
+ /** The members a declaration contributes: an interface's own members, an object
377
+ * type's, or each readable operand's of an intersection. `undefined` when the
378
+ * declaration holds no object type at all. */
379
+ function membersOf(
380
+ declaration: Declaration,
381
+ context: ReadContext
382
+ ): LocatedMember[] | undefined {
383
+ const key = `${declaration.file.fileName}#${declaration.node.name.text}`;
384
+ if (context.seen.has(key)) return [];
385
+ const inner = { ...context, seen: new Set([...context.seen, key]) };
386
+ if (declaration.kind === 'interface') {
387
+ return declaration.node.members.map((member) => ({
388
+ kind: 'member' as const,
389
+ member,
390
+ file: declaration.file,
391
+ }));
392
+ }
393
+ return typeMembers(declaration.node.type, declaration.file, inner);
394
+ }
395
+
396
+ /** The rightmost identifier of a type name: `ComponentProps` for both
397
+ * `ComponentProps` and `React.ComponentProps`. */
398
+ function simpleName(name: ts.EntityName): string {
399
+ return ts.isIdentifier(name) ? name.text : name.right.text;
400
+ }
401
+
402
+ /** The string literals of `'a'` or `'a' | 'b'`, or `undefined` for anything else. */
403
+ function literalNames(
404
+ node: ts.TypeNode,
405
+ file: ts.SourceFile,
406
+ context: ReadContext
407
+ ): string[] | undefined {
408
+ if (ts.isParenthesizedTypeNode(node))
409
+ return literalNames(node.type, file, context);
410
+ if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal))
411
+ return [node.literal.text];
412
+ if (ts.isUnionTypeNode(node)) {
413
+ const parts = node.types.map((part) => literalNames(part, file, context));
414
+ return parts.every((part) => part !== undefined)
415
+ ? parts.flatMap((part) => part ?? [])
416
+ : undefined;
417
+ }
418
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
419
+ const bound = context.bindings.get(node.typeName.text);
420
+ if (bound)
421
+ return literalNames(bound.node, bound.file, {
422
+ ...context,
423
+ bindings: bound.bindings,
424
+ });
425
+ }
426
+ return undefined;
427
+ }
428
+
429
+ function typeMembers(
430
+ node: ts.TypeNode,
431
+ file: ts.SourceFile,
432
+ context: ReadContext
433
+ ): LocatedMember[] | undefined {
434
+ if (ts.isParenthesizedTypeNode(node))
435
+ return typeMembers(node.type, file, context);
436
+ if (ts.isTypeLiteralNode(node))
437
+ return node.members.map((member) => ({
438
+ kind: 'member' as const,
439
+ member,
440
+ file,
441
+ }));
442
+ if (ts.isIntersectionTypeNode(node)) {
443
+ // A utility type or anything else unreadable contributes nothing; the
444
+ // intersection still reads when one operand is an object type.
445
+ const parts = node.types.map((part) => typeMembers(part, file, context));
446
+ return parts.some((part) => part !== undefined)
447
+ ? parts.flatMap((part) => part ?? [])
448
+ : undefined;
449
+ }
450
+ // A conditional that always holds is its true branch.
451
+ if (ts.isConditionalTypeNode(node)) {
452
+ const always =
453
+ node.extendsType.kind === ts.SyntaxKind.UnknownKeyword ||
454
+ node.extendsType.kind === ts.SyntaxKind.AnyKeyword;
455
+ return always ? typeMembers(node.trueType, file, context) : undefined;
456
+ }
457
+ if (!ts.isTypeReferenceNode(node)) return undefined;
458
+ const name = simpleName(node.typeName);
459
+ const typeArguments = node.typeArguments ?? [];
460
+
461
+ // A generic alias's parameter reads as the argument it was given.
462
+ if (ts.isIdentifier(node.typeName)) {
463
+ const bound = context.bindings.get(name);
464
+ if (bound)
465
+ return typeMembers(bound.node, bound.file, {
466
+ ...context,
467
+ bindings: bound.bindings,
468
+ });
469
+ }
470
+
471
+ if (COMPONENT_PROPS_TYPES.has(name) && typeArguments.length === 1) {
472
+ const [argument] = typeArguments;
473
+ if (ts.isTypeQueryNode(argument)) {
474
+ const component = simpleName(argument.exprName);
475
+ const template = context.templates.find(
476
+ (candidate) => candidate.name === component
477
+ );
478
+ if (!template) return undefined;
479
+ return [
480
+ ...Object.entries(template.props).map(([propName, definition]) => ({
481
+ kind: 'template' as const,
482
+ name: propName,
483
+ definition,
484
+ })),
485
+ ...(template.hasDefaultSlot
486
+ ? [
487
+ {
488
+ kind: 'template-children' as const,
489
+ scoped: template.defaultSlotScoped === true,
490
+ },
491
+ ]
492
+ : []),
493
+ ];
494
+ }
495
+ }
496
+
497
+ if (name === 'Omit' && typeArguments.length === 2) {
498
+ const base = typeMembers(typeArguments[0], file, context);
499
+ const omitted = literalNames(typeArguments[1], file, context);
500
+ if (!base || !omitted) return undefined;
501
+ return base.filter((entry) => {
502
+ const entryName = locatedName(entry);
503
+ return entryName === undefined || !omitted.includes(entryName);
504
+ });
505
+ }
506
+
507
+ if (!ts.isIdentifier(node.typeName)) return undefined;
508
+ const found = context.resolver.find(name, file);
509
+ if (!found) return undefined;
510
+ const parameters =
511
+ found.kind === 'alias' ? (found.node.typeParameters ?? []) : [];
512
+ if (parameters.length === 0 || typeArguments.length === 0)
513
+ return membersOf(found, { ...context, bindings: new Map() });
514
+ // A generic alias given arguments: its body, each parameter bound to the
515
+ // argument in this position (read where the reference is).
516
+ const bindings = new Map<string, BoundArgument>();
517
+ parameters.forEach((parameter, index) => {
518
+ const argument = typeArguments[index] ?? parameter.default;
519
+ if (argument)
520
+ bindings.set(parameter.name.text, {
521
+ node: argument,
522
+ file: typeArguments[index] ? file : found.file,
523
+ bindings: typeArguments[index] ? context.bindings : new Map(),
524
+ });
525
+ });
526
+ return membersOf(found, { ...context, bindings });
527
+ }
528
+
529
+ /** The keywords a type's text can name that are never a declaration. */
530
+ const TYPE_KEYWORDS = new Set([
531
+ 'string',
532
+ 'number',
533
+ 'boolean',
534
+ 'bigint',
535
+ 'symbol',
536
+ 'object',
537
+ 'unknown',
538
+ 'any',
539
+ 'never',
540
+ 'null',
541
+ 'undefined',
542
+ 'void',
543
+ 'true',
544
+ 'false',
545
+ ]);
546
+
547
+ /**
548
+ * A template prop's type with the named types it uses expanded, read through
549
+ * the template's own import statements: an alias becomes its members (a
550
+ * generic one with its arguments in place), an interface reads as an object
551
+ * form, and a union is flattened. `undefined` when the type names nothing to
552
+ * expand, or expands to the same text.
553
+ *
554
+ * @param type - The prop's type as the template writes it
555
+ * @param templatePath - The template file, which its imports resolve from
556
+ * @param imports - The template's import statements
557
+ * @param resolver - The declaration resolver to read through
558
+ * @returns The expanded type, or `undefined`
559
+ * @example
560
+ * // With `import type { StackAlign } from '@acme/kit/scripts';`
561
+ * resolveNamedType('StackAlign', templatePath, imports, resolver);
562
+ * // "'start' | 'center' | 'end'"
563
+ */
564
+ export function resolveNamedType(
565
+ type: string,
566
+ templatePath: string,
567
+ imports: readonly string[],
568
+ resolver: DeclarationResolver
569
+ ): string | undefined {
570
+ const names = type.match(/[A-Za-z_$][\w$]*/g) ?? [];
571
+ if (names.every((name) => TYPE_KEYWORDS.has(name))) return undefined;
572
+ const marker = '__PreviewPropType';
573
+ const source = resolver.virtual(
574
+ templatePath,
575
+ `${imports.join('\n')}\ntype ${marker} = ${type};\n`
576
+ );
577
+ const statement = source.statements.find(
578
+ (candidate): candidate is ts.TypeAliasDeclaration =>
579
+ ts.isTypeAliasDeclaration(candidate) && candidate.name.text === marker
580
+ );
581
+ if (!statement) return undefined;
582
+
583
+ const expand = (
584
+ node: ts.TypeNode,
585
+ file: ts.SourceFile,
586
+ bindings: ReadonlyMap<string, BoundArgument>,
587
+ seen: ReadonlySet<string>
588
+ ): string => {
589
+ if (ts.isUnionTypeNode(node))
590
+ return node.types
591
+ .map((member) => expand(member, file, bindings, seen))
592
+ .join(' | ');
593
+ if (ts.isParenthesizedTypeNode(node) && ts.isUnionTypeNode(node.type))
594
+ return expand(node.type, file, bindings, seen);
595
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
596
+ const name = node.typeName.text;
597
+ const bound = bindings.get(name);
598
+ if (bound) return expand(bound.node, bound.file, bound.bindings, seen);
599
+ const found = resolver.find(name, file);
600
+ const key = found ? `${found.file.fileName}#${name}` : '';
601
+ if (found && !seen.has(key)) {
602
+ if (found.kind === 'interface') return '{}';
603
+ const inner = new Map<string, BoundArgument>();
604
+ (found.node.typeParameters ?? []).forEach((parameter, index) => {
605
+ const argument = node.typeArguments?.[index];
606
+ if (argument)
607
+ inner.set(parameter.name.text, { node: argument, file, bindings });
608
+ else if (parameter.default)
609
+ inner.set(parameter.name.text, {
610
+ node: parameter.default,
611
+ file: found.file,
612
+ bindings: new Map(),
613
+ });
614
+ });
615
+ return expand(
616
+ found.node.type,
617
+ found.file,
618
+ inner,
619
+ new Set([...seen, key])
620
+ );
621
+ }
622
+ }
623
+ return node.getText(file);
624
+ };
625
+
626
+ const expanded = expand(statement.type, source, new Map(), new Set());
627
+ return expanded === type ? undefined : expanded;
628
+ }
629
+
630
+ /**
631
+ * Resolves a manual spec into an EnumeratedComponent, reading `propsFrom` when set.
632
+ *
633
+ * @param specification - The manual component's specification
634
+ * @param kitRoot - The kit root `propsFrom` resolves against
635
+ * @param templates - The kit's template components, which a
636
+ * `ComponentProps<typeof X>` in the props type can name
637
+ * @returns The component, with its props read from `propsFrom` or given inline
638
+ */
639
+ export function resolveManualComponent(
640
+ specification: ManualComponentSpecification,
641
+ kitRoot: string,
642
+ templates: readonly EnumeratedComponent[] = []
643
+ ): EnumeratedComponent {
644
+ const { propsFrom, ...rest } = specification;
645
+ if (propsFrom !== undefined && specification.props !== undefined) {
646
+ throw new Error(
647
+ `[preview] manual component ${specification.name}: give props or propsFrom, not both`
648
+ );
649
+ }
650
+ if (propsFrom === undefined) {
651
+ return {
652
+ ...rest,
653
+ props: specification.props ?? {},
654
+ hasDefaultSlot: specification.hasDefaultSlot ?? false,
655
+ slotNames: specification.slotNames ?? [],
656
+ };
657
+ }
658
+ let read: PropsFromResult;
659
+ try {
660
+ read = readPropsFrom(propsFrom, kitRoot, templates);
661
+ } catch (error) {
662
+ throw new Error(
663
+ `[preview] manual component ${specification.name}: ${(error as Error).message}`,
664
+ { cause: error }
665
+ );
666
+ }
667
+ return {
668
+ ...rest,
669
+ props: read.props,
670
+ hasDefaultSlot: specification.hasDefaultSlot ?? read.hasChildren,
671
+ ...((specification.defaultSlotScoped ?? read.childrenScoped)
672
+ ? { defaultSlotScoped: true }
673
+ : {}),
674
+ slotNames: specification.slotNames ?? [],
675
+ hiddenProps: read.hiddenProps,
676
+ memberNames: read.memberNames,
677
+ };
678
+ }