@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,13 @@
1
+ // Shared data shapes for the story generator. Everything here is
2
+ // framework-agnostic - the emitters (emit/<target>.ts) translate these into
3
+ // each framework's own syntax.
4
+ /** The default: a component renders in every target unless it narrows the set. */
5
+ export const ALL_TARGETS = ['react', 'vue', 'svelte'];
6
+ /** The keys of the preview block its pages read, which must be JSON. */
7
+ export const PREVIEW_BROWSER_KEYS = [
8
+ 'options',
9
+ 'canvas',
10
+ 'themes',
11
+ 'mount',
12
+ 'viewCanvas',
13
+ ];
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Whether a tag is an HTML void element.
3
+ *
4
+ * @param tag - The tag name, as an html fill writes it.
5
+ * @returns True for a void element (`img`, `input`, `br`, ...).
6
+ */
7
+ export declare function isVoidElement(tag: string): boolean;
@@ -0,0 +1,13 @@
1
+ // HTML void elements: an html slot fill with one of these tags renders
2
+ // self-closed and takes no children. The list is the engine core's, so the
3
+ // preview and the templates' own output agree on it.
4
+ import { VOID_ELEMENTS } from '@js-template-engine/core';
5
+ /**
6
+ * Whether a tag is an HTML void element.
7
+ *
8
+ * @param tag - The tag name, as an html fill writes it.
9
+ * @returns True for a void element (`img`, `input`, `br`, ...).
10
+ */
11
+ export function isVoidElement(tag) {
12
+ return VOID_ELEMENTS.has(tag.toLowerCase());
13
+ }
package/dist/load.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { type PreviewContext, type ResolvedSource } from './context.js';
2
+ import type { EnumeratedComponent, PreviewPaneWidth, Story } from './lib/types.js';
3
+ /** The pane widths the kit offers and what is wrong with them. */
4
+ export interface KitPaneWidths {
5
+ /** The widths: the preview block's `paneWidths`, else none (the
6
+ * preview's own). */
7
+ paneWidths?: PreviewPaneWidth[];
8
+ /** What makes the kit's widths unusable. */
9
+ errors: string[];
10
+ }
11
+ /**
12
+ * The pane widths a kit offers: its preview block's `paneWidths`, which a
13
+ * kit can derive from its own breakpoints.
14
+ *
15
+ * @param context - The kit's preview context.
16
+ * @returns The widths and the errors.
17
+ */
18
+ export declare function kitPaneWidths(context: PreviewContext): KitPaneWidths;
19
+ /** One component with its source and story file. */
20
+ export interface LoadedComponent {
21
+ component: EnumeratedComponent;
22
+ story: Story | undefined;
23
+ source: ResolvedSource;
24
+ }
25
+ export interface LoadedKit {
26
+ /** Every source's components, each source sorted by name, sources in order. */
27
+ components: LoadedComponent[];
28
+ /**
29
+ * Each compound part, by name, with the root that lists it: in its
30
+ * template's `parts`, or, for a root with no template, in the kit
31
+ * configuration's. A listed part is not standalone, whatever its story
32
+ * file says.
33
+ */
34
+ parts: Map<string, string>;
35
+ }
36
+ /** Loads every source's components and story files. */
37
+ export declare function loadKit(context: PreviewContext): Promise<LoadedKit>;
package/dist/load.js ADDED
@@ -0,0 +1,82 @@
1
+ // Load what the generator and the check both read from a kit: every source's
2
+ // components (globbed templates plus resolved manual specs),
3
+ // each paired with its story file. One loader, so the check sees exactly what
4
+ // generation renders.
5
+ import { enumerateComponents } from './enumerate.js';
6
+ import { resolveManualComponent } from './props-from.js';
7
+ import { loadStory } from './story.js';
8
+ import { paneWidthsProblem, } from './context.js';
9
+ /**
10
+ * The pane widths a kit offers: its preview block's `paneWidths`, which a
11
+ * kit can derive from its own breakpoints.
12
+ *
13
+ * @param context - The kit's preview context.
14
+ * @returns The widths and the errors.
15
+ */
16
+ export function kitPaneWidths(context) {
17
+ const paneWidths = context.configuration.paneWidths;
18
+ if (paneWidths === undefined)
19
+ return { errors: [] };
20
+ const problem = paneWidthsProblem(paneWidths, 'preview.paneWidths');
21
+ return problem ? { errors: [problem] } : { paneWidths, errors: [] };
22
+ }
23
+ /** Loads every source's components and story files. */
24
+ export async function loadKit(context) {
25
+ const { sources, importModule } = context;
26
+ // Every source's templates first, so a manual component's `propsFrom` can
27
+ // build on a template from any source - its own source's first.
28
+ const templatesBySource = new Map();
29
+ for (const source of sources) {
30
+ templatesBySource.set(source, source.templatesDirectoryAbsolute
31
+ ? await enumerateComponents(source.templatesDirectoryAbsolute, importModule)
32
+ : []);
33
+ }
34
+ // A compound's root names its parts, in its template or, with no template,
35
+ // in the kit configuration; none is standalone, as a story file's
36
+ // `standalone: false` also marks one.
37
+ const parts = new Map();
38
+ for (const [root, rootParts] of Object.entries(context.parts ?? {})) {
39
+ for (const part of rootParts)
40
+ parts.set(part, root);
41
+ }
42
+ for (const templates of templatesBySource.values()) {
43
+ for (const { name, template } of templates) {
44
+ if (template && !Array.isArray(template)) {
45
+ for (const part of template.parts ?? [])
46
+ parts.set(part, name);
47
+ }
48
+ }
49
+ }
50
+ const components = [];
51
+ for (const source of sources) {
52
+ const templates = templatesBySource.get(source) ?? [];
53
+ const visibleTemplates = [
54
+ ...templates,
55
+ ...sources
56
+ .filter((other) => other !== source)
57
+ .flatMap((other) => templatesBySource.get(other) ?? []),
58
+ ];
59
+ const manual = source.manualModuleAbsolute
60
+ ? (await importModule(source.manualModuleAbsolute))
61
+ : undefined;
62
+ if (source.manualModuleAbsolute && typeof manual?.default !== 'function') {
63
+ throw new Error(`[preview] ${source.manualModule} must default-export a () => ManualComponentSpecification[]`);
64
+ }
65
+ const sourceComponents = [
66
+ ...templates,
67
+ ...(manual?.default
68
+ ? manual
69
+ .default()
70
+ .map((specification) => resolveManualComponent(specification, context.kitRoot, visibleTemplates))
71
+ : []),
72
+ ].sort((a, b) => a.name.localeCompare(b.name));
73
+ for (const listed of sourceComponents) {
74
+ const component = parts.has(listed.name)
75
+ ? { ...listed, standalone: false }
76
+ : listed;
77
+ const story = await loadStory(component.name, source.storiesDirectoryAbsolute, importModule);
78
+ components.push({ component, story, source });
79
+ }
80
+ }
81
+ return { components, parts };
82
+ }
@@ -0,0 +1,44 @@
1
+ import type { PropTypeSpecification, EnumeratedComponent, JsonValue, Story, PreviewBinding, ResolvedStory, PreviewControlHints, PreviewLabels, Target } from './lib/types.js';
2
+ /** The category a component without a story file `category` sits in. */
3
+ export declare const DEFAULT_CATEGORY = "Components";
4
+ /**
5
+ * The two-way props a component's template declares, in prop order: each
6
+ * `bindable` object's prop, copy form or relayed, the props it names, and
7
+ * whether a Vue consumer binds it with a bare `v-model`. A component written
8
+ * by hand declares none.
9
+ *
10
+ * @param component - The enumerated component.
11
+ * @returns The bindings.
12
+ */
13
+ export declare function bindingsOf(component: EnumeratedComponent): PreviewBinding[];
14
+ /**
15
+ * The values a prop's scenarios should cover: each select option and each
16
+ * boolean value other than the prop's default (a select's `false` too only
17
+ * when the prop has one), for every included select and
18
+ * boolean control. Shared by scenario generation and the check's coverage.
19
+ */
20
+ export declare function scenarioValues(propTypes: readonly PropTypeSpecification[], defaultProps: Record<string, JsonValue>): Array<{
21
+ prop: string;
22
+ value: string | boolean;
23
+ }>;
24
+ /** Whether a story file's `generate` lets this prop generate scenarios. */
25
+ export declare function generatesProp(generate: Story['generate'], prop: string): boolean;
26
+ /** The id a generated scenario takes: `<prop>-<value>`, or the prop for a
27
+ * boolean turned on, unless that is the preview's own Empty scenario's id,
28
+ * which a boolean named `empty` takes as `empty-true`. A curated scenario
29
+ * with this id replaces it. */
30
+ export declare function generatedScenarioId(prop: string, value: string | boolean): string;
31
+ /**
32
+ * Resolve the story of one component: its controls, default props,
33
+ * scenarios, slot content and the targets it renders in.
34
+ *
35
+ * @param component - The enumerated component.
36
+ * @param story - The component's story file, when it has one.
37
+ * @param hints - The kit's control hints.
38
+ * @param labels - The kit's preview labels.
39
+ * @param omitPattern - Component names matching it get no story.
40
+ * @param kitTargets - The scripted targets the kit builds; the component's own
41
+ * targets are narrowed to them.
42
+ * @returns The resolved story.
43
+ */
44
+ export declare function resolveStory(component: EnumeratedComponent, story: Story | undefined, hints?: PreviewControlHints, labels?: PreviewLabels, omitPattern?: RegExp, kitTargets?: readonly Target[]): ResolvedStory;
package/dist/model.js ADDED
@@ -0,0 +1,464 @@
1
+ // Build the framework-agnostic ResolvedStory for one component: merge prop-derived
2
+ // propTypes with the story file (controls, prop defaults, slots, scenarios, omit).
3
+ import { isEventType, relayedBindingsOf, twoWayBindingsOf, } from '@js-template-engine/core';
4
+ import { propTypeForProp, defaultPropForProp } from './prop-types.js';
5
+ import { isStructurallyOmitted } from './story.js';
6
+ import { collectSlotComponents } from './lib/slot-fill.js';
7
+ import { EMPTY_SCENARIO_ID } from './lib/naming.js';
8
+ import { isVoidElement } from './lib/void-elements.js';
9
+ import { ALL_TARGETS } from './lib/types.js';
10
+ /** The category a component without a story file `category` sits in. */
11
+ export const DEFAULT_CATEGORY = 'Components';
12
+ /**
13
+ * The two-way props a component's template declares, in prop order: each
14
+ * `bindable` object's prop, copy form or relayed, the props it names, and
15
+ * whether a Vue consumer binds it with a bare `v-model`. A component written
16
+ * by hand declares none.
17
+ *
18
+ * @param component - The enumerated component.
19
+ * @returns The bindings.
20
+ */
21
+ export function bindingsOf(component) {
22
+ const template = component.template;
23
+ if (!template || Array.isArray(template) || !template.props)
24
+ return [];
25
+ const vue = template.extensions?.['vue'];
26
+ const vueModel = typeof vue === 'object' && vue !== null
27
+ ? vue.model
28
+ : undefined;
29
+ const order = Object.keys(template.props);
30
+ return [
31
+ ...twoWayBindingsOf(template.props).map((binding) => ({
32
+ prop: binding.prop,
33
+ ...(binding.defaultProp !== undefined
34
+ ? { defaultProp: binding.defaultProp }
35
+ : {}),
36
+ changeProp: binding.changeProp,
37
+ })),
38
+ ...relayedBindingsOf(template.props).map((binding) => ({
39
+ prop: binding.prop,
40
+ changeProp: binding.changeProp,
41
+ })),
42
+ ]
43
+ .sort((first, second) => order.indexOf(first.prop) - order.indexOf(second.prop))
44
+ .map(({ changeProp, ...binding }) => ({
45
+ ...binding,
46
+ ...(changeProp !== undefined ? { changeProp } : {}),
47
+ ...(vueModel === binding.prop ? { vueModel: true } : {}),
48
+ ...(template.typeParameters?.length ? { generic: true } : {}),
49
+ }));
50
+ }
51
+ /**
52
+ * The values a prop's scenarios should cover: each select option and each
53
+ * boolean value other than the prop's default (a select's `false` too only
54
+ * when the prop has one), for every included select and
55
+ * boolean control. Shared by scenario generation and the check's coverage.
56
+ */
57
+ export function scenarioValues(propTypes, defaultProps) {
58
+ const values = [];
59
+ for (const propType of propTypes) {
60
+ if (!propType.include)
61
+ continue;
62
+ const current = defaultProps[propType.name];
63
+ if (propType.control === 'boolean') {
64
+ values.push({ prop: propType.name, value: current !== true });
65
+ }
66
+ else if (propType.control === 'select') {
67
+ for (const option of propType.options ?? []) {
68
+ // As for a toggle, `false` is no departure from a prop left unset.
69
+ if (option !== undefined &&
70
+ option !== current &&
71
+ !(option === false && current === undefined))
72
+ values.push({ prop: propType.name, value: option });
73
+ }
74
+ }
75
+ }
76
+ return values;
77
+ }
78
+ /** Whether a story file's `generate` lets this prop generate scenarios. */
79
+ export function generatesProp(generate, prop) {
80
+ if (generate === undefined)
81
+ return true;
82
+ if (generate === false)
83
+ return false;
84
+ if (Array.isArray(generate))
85
+ return generate.includes(prop);
86
+ return !generate.exclude.includes(prop);
87
+ }
88
+ /** The keys a story file sets. */
89
+ const STORY_KEYS = [
90
+ 'omit',
91
+ 'standalone',
92
+ 'unlisted',
93
+ 'category',
94
+ 'label',
95
+ 'defaultProps',
96
+ 'controls',
97
+ 'fixed',
98
+ 'generate',
99
+ 'slots',
100
+ 'around',
101
+ 'scenarios',
102
+ 'options',
103
+ ];
104
+ /** What to write for a key a story file does not take, where one thing is meant. */
105
+ const STORY_KEY_REPLACEMENTS = new Map([
106
+ ['partial', 'standalone: false'],
107
+ ['description', 'description on the template'],
108
+ ]);
109
+ /** The keys a curated scenario may set. */
110
+ const CURATED_SCENARIO_KEYS = new Set([
111
+ 'id',
112
+ 'label',
113
+ 'description',
114
+ 'props',
115
+ 'slots',
116
+ 'options',
117
+ 'default',
118
+ ]);
119
+ /**
120
+ * The component of the first element fill, at any depth of a fill tree,
121
+ * that fills its default slot through `slots.default` rather than
122
+ * `children`, or `undefined` when none does.
123
+ */
124
+ function elementFillSettingDefaultSlot(fill) {
125
+ if (!fill || typeof fill !== 'object' || Array.isArray(fill))
126
+ return undefined;
127
+ if (fill.kind === 'element') {
128
+ if (fill.slots && 'default' in fill.slots)
129
+ return fill.component;
130
+ for (const nested of [...Object.values(fill.slots ?? {}), fill.children]) {
131
+ const found = elementFillSettingDefaultSlot(nested);
132
+ if (found !== undefined)
133
+ return found;
134
+ }
135
+ }
136
+ else if (fill.kind === 'html') {
137
+ return elementFillSettingDefaultSlot(fill.children);
138
+ }
139
+ else if (fill.kind === 'fragment') {
140
+ for (const child of fill.children ?? []) {
141
+ const found = elementFillSettingDefaultSlot(child);
142
+ if (found !== undefined)
143
+ return found;
144
+ }
145
+ }
146
+ return undefined;
147
+ }
148
+ /**
149
+ * The tag of the first html fill, at any depth of a fill tree, that gives a
150
+ * void element (img, input, br) children, or `undefined` when none does.
151
+ */
152
+ function voidElementWithChildren(fill) {
153
+ if (!fill || typeof fill !== 'object' || Array.isArray(fill))
154
+ return undefined;
155
+ if (fill.kind === 'html') {
156
+ if (fill.children !== undefined && isVoidElement(fill.tag))
157
+ return fill.tag;
158
+ return voidElementWithChildren(fill.children);
159
+ }
160
+ if (fill.kind === 'element') {
161
+ for (const nested of [...Object.values(fill.slots ?? {}), fill.children]) {
162
+ const found = voidElementWithChildren(nested);
163
+ if (found !== undefined)
164
+ return found;
165
+ }
166
+ }
167
+ else if (fill.kind === 'fragment') {
168
+ for (const child of fill.children ?? []) {
169
+ const found = voidElementWithChildren(child);
170
+ if (found !== undefined)
171
+ return found;
172
+ }
173
+ }
174
+ return undefined;
175
+ }
176
+ /** The id a generated scenario takes: `<prop>-<value>`, or the prop for a
177
+ * boolean turned on, unless that is the preview's own Empty scenario's id,
178
+ * which a boolean named `empty` takes as `empty-true`. A curated scenario
179
+ * with this id replaces it. */
180
+ export function generatedScenarioId(prop, value) {
181
+ if (value === true)
182
+ return prop === EMPTY_SCENARIO_ID ? `${prop}-true` : prop;
183
+ return `${prop}-${String(value)}`;
184
+ }
185
+ /** The default label of a generated scenario: `prop: value`, or the bare prop
186
+ * for a boolean turned on. */
187
+ function defaultGeneratedLabel(prop, value) {
188
+ return value === true ? prop : `${prop}: ${String(value)}`;
189
+ }
190
+ /** Whether a scenario sets exactly one prop, to this value. */
191
+ function setsOnly(scenario, prop, value) {
192
+ const entries = Object.entries(scenario.props ?? {});
193
+ return (entries.length === 1 && entries[0][0] === prop && entries[0][1] === value);
194
+ }
195
+ /**
196
+ * One scenario per value `scenarioValues` lists, narrowed by the story file's
197
+ * `generate`, skipping a value a curated scenario already sets alone, and any
198
+ * id a curated scenario already uses: that scenario replaces the generated one.
199
+ * Labeled by the kit's `generatedScenario`, or `prop: value` and the bare prop
200
+ * for a boolean turned on.
201
+ */
202
+ function generateScenarios(propTypes, defaultProps, story, labels) {
203
+ const generate = story?.generate;
204
+ if (generate === false)
205
+ return [];
206
+ const curated = story?.scenarios ?? [];
207
+ const takenIds = new Set(curated.map((scenario) => scenario.id));
208
+ const label = labels.generatedScenario ?? defaultGeneratedLabel;
209
+ const scenarios = [];
210
+ for (const { prop, value } of scenarioValues(propTypes, defaultProps)) {
211
+ if (!generatesProp(generate, prop))
212
+ continue;
213
+ if (curated.some((scenario) => setsOnly(scenario, prop, value)))
214
+ continue;
215
+ const id = generatedScenarioId(prop, value);
216
+ if (takenIds.has(id))
217
+ continue;
218
+ takenIds.add(id);
219
+ scenarios.push({
220
+ id,
221
+ label: label(prop, value),
222
+ props: { [prop]: value },
223
+ generated: true,
224
+ });
225
+ }
226
+ return scenarios;
227
+ }
228
+ /**
229
+ * Resolve the story of one component: its controls, default props,
230
+ * scenarios, slot content and the targets it renders in.
231
+ *
232
+ * @param component - The enumerated component.
233
+ * @param story - The component's story file, when it has one.
234
+ * @param hints - The kit's control hints.
235
+ * @param labels - The kit's preview labels.
236
+ * @param omitPattern - Component names matching it get no story.
237
+ * @param kitTargets - The scripted targets the kit builds; the component's own
238
+ * targets are narrowed to them.
239
+ * @returns The resolved story.
240
+ */
241
+ export function resolveStory(component, story, hints = {}, labels = {}, omitPattern, kitTargets = ALL_TARGETS) {
242
+ const omit = (story?.omit ?? false) ||
243
+ isStructurallyOmitted(component.name, omitPattern);
244
+ // A component that is not standalone (a compound's part, a shared building
245
+ // block) stays in the manifest for the editor but gets no story - set on the
246
+ // story file (templates) or the manual spec (hand-written components).
247
+ const standalone = (story?.standalone ?? true) && (component.standalone ?? true);
248
+ // An unlisted story is generated as any other, only left out of what a
249
+ // reader browses; a component with no story of its own has nothing to hide.
250
+ const unlisted = ((story?.unlisted ?? false) || (component.unlisted ?? false)) &&
251
+ !omit &&
252
+ standalone;
253
+ // propTypes from props, then story file control overrides (false disables).
254
+ const propTypes = [];
255
+ for (const [propName, def] of Object.entries(component.props)) {
256
+ let propType = propTypeForProp(component.name, propName, def, hints);
257
+ if (def.description)
258
+ propType = { ...propType, description: def.description };
259
+ const override = story?.controls?.[propName];
260
+ if (override === false)
261
+ propType = { ...propType, include: false };
262
+ else if (override)
263
+ propType = { ...propType, ...override };
264
+ // A fixed prop is always passed and never shown.
265
+ if (story?.fixed && propName in story.fixed)
266
+ propType = { ...propType, include: false };
267
+ propTypes.push({
268
+ ...propType,
269
+ declaredType: def.type,
270
+ required: def.required === true,
271
+ ...(def.default !== undefined ? { declaredDefault: def.default } : {}),
272
+ ...(def.documentedDefault !== undefined
273
+ ? { documentedDefault: def.documentedDefault }
274
+ : {}),
275
+ });
276
+ }
277
+ // default props: template defaults + size config, then story file defaultProps.
278
+ const defaultProps = {};
279
+ // The values the preview starts a prop from that a consumer never writes:
280
+ // a hint's, for a prop the component gives no default and the story file
281
+ // leaves alone.
282
+ const previewDefaults = {};
283
+ for (const [propName, def] of Object.entries(component.props)) {
284
+ const value = defaultPropForProp(component.name, propName, def, hints);
285
+ if (value !== undefined)
286
+ defaultProps[propName] = value;
287
+ if (value !== undefined &&
288
+ def.default === undefined &&
289
+ !(propName in (story?.defaultProps ?? {})) &&
290
+ !(propName in (story?.fixed ?? {})))
291
+ previewDefaults[propName] = value;
292
+ }
293
+ // A component with a default slot gets visible placeholder text - but only
294
+ // when the story file declares no slot plan at all. A story file that curates any
295
+ // slot (a named fill, an explicit default) owns the slot story: injecting
296
+ // children beside a named fill fabricates a mount no consumer writes.
297
+ // `slots: {}` forces a genuinely slotless mount. A default slot that
298
+ // exposes values gets none either: its content is a function of those
299
+ // values (React calls `children(scope)`), so text there is a mount no
300
+ // consumer writes, and the slot's fallback renders instead.
301
+ if (component.hasDefaultSlot &&
302
+ !component.defaultSlotScoped &&
303
+ !story?.slots &&
304
+ defaultProps.children === undefined) {
305
+ defaultProps.children = component.name;
306
+ // The preview's own, which a consumer's source leaves out, unless the
307
+ // story file gives the slot text of its own.
308
+ if (!('children' in (story?.defaultProps ?? {})) &&
309
+ !('children' in (story?.fixed ?? {})))
310
+ previewDefaults.children = component.name;
311
+ }
312
+ Object.assign(defaultProps, story?.defaultProps ?? {}, story?.fixed ?? {});
313
+ // A named slot spelled flat (`slots: { header: ... }` instead of
314
+ // `slots: { named: { header: ... } }`) would silently never render - the
315
+ // emitters read only `default` and `named`. Fail loudly instead. A
316
+ // scenario's own slots are a plan of the same shape.
317
+ const slotPlans = [
318
+ ['', story?.slots],
319
+ ...(story?.scenarios ?? [])
320
+ .filter((scenario) => scenario.generated !== true)
321
+ .map((scenario) => [
322
+ `scenario '${scenario.id}': `,
323
+ scenario.slots,
324
+ ]),
325
+ ];
326
+ for (const [where, plan] of slotPlans) {
327
+ if (plan === undefined)
328
+ continue;
329
+ if (typeof plan !== 'object' || plan === null || Array.isArray(plan)) {
330
+ throw new Error(`${component.name}: ${where}slots must be a slot plan, { default, named }`);
331
+ }
332
+ const unknownSlotKeys = Object.keys(plan).filter((key) => key !== 'default' && key !== 'named');
333
+ if (unknownSlotKeys.length > 0) {
334
+ throw new Error(`${component.name}: ${where}unknown slot-plan key(s) ${unknownSlotKeys.join(', ')} - ` +
335
+ `named slots go under "named": { slots: { named: { ... } } }.`);
336
+ }
337
+ }
338
+ const planFills = slotPlans.flatMap(([, plan]) => [
339
+ plan?.default,
340
+ ...Object.values(plan?.named ?? {}),
341
+ ]);
342
+ // `around` holds the content before and after the component, and a scoped
343
+ // fill has no slot there to give it values.
344
+ const around = story?.around;
345
+ if (around !== undefined) {
346
+ if (typeof around !== 'object' || around === null || Array.isArray(around))
347
+ throw new Error(`${component.name}: around must be { before, after }, each a fill`);
348
+ const unknownAroundKeys = Object.keys(around).filter((key) => key !== 'before' && key !== 'after');
349
+ if (unknownAroundKeys.length > 0)
350
+ throw new Error(`${component.name}: unknown around key(s) ${unknownAroundKeys.join(', ')} - around takes before and after`);
351
+ for (const [where, fill] of Object.entries(around)) {
352
+ if (fill?.kind === 'scoped')
353
+ throw new Error(`${component.name}: around.${where} is a scoped fill, which takes values from a slot; beside the component there is none`);
354
+ }
355
+ }
356
+ const aroundFills = [story?.around?.before, story?.around?.after];
357
+ // The composed component's default slot takes an element fill's
358
+ // `children`; `slots` names its named slots only. A `default` there would
359
+ // otherwise reach the emitters as a slot named `default`, which Svelte
360
+ // refuses as a reserved word.
361
+ for (const fill of [...planFills, ...aroundFills]) {
362
+ const filled = elementFillSettingDefaultSlot(fill);
363
+ if (filled !== undefined) {
364
+ throw new Error(`${component.name}: the element fill of '${filled}' sets slots.default - its default slot takes children`);
365
+ }
366
+ }
367
+ // A void element renders self-closed, so children on one would be dropped
368
+ // in one framework and refused by another's compiler.
369
+ for (const fill of [...planFills, ...aroundFills]) {
370
+ const tag = voidElementWithChildren(fill);
371
+ if (tag !== undefined) {
372
+ throw new Error(`${component.name}: the html fill <${tag}> has children - ${tag} is a void element and takes none`);
373
+ }
374
+ }
375
+ // A story file sets only what a story holds; a key the preview does not
376
+ // read would otherwise change nothing without a word.
377
+ const unknownStoryKey = Object.keys(story ?? {}).find((key) => !STORY_KEYS.includes(key));
378
+ if (unknownStoryKey !== undefined) {
379
+ const replacement = STORY_KEY_REPLACEMENTS.get(unknownStoryKey);
380
+ throw new Error(`${component.name}: story sets unknown key '${unknownStoryKey}' - ${replacement === undefined
381
+ ? `a story sets ${STORY_KEYS.join(', ')}`
382
+ : `use ${replacement}`}`);
383
+ }
384
+ // A curated scenario sets only what a scenario holds. The scenarios the
385
+ // preview generates itself reach the model beside them, marked
386
+ // `generated`, and are not the story file's to check.
387
+ for (const scenario of story?.scenarios ?? []) {
388
+ if (scenario.generated === true)
389
+ continue;
390
+ if (scenario.id === EMPTY_SCENARIO_ID)
391
+ throw new Error(`${component.name}: scenario id '${EMPTY_SCENARIO_ID}' is the preview's own Empty scenario's; give the scenario another id`);
392
+ const unknownKey = Object.keys(scenario).find((key) => !CURATED_SCENARIO_KEYS.has(key));
393
+ if (unknownKey !== undefined) {
394
+ throw new Error(`${component.name}: scenario '${scenario.id}' sets unknown key '${unknownKey}' - a scenario sets ${[...CURATED_SCENARIO_KEYS].join(', ')}`);
395
+ }
396
+ }
397
+ // A scenario's description is text for a shell to show, and its options
398
+ // are option values by id; anything else is a story file mistake that would
399
+ // otherwise reach the catalog unnoticed. Whether each names one of the
400
+ // kit's options is the check's to say, which reads the kit's options.
401
+ for (const scenario of story?.scenarios ?? []) {
402
+ if (scenario.description !== undefined &&
403
+ typeof scenario.description !== 'string') {
404
+ throw new Error(`${component.name}: scenario '${scenario.id}' has a description that is not a string.`);
405
+ }
406
+ const options = scenario.options;
407
+ if (options !== undefined &&
408
+ (typeof options !== 'object' ||
409
+ options === null ||
410
+ Array.isArray(options) ||
411
+ Object.values(options).some((value) => typeof value !== 'string' && typeof value !== 'boolean'))) {
412
+ throw new Error(`${component.name}: scenario '${scenario.id}' has options that are not option values by id, each a string or a boolean.`);
413
+ }
414
+ }
415
+ // slot components to import
416
+ const slotComponentSet = new Set();
417
+ try {
418
+ // A scenario's own content is imported with the story's: one module
419
+ // per target holds them all.
420
+ for (const fill of planFills)
421
+ collectSlotComponents(fill, slotComponentSet);
422
+ for (const fill of aroundFills)
423
+ collectSlotComponents(fill, slotComponentSet);
424
+ }
425
+ catch (error) {
426
+ // Name the component so a bad story file is findable without a bisect.
427
+ throw new Error(`${component.name}: ${error.message}`, {
428
+ cause: error,
429
+ });
430
+ }
431
+ return {
432
+ name: component.name,
433
+ ...(component.description !== undefined
434
+ ? { description: component.description }
435
+ : {}),
436
+ title: `${story?.category ?? labels.category ?? DEFAULT_CATEGORY}/${story?.label ?? component.name}`,
437
+ omit,
438
+ standalone,
439
+ unlisted,
440
+ propTypes,
441
+ events: propTypes
442
+ .filter((propType) => isEventType(propType.declaredType))
443
+ .map((propType) => propType.name),
444
+ bindings: bindingsOf(component),
445
+ defaultProps,
446
+ ...(Object.keys(previewDefaults).length > 0 ? { previewDefaults } : {}),
447
+ slots: story?.slots,
448
+ ...(story?.around ? { around: story.around } : {}),
449
+ scenarios: [
450
+ ...(story?.scenarios ?? []),
451
+ ...generateScenarios(propTypes, defaultProps, story, labels),
452
+ ],
453
+ slotComponents: [...slotComponentSet],
454
+ contentSlots: {
455
+ default: component.hasDefaultSlot,
456
+ named: component.slotNames,
457
+ ...(component.scopeRequiredSlots?.length
458
+ ? { scopeRequired: component.scopeRequiredSlots }
459
+ : {}),
460
+ },
461
+ options: story?.options,
462
+ targets: (component.targets ?? ALL_TARGETS).filter((target) => kitTargets.includes(target)),
463
+ };
464
+ }