@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,510 @@
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
+ import { existsSync, readFileSync } from 'node:fs';
21
+ import { dirname, resolve } from 'node:path';
22
+ import ts from 'typescript';
23
+ import { parseType } from './lib/parse-type.js';
24
+ /** Splits `'<file>#<InterfaceName>'`, failing on any other shape. */
25
+ function parseReference(reference) {
26
+ const [file, name, ...rest] = reference.split('#');
27
+ if (!file || !name || rest.length > 0) {
28
+ throw new Error(`propsFrom '${reference}' must be '<file relative to the kit root>#<InterfaceName>'`);
29
+ }
30
+ return { file, name };
31
+ }
32
+ /** The React utility types that take a component's props from `typeof X`. */
33
+ const COMPONENT_PROPS_TYPES = new Set([
34
+ 'ComponentProps',
35
+ 'ComponentPropsWithRef',
36
+ 'ComponentPropsWithoutRef',
37
+ ]);
38
+ /** Finds type declarations by name across a kit's source files, following
39
+ * imports and re-exports with TypeScript's module resolution and the kit's
40
+ * tsconfig. Parses each file once. */
41
+ export class DeclarationResolver {
42
+ files = new Map();
43
+ options;
44
+ constructor(kitRoot) {
45
+ const configurationPath = ts.findConfigFile(kitRoot, ts.sys.fileExists);
46
+ const parsed = configurationPath
47
+ ? ts.parseJsonConfigFileContent(ts.readConfigFile(configurationPath, ts.sys.readFile).config, ts.sys, dirname(configurationPath))
48
+ : undefined;
49
+ this.options = {
50
+ ...(parsed?.options ?? {}),
51
+ // Bundler resolution reads package `exports`, so a kit importing its own
52
+ // package by name resolves to its source.
53
+ moduleResolution: parsed?.options.moduleResolution ?? ts.ModuleResolutionKind.Bundler,
54
+ module: parsed?.options.module ?? ts.ModuleKind.ESNext,
55
+ };
56
+ }
57
+ /** Parses source that is not on disk, as if it were the file at `path`, so
58
+ * its imports resolve from there. Not cached. */
59
+ virtual(path, text) {
60
+ return ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
61
+ }
62
+ file(path) {
63
+ let file = this.files.get(path);
64
+ if (!file) {
65
+ file = ts.createSourceFile(path, readFileSync(path, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
66
+ this.files.set(path, file);
67
+ }
68
+ return file;
69
+ }
70
+ resolveModule(specifier, from) {
71
+ const resolved = ts.resolveModuleName(specifier, from.fileName, this.options, ts.sys).resolvedModule;
72
+ return resolved && existsSync(resolved.resolvedFileName)
73
+ ? this.file(resolved.resolvedFileName)
74
+ : undefined;
75
+ }
76
+ /** The declaration `name` refers to inside `file`: declared there, or imported. */
77
+ find(name, file, seen = new Set()) {
78
+ const key = `${file.fileName}#${name}`;
79
+ if (seen.has(key))
80
+ return undefined;
81
+ seen.add(key);
82
+ for (const statement of file.statements) {
83
+ if (ts.isInterfaceDeclaration(statement) &&
84
+ statement.name.text === name) {
85
+ return { kind: 'interface', node: statement, file };
86
+ }
87
+ if (ts.isTypeAliasDeclaration(statement) &&
88
+ statement.name.text === name) {
89
+ return { kind: 'alias', node: statement, file };
90
+ }
91
+ }
92
+ for (const statement of file.statements) {
93
+ if (ts.isImportDeclaration(statement) &&
94
+ ts.isStringLiteral(statement.moduleSpecifier)) {
95
+ const bindings = statement.importClause?.namedBindings;
96
+ if (!bindings || !ts.isNamedImports(bindings))
97
+ continue;
98
+ for (const element of bindings.elements) {
99
+ if (element.name.text !== name)
100
+ continue;
101
+ const target = this.resolveModule(statement.moduleSpecifier.text, file);
102
+ return target
103
+ ? this.findExport((element.propertyName ?? element.name).text, target, seen)
104
+ : undefined;
105
+ }
106
+ }
107
+ }
108
+ return undefined;
109
+ }
110
+ /** The declaration a module exports as `name`, through re-exports. */
111
+ findExport(name, file, seen) {
112
+ const local = this.find(name, file, seen);
113
+ if (local)
114
+ return local;
115
+ for (const statement of file.statements) {
116
+ if (!ts.isExportDeclaration(statement) || !statement.moduleSpecifier)
117
+ continue;
118
+ if (!ts.isStringLiteral(statement.moduleSpecifier))
119
+ continue;
120
+ const clause = statement.exportClause;
121
+ let exported;
122
+ if (!clause)
123
+ exported = name;
124
+ else if (ts.isNamedExports(clause)) {
125
+ const element = clause.elements.find((candidate) => candidate.name.text === name);
126
+ exported = element
127
+ ? (element.propertyName ?? element.name).text
128
+ : undefined;
129
+ }
130
+ if (exported === undefined)
131
+ continue;
132
+ const target = this.resolveModule(statement.moduleSpecifier.text, file);
133
+ const found = target
134
+ ? this.findExport(exported, target, seen)
135
+ : undefined;
136
+ if (found)
137
+ return found;
138
+ }
139
+ return undefined;
140
+ }
141
+ }
142
+ /**
143
+ * Reads the own members of the interface (or object type alias, or intersection
144
+ * of them) `propsFrom` names into preview prop definitions.
145
+ *
146
+ * @param reference - `'<file relative to the kit root>#<InterfaceName>'`
147
+ * @param kitRoot - The kit root the file resolves against
148
+ * @param templates - The kit's template components, which a
149
+ * `ComponentProps<typeof X>` in the type can name
150
+ * @returns The props, the default-slot flag, and the hidden and member names
151
+ */
152
+ export function readPropsFrom(reference, kitRoot, templates = []) {
153
+ const { file, name } = parseReference(reference);
154
+ const path = resolve(kitRoot, file);
155
+ if (!existsSync(path))
156
+ throw new Error(`propsFrom '${reference}': no file at ${path}`);
157
+ const resolver = new DeclarationResolver(kitRoot);
158
+ const source = resolver.file(path);
159
+ const declaration = resolver.find(name, source);
160
+ const located = declaration
161
+ ? membersOf(declaration, {
162
+ resolver,
163
+ templates,
164
+ bindings: new Map(),
165
+ seen: new Set(),
166
+ })
167
+ : undefined;
168
+ if (!located) {
169
+ throw new Error(`propsFrom '${reference}': no interface or object type named ${name} in ${file}`);
170
+ }
171
+ // Expands aliases one member at a time, so a union of aliases reads as the
172
+ // union of their members; anything else keeps its source text.
173
+ const typeText = (node, file, seen = new Set()) => {
174
+ if (ts.isUnionTypeNode(node))
175
+ return node.types
176
+ .map((member) => typeText(member, file, seen))
177
+ .join(' | ');
178
+ if (ts.isParenthesizedTypeNode(node))
179
+ return typeText(node.type, file, seen);
180
+ if (ts.isTypeReferenceNode(node) &&
181
+ ts.isIdentifier(node.typeName) &&
182
+ !node.typeArguments) {
183
+ const found = resolver.find(node.typeName.text, file);
184
+ const key = found ? `${found.file.fileName}#${node.typeName.text}` : '';
185
+ if (found?.kind === 'alias' && !seen.has(key)) {
186
+ return typeText(found.node.type, found.file, new Set([...seen, key]));
187
+ }
188
+ }
189
+ return node.getText(file);
190
+ };
191
+ // Later operands of an intersection win a shared name, as the type would.
192
+ const byName = new Map();
193
+ for (const entry of located) {
194
+ const memberName = locatedName(entry);
195
+ if (memberName !== undefined)
196
+ byName.set(memberName, entry);
197
+ }
198
+ const result = {
199
+ props: {},
200
+ hasChildren: false,
201
+ childrenScoped: false,
202
+ hiddenProps: [],
203
+ memberNames: [],
204
+ };
205
+ for (const [memberName, entry] of byName) {
206
+ if (entry.kind === 'template-children') {
207
+ result.memberNames.push('children');
208
+ result.hasChildren = true;
209
+ result.childrenScoped = entry.scoped;
210
+ continue;
211
+ }
212
+ if (entry.kind === 'template') {
213
+ result.memberNames.push(memberName);
214
+ result.props[memberName] = entry.definition;
215
+ if (parseType(entry.definition.type).kind === 'complex')
216
+ result.hiddenProps.push(memberName);
217
+ continue;
218
+ }
219
+ const { member, file: memberFile } = entry;
220
+ if (!ts.isPropertySignature(member) && !ts.isMethodSignature(member))
221
+ continue;
222
+ result.memberNames.push(memberName);
223
+ if (memberName === 'children') {
224
+ const childrenType = ts.isPropertySignature(member) && member.type ? member.type : undefined;
225
+ result.hasChildren =
226
+ childrenType !== undefined && !ts.isFunctionTypeNode(childrenType);
227
+ result.childrenScoped = false;
228
+ continue;
229
+ }
230
+ // A method signature is a function prop: never a control.
231
+ const type = ts.isPropertySignature(member) && member.type
232
+ ? typeText(member.type, memberFile)
233
+ : '(...) => unknown';
234
+ result.props[memberName] = {
235
+ type,
236
+ ...(member.questionToken ? {} : { required: true }),
237
+ };
238
+ if (parseType(type).kind === 'complex')
239
+ result.hiddenProps.push(memberName);
240
+ }
241
+ return result;
242
+ }
243
+ /** The name a located member declares, when it has a plain one. */
244
+ function locatedName(entry) {
245
+ if (entry.kind === 'template')
246
+ return entry.name;
247
+ if (entry.kind === 'template-children')
248
+ return 'children';
249
+ const { name } = entry.member;
250
+ return name && (ts.isIdentifier(name) || ts.isStringLiteral(name))
251
+ ? name.text
252
+ : undefined;
253
+ }
254
+ /** The members a declaration contributes: an interface's own members, an object
255
+ * type's, or each readable operand's of an intersection. `undefined` when the
256
+ * declaration holds no object type at all. */
257
+ function membersOf(declaration, context) {
258
+ const key = `${declaration.file.fileName}#${declaration.node.name.text}`;
259
+ if (context.seen.has(key))
260
+ return [];
261
+ const inner = { ...context, seen: new Set([...context.seen, key]) };
262
+ if (declaration.kind === 'interface') {
263
+ return declaration.node.members.map((member) => ({
264
+ kind: 'member',
265
+ member,
266
+ file: declaration.file,
267
+ }));
268
+ }
269
+ return typeMembers(declaration.node.type, declaration.file, inner);
270
+ }
271
+ /** The rightmost identifier of a type name: `ComponentProps` for both
272
+ * `ComponentProps` and `React.ComponentProps`. */
273
+ function simpleName(name) {
274
+ return ts.isIdentifier(name) ? name.text : name.right.text;
275
+ }
276
+ /** The string literals of `'a'` or `'a' | 'b'`, or `undefined` for anything else. */
277
+ function literalNames(node, file, context) {
278
+ if (ts.isParenthesizedTypeNode(node))
279
+ return literalNames(node.type, file, context);
280
+ if (ts.isLiteralTypeNode(node) && ts.isStringLiteral(node.literal))
281
+ return [node.literal.text];
282
+ if (ts.isUnionTypeNode(node)) {
283
+ const parts = node.types.map((part) => literalNames(part, file, context));
284
+ return parts.every((part) => part !== undefined)
285
+ ? parts.flatMap((part) => part ?? [])
286
+ : undefined;
287
+ }
288
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
289
+ const bound = context.bindings.get(node.typeName.text);
290
+ if (bound)
291
+ return literalNames(bound.node, bound.file, {
292
+ ...context,
293
+ bindings: bound.bindings,
294
+ });
295
+ }
296
+ return undefined;
297
+ }
298
+ function typeMembers(node, file, context) {
299
+ if (ts.isParenthesizedTypeNode(node))
300
+ return typeMembers(node.type, file, context);
301
+ if (ts.isTypeLiteralNode(node))
302
+ return node.members.map((member) => ({
303
+ kind: 'member',
304
+ member,
305
+ file,
306
+ }));
307
+ if (ts.isIntersectionTypeNode(node)) {
308
+ // A utility type or anything else unreadable contributes nothing; the
309
+ // intersection still reads when one operand is an object type.
310
+ const parts = node.types.map((part) => typeMembers(part, file, context));
311
+ return parts.some((part) => part !== undefined)
312
+ ? parts.flatMap((part) => part ?? [])
313
+ : undefined;
314
+ }
315
+ // A conditional that always holds is its true branch.
316
+ if (ts.isConditionalTypeNode(node)) {
317
+ const always = node.extendsType.kind === ts.SyntaxKind.UnknownKeyword ||
318
+ node.extendsType.kind === ts.SyntaxKind.AnyKeyword;
319
+ return always ? typeMembers(node.trueType, file, context) : undefined;
320
+ }
321
+ if (!ts.isTypeReferenceNode(node))
322
+ return undefined;
323
+ const name = simpleName(node.typeName);
324
+ const typeArguments = node.typeArguments ?? [];
325
+ // A generic alias's parameter reads as the argument it was given.
326
+ if (ts.isIdentifier(node.typeName)) {
327
+ const bound = context.bindings.get(name);
328
+ if (bound)
329
+ return typeMembers(bound.node, bound.file, {
330
+ ...context,
331
+ bindings: bound.bindings,
332
+ });
333
+ }
334
+ if (COMPONENT_PROPS_TYPES.has(name) && typeArguments.length === 1) {
335
+ const [argument] = typeArguments;
336
+ if (ts.isTypeQueryNode(argument)) {
337
+ const component = simpleName(argument.exprName);
338
+ const template = context.templates.find((candidate) => candidate.name === component);
339
+ if (!template)
340
+ return undefined;
341
+ return [
342
+ ...Object.entries(template.props).map(([propName, definition]) => ({
343
+ kind: 'template',
344
+ name: propName,
345
+ definition,
346
+ })),
347
+ ...(template.hasDefaultSlot
348
+ ? [
349
+ {
350
+ kind: 'template-children',
351
+ scoped: template.defaultSlotScoped === true,
352
+ },
353
+ ]
354
+ : []),
355
+ ];
356
+ }
357
+ }
358
+ if (name === 'Omit' && typeArguments.length === 2) {
359
+ const base = typeMembers(typeArguments[0], file, context);
360
+ const omitted = literalNames(typeArguments[1], file, context);
361
+ if (!base || !omitted)
362
+ return undefined;
363
+ return base.filter((entry) => {
364
+ const entryName = locatedName(entry);
365
+ return entryName === undefined || !omitted.includes(entryName);
366
+ });
367
+ }
368
+ if (!ts.isIdentifier(node.typeName))
369
+ return undefined;
370
+ const found = context.resolver.find(name, file);
371
+ if (!found)
372
+ return undefined;
373
+ const parameters = found.kind === 'alias' ? (found.node.typeParameters ?? []) : [];
374
+ if (parameters.length === 0 || typeArguments.length === 0)
375
+ return membersOf(found, { ...context, bindings: new Map() });
376
+ // A generic alias given arguments: its body, each parameter bound to the
377
+ // argument in this position (read where the reference is).
378
+ const bindings = new Map();
379
+ parameters.forEach((parameter, index) => {
380
+ const argument = typeArguments[index] ?? parameter.default;
381
+ if (argument)
382
+ bindings.set(parameter.name.text, {
383
+ node: argument,
384
+ file: typeArguments[index] ? file : found.file,
385
+ bindings: typeArguments[index] ? context.bindings : new Map(),
386
+ });
387
+ });
388
+ return membersOf(found, { ...context, bindings });
389
+ }
390
+ /** The keywords a type's text can name that are never a declaration. */
391
+ const TYPE_KEYWORDS = new Set([
392
+ 'string',
393
+ 'number',
394
+ 'boolean',
395
+ 'bigint',
396
+ 'symbol',
397
+ 'object',
398
+ 'unknown',
399
+ 'any',
400
+ 'never',
401
+ 'null',
402
+ 'undefined',
403
+ 'void',
404
+ 'true',
405
+ 'false',
406
+ ]);
407
+ /**
408
+ * A template prop's type with the named types it uses expanded, read through
409
+ * the template's own import statements: an alias becomes its members (a
410
+ * generic one with its arguments in place), an interface reads as an object
411
+ * form, and a union is flattened. `undefined` when the type names nothing to
412
+ * expand, or expands to the same text.
413
+ *
414
+ * @param type - The prop's type as the template writes it
415
+ * @param templatePath - The template file, which its imports resolve from
416
+ * @param imports - The template's import statements
417
+ * @param resolver - The declaration resolver to read through
418
+ * @returns The expanded type, or `undefined`
419
+ * @example
420
+ * // With `import type { StackAlign } from '@acme/kit/scripts';`
421
+ * resolveNamedType('StackAlign', templatePath, imports, resolver);
422
+ * // "'start' | 'center' | 'end'"
423
+ */
424
+ export function resolveNamedType(type, templatePath, imports, resolver) {
425
+ const names = type.match(/[A-Za-z_$][\w$]*/g) ?? [];
426
+ if (names.every((name) => TYPE_KEYWORDS.has(name)))
427
+ return undefined;
428
+ const marker = '__PreviewPropType';
429
+ const source = resolver.virtual(templatePath, `${imports.join('\n')}\ntype ${marker} = ${type};\n`);
430
+ const statement = source.statements.find((candidate) => ts.isTypeAliasDeclaration(candidate) && candidate.name.text === marker);
431
+ if (!statement)
432
+ return undefined;
433
+ const expand = (node, file, bindings, seen) => {
434
+ if (ts.isUnionTypeNode(node))
435
+ return node.types
436
+ .map((member) => expand(member, file, bindings, seen))
437
+ .join(' | ');
438
+ if (ts.isParenthesizedTypeNode(node) && ts.isUnionTypeNode(node.type))
439
+ return expand(node.type, file, bindings, seen);
440
+ if (ts.isTypeReferenceNode(node) && ts.isIdentifier(node.typeName)) {
441
+ const name = node.typeName.text;
442
+ const bound = bindings.get(name);
443
+ if (bound)
444
+ return expand(bound.node, bound.file, bound.bindings, seen);
445
+ const found = resolver.find(name, file);
446
+ const key = found ? `${found.file.fileName}#${name}` : '';
447
+ if (found && !seen.has(key)) {
448
+ if (found.kind === 'interface')
449
+ return '{}';
450
+ const inner = new Map();
451
+ (found.node.typeParameters ?? []).forEach((parameter, index) => {
452
+ const argument = node.typeArguments?.[index];
453
+ if (argument)
454
+ inner.set(parameter.name.text, { node: argument, file, bindings });
455
+ else if (parameter.default)
456
+ inner.set(parameter.name.text, {
457
+ node: parameter.default,
458
+ file: found.file,
459
+ bindings: new Map(),
460
+ });
461
+ });
462
+ return expand(found.node.type, found.file, inner, new Set([...seen, key]));
463
+ }
464
+ }
465
+ return node.getText(file);
466
+ };
467
+ const expanded = expand(statement.type, source, new Map(), new Set());
468
+ return expanded === type ? undefined : expanded;
469
+ }
470
+ /**
471
+ * Resolves a manual spec into an EnumeratedComponent, reading `propsFrom` when set.
472
+ *
473
+ * @param specification - The manual component's specification
474
+ * @param kitRoot - The kit root `propsFrom` resolves against
475
+ * @param templates - The kit's template components, which a
476
+ * `ComponentProps<typeof X>` in the props type can name
477
+ * @returns The component, with its props read from `propsFrom` or given inline
478
+ */
479
+ export function resolveManualComponent(specification, kitRoot, templates = []) {
480
+ const { propsFrom, ...rest } = specification;
481
+ if (propsFrom !== undefined && specification.props !== undefined) {
482
+ throw new Error(`[preview] manual component ${specification.name}: give props or propsFrom, not both`);
483
+ }
484
+ if (propsFrom === undefined) {
485
+ return {
486
+ ...rest,
487
+ props: specification.props ?? {},
488
+ hasDefaultSlot: specification.hasDefaultSlot ?? false,
489
+ slotNames: specification.slotNames ?? [],
490
+ };
491
+ }
492
+ let read;
493
+ try {
494
+ read = readPropsFrom(propsFrom, kitRoot, templates);
495
+ }
496
+ catch (error) {
497
+ throw new Error(`[preview] manual component ${specification.name}: ${error.message}`, { cause: error });
498
+ }
499
+ return {
500
+ ...rest,
501
+ props: read.props,
502
+ hasDefaultSlot: specification.hasDefaultSlot ?? read.hasChildren,
503
+ ...((specification.defaultSlotScoped ?? read.childrenScoped)
504
+ ? { defaultSlotScoped: true }
505
+ : {}),
506
+ slotNames: specification.slotNames ?? [],
507
+ hiddenProps: read.hiddenProps,
508
+ memberNames: read.memberNames,
509
+ };
510
+ }