@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,143 @@
1
+ // Parse the TypeScript-like `type` string a template's prop definition
2
+ // carries into a structured kind the propType mapper can switch on.
3
+
4
+ export type ParsedType =
5
+ | { kind: 'boolean' }
6
+ | { kind: 'number' }
7
+ | { kind: 'string' }
8
+ | { kind: 'stringOrNumber' }
9
+ | {
10
+ kind: 'enum';
11
+ /** The literals the select offers. */
12
+ options: (string | boolean)[];
13
+ /** Whether the type also takes any string (`string & {}` among its
14
+ * members), which a story file may set beside the literals. */
15
+ open?: boolean;
16
+ /** Whether the type also takes a structured value (an object, an
17
+ * array or a tuple among its members), which a story file may set
18
+ * beside the literals. */
19
+ structural?: boolean;
20
+ }
21
+ | { kind: 'complex'; raw: string };
22
+
23
+ /**
24
+ * Splits a type's text on its top-level `|`, leaving a `|` inside brackets,
25
+ * braces, parentheses, angle brackets or a template literal alone.
26
+ *
27
+ * @param type - The type as written.
28
+ * @returns Its union members, trimmed.
29
+ */
30
+ export function unionMembers(type: string): string[] {
31
+ const members: string[] = [];
32
+ let depth = 0;
33
+ let quote: string | undefined;
34
+ let current = '';
35
+ for (const character of type) {
36
+ if (quote) {
37
+ if (character === quote) quote = undefined;
38
+ } else if (character === "'" || character === '"' || character === '`') {
39
+ quote = character;
40
+ } else if ('([{<'.includes(character)) {
41
+ depth += 1;
42
+ } else if (')]}>'.includes(character)) {
43
+ depth -= 1;
44
+ } else if (character === '|' && depth === 0) {
45
+ members.push(current.trim());
46
+ current = '';
47
+ continue;
48
+ }
49
+ current += character;
50
+ }
51
+ members.push(current.trim());
52
+ return members.filter(Boolean);
53
+ }
54
+
55
+ /** A string literal in either quote style. */
56
+ const STRING_LITERAL = /^(['"])[^'"]*\1$/;
57
+
58
+ /** An open string member, `string & {}`: any string, the literals suggested. */
59
+ function isOpenString(member: string): boolean {
60
+ return /^\(?\s*string\s*&\s*\{\s*\}\s*\)?$/.test(member);
61
+ }
62
+
63
+ /** A member with structure a select cannot offer but need not: an object, an
64
+ * array or tuple, or a read-only array (a responsive value's breakpoint form). */
65
+ function isStructural(member: string): boolean {
66
+ return (
67
+ member.startsWith('{') ||
68
+ member.startsWith('[') ||
69
+ member.endsWith('[]') ||
70
+ member.startsWith('readonly ') ||
71
+ /^(Readonly)?Array</.test(member)
72
+ );
73
+ }
74
+
75
+ export function parseType(type: string): ParsedType {
76
+ // An optional union (`'a' | 'b' | undefined`, `string | null`) is controlled as
77
+ // its non-nullish members: the control's unset state already covers the rest.
78
+ const parts = unionMembers(type).filter(
79
+ (part) => part !== 'undefined' && part !== 'null'
80
+ );
81
+ const t = parts.join(' | ');
82
+ if (t === 'boolean' || t === 'true | false' || t === 'false | true')
83
+ return { kind: 'boolean' };
84
+ if (t === 'number') return { kind: 'number' };
85
+ if (t === 'string') return { kind: 'string' };
86
+
87
+ // `string | number` (primitives only, any order)
88
+ if (
89
+ parts.length > 1 &&
90
+ parts.every((part) => part === 'string' || part === 'number')
91
+ ) {
92
+ return { kind: 'stringOrNumber' };
93
+ }
94
+
95
+ // A union of string literals (either quote style, as a driver's source may
96
+ // spell it), e.g. 'soft' | 'solid', is a select of them. So is one that also
97
+ // takes any string (`string & {}`) or a structured form such as a responsive
98
+ // value's breakpoint list: the select offers the literals, and a story file sets
99
+ // the rest. Any other member - a plain `string`, `number`, `boolean` or `false`
100
+ // the select could not offer, or a name left unresolved - keeps it complex.
101
+ const literals = parts.filter((part) => STRING_LITERAL.test(part));
102
+ if (
103
+ literals.length > 0 &&
104
+ parts.every(
105
+ (part) =>
106
+ STRING_LITERAL.test(part) || isOpenString(part) || isStructural(part)
107
+ )
108
+ ) {
109
+ return {
110
+ kind: 'enum',
111
+ options: literals.map((part) => part.slice(1, -1)),
112
+ ...(parts.some(isOpenString) ? { open: true } : {}),
113
+ ...(parts.some(isStructural) ? { structural: true } : {}),
114
+ };
115
+ }
116
+
117
+ // A finite union of string literals and booleans, e.g. boolean |
118
+ // 'indeterminate', is a select of each: true, false, then the literals.
119
+ const booleans: string[] = parts.filter(
120
+ (part) => part === 'boolean' || part === 'true' || part === 'false'
121
+ );
122
+ if (
123
+ literals.length > 0 &&
124
+ booleans.length > 0 &&
125
+ parts.every((part) => STRING_LITERAL.test(part) || booleans.includes(part))
126
+ ) {
127
+ const values = new Set<boolean>(
128
+ booleans.flatMap((part) =>
129
+ part === 'boolean' ? [true, false] : [part === 'true']
130
+ )
131
+ );
132
+ return {
133
+ kind: 'enum',
134
+ options: [
135
+ ...[true, false].filter((value) => values.has(value)),
136
+ ...literals.map((part) => part.slice(1, -1)),
137
+ ],
138
+ };
139
+ }
140
+
141
+ // Record<...>, SomeInterface[], SelectOption[], opaque bags, etc.
142
+ return { kind: 'complex', raw: t };
143
+ }
@@ -0,0 +1,235 @@
1
+ // How React takes the attributes a story gives a component. A story spells
2
+ // an attribute as HTML does, the form every other target takes as it is;
3
+ // React takes `class` as `className`, a `style` string as a style object and
4
+ // the attributes it renames by their React names (`tabindex` as `tabIndex`,
5
+ // `for` as `htmlFor`), unless the component declares a prop by that exact
6
+ // name. Browser-safe, with no imports of its own: the React pane, the React
7
+ // registry and the printed React source share it, and the names React gives
8
+ // the attributes it renames are handed in (the React extension's table).
9
+
10
+ import type { SlotFill } from './types.js';
11
+
12
+ /**
13
+ * A plain-CSS `style` string's declarations, split at each `;` outside a
14
+ * quoted string and outside parentheses, so a `url("data:...;base64,...")`
15
+ * stays whole.
16
+ *
17
+ * @param css - The declarations as a `style` attribute holds them.
18
+ * @returns Each declaration's text, as written.
19
+ */
20
+ export function styleDeclarations(css: string): string[] {
21
+ const declarations: string[] = [];
22
+ let quote = '';
23
+ let depth = 0;
24
+ let start = 0;
25
+ for (let i = 0; i < css.length; i++) {
26
+ const character = css[i];
27
+ if (quote) {
28
+ if (character === '\\') i++;
29
+ else if (character === quote) quote = '';
30
+ } else if (character === '"' || character === "'") quote = character;
31
+ else if (character === '(') depth++;
32
+ else if (character === ')') depth = Math.max(0, depth - 1);
33
+ else if (character === ';' && depth === 0) {
34
+ declarations.push(css.slice(start, i));
35
+ start = i + 1;
36
+ }
37
+ }
38
+ declarations.push(css.slice(start));
39
+ return declarations;
40
+ }
41
+
42
+ /** A CSS property's name in a React style object: camel-cased, a vendor
43
+ * prefix capitalized (`WebkitLineClamp`) but for `-ms-` (`msOverflowStyle`),
44
+ * a custom property (`--gap`) as written. */
45
+ function stylePropertyName(property: string): string {
46
+ if (property.startsWith('--')) return property;
47
+ const unprefixed = property.startsWith('-ms-') ? property.slice(1) : property;
48
+ return unprefixed.replace(/-([a-z])/g, (_, letter: string) =>
49
+ letter.toUpperCase()
50
+ );
51
+ }
52
+
53
+ /**
54
+ * A plain-CSS `style` string as the properties of a React style object:
55
+ * each property camel-cased (a vendor prefix capitalized, `-ms-` as `ms`), a
56
+ * custom property (`--gap`) kept as written. A `;` inside a quoted string or
57
+ * parentheses stays in its value. A React style object has no
58
+ * `!important`, so a value marked with it is kept without the mark: the
59
+ * browser would drop the whole declaration otherwise.
60
+ *
61
+ * @param css - The declarations, `;`-separated.
62
+ * @returns The style object's properties, in their order.
63
+ * @example
64
+ * ```ts
65
+ * styleProperties('padding-inline: 1ch; --gap: 2px'); // { paddingInline: '1ch', '--gap': '2px' }
66
+ * ```
67
+ */
68
+ export function styleProperties(css: string): Record<string, string> {
69
+ const properties: Record<string, string> = {};
70
+ for (const declaration of styleDeclarations(css)) {
71
+ const colon = declaration.indexOf(':');
72
+ if (colon === -1) continue;
73
+ const property = declaration.slice(0, colon).trim();
74
+ if (!property) continue;
75
+ properties[stylePropertyName(property)] = declaration
76
+ .slice(colon + 1)
77
+ .trim()
78
+ .replace(/\s*!\s*important$/i, '');
79
+ }
80
+ return properties;
81
+ }
82
+
83
+ /** Two classes given under both `class` and `className`, joined. */
84
+ function joinedClassName<Value>(
85
+ previous: Value | Record<string, string> | undefined,
86
+ next: Value
87
+ ): Value | string {
88
+ if (previous === undefined || previous === null || previous === '')
89
+ return next;
90
+ if (next === undefined || next === null || (next as unknown) === '')
91
+ return previous as Value;
92
+ return `${String(previous)} ${String(next)}`;
93
+ }
94
+
95
+ /**
96
+ * A component's props as React takes them: `class` as `className`, a
97
+ * `style` string as a style object, and each attribute React renames by its
98
+ * React name, unless the component declares a prop by that exact name, which
99
+ * it takes as it is. Any other prop passes unchanged. A story that gives both
100
+ * `class` and `className` gets the two joined, in their order.
101
+ *
102
+ * @param props - The props, as the story gives them.
103
+ * @param declares - Whether the component declares a prop of a name.
104
+ * @param attributeNames - The React name of each attribute React renames,
105
+ * by its HTML name.
106
+ * @returns The props, spelled as React takes them, in their order.
107
+ * @example
108
+ * ```ts
109
+ * reactProps({ class: 'w-48', tabindex: 0 }, () => false, { tabindex: 'tabIndex' });
110
+ * // { className: 'w-48', tabIndex: 0 }
111
+ * ```
112
+ */
113
+ export function reactProps<Value>(
114
+ props: Record<string, Value>,
115
+ declares: (name: string) => boolean,
116
+ attributeNames: Readonly<Record<string, string>>
117
+ ): Record<string, Value | Record<string, string> | string> {
118
+ const spelled: Record<string, Value | Record<string, string> | string> = {};
119
+ for (const [name, value] of Object.entries(props)) {
120
+ if (declares(name)) spelled[name] = value;
121
+ else if (name === 'class' || name === 'className')
122
+ spelled.className = joinedClassName(spelled.className, value);
123
+ else if (name === 'style' && typeof value === 'string')
124
+ spelled.style = styleProperties(value);
125
+ else
126
+ spelled[
127
+ Object.prototype.hasOwnProperty.call(attributeNames, name)
128
+ ? attributeNames[name]
129
+ : name
130
+ ] = value;
131
+ }
132
+ return spelled;
133
+ }
134
+
135
+ /**
136
+ * A slot fill as React takes it: each component's props in it spelled by
137
+ * {@link reactProps}, and each attribute of an html element React renames by
138
+ * its React name (`for` as `htmlFor`). An html element's `class` and `style`
139
+ * stay as written, which the React emitters spell themselves, and opaque
140
+ * bodies stay as they are.
141
+ *
142
+ * @param fill - The fill.
143
+ * @param declares - Whether a component declares a prop of a name.
144
+ * @param attributeNames - The React name of each attribute React renames.
145
+ * @returns The fill, every component's attributes spelled as React takes them.
146
+ */
147
+ export function reactFill<Fill extends SlotFill | undefined>(
148
+ fill: Fill,
149
+ declares: (component: string, name: string) => boolean,
150
+ attributeNames: Readonly<Record<string, string>>
151
+ ): Fill {
152
+ return fill && (reactFillOf(fill, declares, attributeNames) as Fill);
153
+ }
154
+
155
+ /** An html element's attributes, each React renames by its React name
156
+ * (`for` as `htmlFor`), in their order. `class` and `style` stay as
157
+ * written: the React emitters spell those on an element themselves. */
158
+ function renamedAttributes<Value>(
159
+ attributes: Record<string, Value>,
160
+ attributeNames: Readonly<Record<string, string>>
161
+ ): Record<string, Value> {
162
+ return Object.fromEntries(
163
+ Object.entries(attributes).map(([name, value]) => [
164
+ Object.prototype.hasOwnProperty.call(attributeNames, name)
165
+ ? attributeNames[name]
166
+ : name,
167
+ value,
168
+ ])
169
+ );
170
+ }
171
+
172
+ function reactFillOf(
173
+ fill: SlotFill,
174
+ declares: (component: string, name: string) => boolean,
175
+ attributeNames: Readonly<Record<string, string>>
176
+ ): SlotFill {
177
+ const spell = (child: SlotFill): SlotFill =>
178
+ reactFillOf(child, declares, attributeNames);
179
+ if (fill.kind === 'element')
180
+ return {
181
+ ...fill,
182
+ ...(fill.props
183
+ ? {
184
+ props: reactProps(
185
+ fill.props,
186
+ (name) => declares(fill.component, name),
187
+ attributeNames
188
+ ),
189
+ }
190
+ : {}),
191
+ ...(fill.slots
192
+ ? {
193
+ slots: Object.fromEntries(
194
+ Object.entries(fill.slots).map(([slot, slotFill]) => [
195
+ slot,
196
+ spell(slotFill),
197
+ ])
198
+ ),
199
+ }
200
+ : {}),
201
+ ...(fill.children ? { children: spell(fill.children) } : {}),
202
+ };
203
+ if (fill.kind === 'html')
204
+ return {
205
+ ...fill,
206
+ ...(fill.props
207
+ ? { props: renamedAttributes(fill.props, attributeNames) }
208
+ : {}),
209
+ ...(fill.children ? { children: spell(fill.children) } : {}),
210
+ };
211
+ if (fill.kind === 'fragment')
212
+ return { ...fill, children: fill.children.map(spell) };
213
+ return fill;
214
+ }
215
+
216
+ /**
217
+ * The names a component declares that {@link reactProps} would otherwise
218
+ * respell: `class`, `style` and the attributes React renames. A component
219
+ * keeps these as they are in React.
220
+ *
221
+ * @param declared - The component's declared prop names.
222
+ * @param attributeNames - The React name of each attribute React renames.
223
+ * @returns Those of `declared` React would otherwise respell.
224
+ */
225
+ export function declaredAttributes(
226
+ declared: Iterable<string>,
227
+ attributeNames: Readonly<Record<string, string>>
228
+ ): string[] {
229
+ return [...declared].filter(
230
+ (name) =>
231
+ name === 'class' ||
232
+ name === 'style' ||
233
+ Object.prototype.hasOwnProperty.call(attributeNames, name)
234
+ );
235
+ }
@@ -0,0 +1,97 @@
1
+ // A scenario with slot content of its own: the plan it shows, and the story
2
+ // the generator renders for it beside the component's. The panes render a
3
+ // story's slot content as it was generated and take only props at run time,
4
+ // so such a scenario is a story of its own in every target's registry.
5
+ // Browser-safe: no node imports.
6
+
7
+ import { sanitize, scenarioStoryId } from './naming.js';
8
+ import type { ResolvedStory, Scenario, SlotPlan } from './types.js';
9
+
10
+ /**
11
+ * The slot plan one scenario of a story shows: the scenario's own fills over
12
+ * the story's, slot by slot. A scenario with no slots of its own shows the
13
+ * story's plan.
14
+ *
15
+ * @param story - The story's plan, if it has one.
16
+ * @param scenario - The scenario's own plan, if it has one.
17
+ * @returns The plan the scenario shows, or `undefined` when neither has one.
18
+ */
19
+ export function scenarioSlotPlan(
20
+ story: SlotPlan | undefined,
21
+ scenario: SlotPlan | undefined
22
+ ): SlotPlan | undefined {
23
+ if (scenario === undefined) return story;
24
+ const named = { ...(story?.named ?? {}), ...(scenario.named ?? {}) };
25
+ const fill = scenario.default ?? story?.default;
26
+ return {
27
+ ...(fill === undefined ? {} : { default: fill }),
28
+ ...(Object.keys(named).length === 0 ? {} : { named }),
29
+ };
30
+ }
31
+
32
+ /**
33
+ * A resolved story as one of its scenarios shows it: with the scenario's
34
+ * slot plan over the story's. A scenario with no slots of its own, or no
35
+ * scenario, shows the story as it is.
36
+ *
37
+ * @param model - The component's resolved story.
38
+ * @param scenario - The scenario, when one is meant.
39
+ * @returns The story to print or render for that scenario.
40
+ */
41
+ export function storyOnScenario(
42
+ model: ResolvedStory,
43
+ scenario: Scenario | undefined
44
+ ): ResolvedStory {
45
+ return scenario?.slots === undefined
46
+ ? model
47
+ : { ...model, slots: scenarioSlotPlan(model.slots, scenario.slots) };
48
+ }
49
+
50
+ /**
51
+ * The stories of a component's scenarios that have slot content of their
52
+ * own: one per such scenario, the component's story with the scenario's
53
+ * plan, under the id the panes mount it by. The panes render a story's slot
54
+ * content as it was generated, so a scenario that changes it is a story
55
+ * beside the component's.
56
+ *
57
+ * @param model - The component's resolved story.
58
+ * @returns The scenario stories, in the scenarios' order; none for a
59
+ * component whose scenarios set props alone.
60
+ */
61
+ export function scenarioStories(model: ResolvedStory): ResolvedStory[] {
62
+ const componentId = sanitize(model.title);
63
+ return model.scenarios.flatMap((scenario, index) => {
64
+ if (scenario.slots === undefined) return [];
65
+ const slots = scenarioSlotPlan(model.slots, scenario.slots);
66
+ // The placeholder text a component with no plan starts from is the
67
+ // preview's own; a scenario that fills the default slot replaces it.
68
+ const withoutPlaceholder = Object.fromEntries(
69
+ Object.entries(model.defaultProps).filter(([name]) => name !== 'children')
70
+ );
71
+ const fillsDefault =
72
+ slots?.default !== undefined &&
73
+ model.previewDefaults?.children !== undefined;
74
+ return [
75
+ {
76
+ ...model,
77
+ ...(fillsDefault ? { defaultProps: withoutPlaceholder } : {}),
78
+ slots,
79
+ scenarioStory: {
80
+ id: scenarioStoryId(componentId, scenario.id),
81
+ renderName: `${model.name}_scenario${index}`,
82
+ },
83
+ },
84
+ ];
85
+ });
86
+ }
87
+
88
+ /**
89
+ * The id a pane mounts a resolved story by: a scenario story's own, or the
90
+ * one derived from the title.
91
+ *
92
+ * @param model - The resolved story.
93
+ * @returns Its id in every target's registry.
94
+ */
95
+ export function storyRegistryId(model: ResolvedStory): string {
96
+ return model.scenarioStory?.id ?? sanitize(model.title);
97
+ }
@@ -0,0 +1,102 @@
1
+ // Component references in a slot fill tree, collected so a generated registry
2
+ // imports them. Kept dependency-free in lib/ so both the generator (model.ts)
3
+ // and the dev middleware's scratch regeneration (emit/scratch.ts) share it without the
4
+ // middleware pulling in the generation-time story file graph.
5
+
6
+ import type { PartFill, SlotFill } from './types.js';
7
+
8
+ const SLOT_FILL_KINDS = new Set<SlotFill['kind']>([
9
+ 'text',
10
+ 'element',
11
+ 'html',
12
+ 'fragment',
13
+ 'raw',
14
+ 'scoped',
15
+ 'part',
16
+ ]);
17
+
18
+ /** Adds every component name a fill tree references to `into`: each `element`'s
19
+ * component, and the declared `imports` of an opaque `raw` or `scoped` body
20
+ * or a part's, unless `parts` is false. */
21
+ export function collectSlotComponents(
22
+ fill: SlotFill | undefined,
23
+ into: Set<string>,
24
+ parts = true
25
+ ): void {
26
+ if (!fill) return;
27
+ // Fail loudly on a malformed fill rather than letting it flow downstream, where
28
+ // an emitter silently generates `<undefined>`. The most common slip is an array
29
+ // where a single SlotFill is expected - multiple children need a fragment.
30
+ if (
31
+ typeof fill !== 'object' ||
32
+ Array.isArray(fill) ||
33
+ !SLOT_FILL_KINDS.has((fill as SlotFill).kind)
34
+ ) {
35
+ throw new Error(
36
+ `Invalid slot fill: expected a SlotFill object with a known "kind", got ${
37
+ Array.isArray(fill)
38
+ ? 'an array - wrap multiple children in { kind: "fragment", children: [...] }'
39
+ : JSON.stringify(fill)
40
+ }.`
41
+ );
42
+ }
43
+ if (fill.kind === 'element') {
44
+ into.add(fill.component);
45
+ for (const slotFill of Object.values(fill.slots ?? {}))
46
+ collectSlotComponents(slotFill, into, parts);
47
+ collectSlotComponents(fill.children, into, parts);
48
+ } else if (fill.kind === 'html') {
49
+ collectSlotComponents(fill.children, into, parts);
50
+ } else if (fill.kind === 'fragment') {
51
+ for (const child of fill.children)
52
+ collectSlotComponents(child, into, parts);
53
+ } else if (
54
+ fill.kind === 'raw' ||
55
+ fill.kind === 'scoped' ||
56
+ (fill.kind === 'part' && parts)
57
+ ) {
58
+ // A raw/scoped/part body is opaque, so it declares its component references explicitly.
59
+ for (const name of fill.imports ?? []) into.add(name);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * The parts a story's slot content calls, by name, in the order first called.
65
+ * A part is called in the content's tree; a call inside a body is the body's
66
+ * own markup and declares nothing.
67
+ *
68
+ * @param fills - The story's slot fills: its default slot and named slots.
69
+ * @returns Each part's call, carrying its declaration.
70
+ * @throws When two calls of one name declare different parts.
71
+ */
72
+ export function collectParts(
73
+ fills: Iterable<SlotFill | undefined>
74
+ ): Map<string, PartFill> {
75
+ const parts = new Map<string, PartFill>();
76
+ const visit = (fill: SlotFill | undefined): void => {
77
+ if (!fill) return;
78
+ if (fill.kind === 'element') {
79
+ for (const slotFill of Object.values(fill.slots ?? {})) visit(slotFill);
80
+ visit(fill.children);
81
+ } else if (fill.kind === 'html') {
82
+ visit(fill.children);
83
+ } else if (fill.kind === 'fragment') {
84
+ fill.children.forEach(visit);
85
+ } else if (fill.kind === 'part') {
86
+ const known = parts.get(fill.name);
87
+ if (!known) parts.set(fill.name, fill);
88
+ else if (declarationOf(known) !== declarationOf(fill))
89
+ throw new Error(
90
+ `Two calls of the part ${fill.name} declare it differently; every call carries the same declaration.`
91
+ );
92
+ }
93
+ };
94
+ for (const fill of fills) visit(fill);
95
+ return parts;
96
+ }
97
+
98
+ /** A part's declaration, without the props one call passes, as a key. */
99
+ function declarationOf(part: PartFill): string {
100
+ const { props: _props, ...declaration } = part;
101
+ return JSON.stringify(declaration);
102
+ }
@@ -0,0 +1,31 @@
1
+ // Which files in a templates directory are component templates: the rule
2
+ // the kit's build reads `src/components/` by, so the preview shows a story
3
+ // for every component the build emits.
4
+
5
+ /** The extensions of a component template file, as the kit's build reads
6
+ * them: JSON, TypeScript and JavaScript in each module form. */
7
+ export const TEMPLATE_FILE_EXTENSIONS = [
8
+ '.json',
9
+ '.ts',
10
+ '.js',
11
+ '.mjs',
12
+ '.cjs',
13
+ ] as const;
14
+
15
+ /**
16
+ * Whether a file name is a component template's: one of
17
+ * {@link TEMPLATE_FILE_EXTENSIONS}, a declaration file (`.d.ts`) left out.
18
+ *
19
+ * @param fileName - The file's name or path.
20
+ * @returns Whether the kit's build reads it as a template.
21
+ * @example
22
+ * isTemplateFile('badge.json'); // true
23
+ * isTemplateFile('badge.d.ts'); // false
24
+ */
25
+ export function isTemplateFile(fileName: string): boolean {
26
+ return (
27
+ TEMPLATE_FILE_EXTENSIONS.some((extension) =>
28
+ fileName.endsWith(extension)
29
+ ) && !fileName.endsWith('.d.ts')
30
+ );
31
+ }