@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,783 @@
1
+ // Load and validate the kit configuration's `preview` block. The build leaves
2
+ // the block alone - this package owns it: the kit configuration is found and
3
+ // read by scaffold-ui-kit's own resolver (`scaffold-ui-kit.ts` or
4
+ // `scaffold-ui-kit.json`), and the block is validated by hand (the errors
5
+ // name the offending key) and resolved into absolute paths. Kit-authored TS
6
+ // modules (templates, story files, manual specs, views) are imported through
7
+ // jiti, so the built CLI compiles them on the fly as the build compiles
8
+ // templates.
9
+ import { existsSync, readFileSync, realpathSync } from 'node:fs';
10
+ import { createRequire } from 'node:module';
11
+ import { dirname, isAbsolute, resolve } from 'node:path';
12
+ import { createJiti } from 'jiti';
13
+ import { ALL_TARGETS, PREVIEW_BROWSER_KEYS, } from './lib/types.js';
14
+ import { isPaneWidth, MAXIMUM_PANE_WIDTH } from './controller/persistence.js';
15
+ function fail(message) {
16
+ throw new Error(`[preview] ${message}`);
17
+ }
18
+ /** The keys the `preview` block takes. */
19
+ const PREVIEW_KEYS = [
20
+ 'shell',
21
+ 'sources',
22
+ 'options',
23
+ 'canvas',
24
+ 'themes',
25
+ 'mount',
26
+ 'viewCanvas',
27
+ 'paneWidths',
28
+ 'controlHints',
29
+ 'labels',
30
+ 'viewsModule',
31
+ 'codeBlock',
32
+ 'setupModules',
33
+ 'htmlStylesheets',
34
+ 'aliases',
35
+ 'scssLoadPaths',
36
+ 'optimizeDepsExclude',
37
+ 'dedupe',
38
+ 'omitPattern',
39
+ 'handWrittenComponents',
40
+ 'port',
41
+ 'host',
42
+ 'persistence',
43
+ 'landing',
44
+ ];
45
+ /** The keys a `preview.sources` entry takes. */
46
+ const SOURCE_KEYS = [
47
+ 'id',
48
+ 'label',
49
+ 'order',
50
+ 'moduleBase',
51
+ 'templatesDirectory',
52
+ 'manualModule',
53
+ 'storiesDirectory',
54
+ ];
55
+ /** The keys `preview.codeBlock` takes. */
56
+ const CODE_BLOCK_KEYS = [
57
+ 'module',
58
+ 'framework',
59
+ ];
60
+ /** The keys `preview.landing` takes. */
61
+ const LANDING_KEYS = ['view', 'component', 'scenario'];
62
+ /** The keys the object form of a `handWrittenComponents` entry takes. */
63
+ const HAND_WRITTEN_KEYS = [
64
+ 'template',
65
+ 'slots',
66
+ ];
67
+ /** What to write for a key the `preview` block does not take, where one thing is meant. */
68
+ const PREVIEW_KEY_REPLACEMENTS = new Map([
69
+ ['scopes', "'sources'"],
70
+ [
71
+ 'configurationModule',
72
+ "the keys of the 'preview' block, such as options, canvas, themes and mount",
73
+ ],
74
+ ]);
75
+ /** What to write for a key a `preview.sources` entry does not take, where one thing is meant. */
76
+ const SOURCE_KEY_REPLACEMENTS = new Map([
77
+ ['sidecarsDirectory', "'storiesDirectory'"],
78
+ ]);
79
+ /** Whether a value is a plain object, whose keys can be checked. */
80
+ function isPlainObject(value) {
81
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
82
+ }
83
+ /**
84
+ * Each key of an object that is not among the keys it takes, with its path
85
+ * and the keys the object takes, as the scaffolder reports the
86
+ * configuration's own keys, or what to use instead where `replacements`
87
+ * names it.
88
+ */
89
+ function unknownKeyProblems(value, allowed, path, description, replacements = new Map()) {
90
+ return Object.keys(value)
91
+ .filter((key) => !allowed.includes(key))
92
+ .map((key) => {
93
+ const replacement = replacements.get(key);
94
+ return `unknown key '${path}.${key}'; ${replacement === undefined
95
+ ? `${description} takes ${allowed.join(', ')}`
96
+ : `use ${replacement}`}`;
97
+ });
98
+ }
99
+ /**
100
+ * What is wrong with a list of pane widths, or nothing: it must be a list of
101
+ * `{ label, width }`, each label a non-empty string and each width a whole
102
+ * number of CSS pixels from 1 to the most a pane is laid out at, no label
103
+ * and no width named twice.
104
+ *
105
+ * @param paneWidths - The list, as the kit gives it.
106
+ * @param name - Where the kit gives it, for the message.
107
+ * @returns The problem, or `undefined` when the list is sound.
108
+ * @example
109
+ * ```ts
110
+ * paneWidthsProblem([{ label: 'small', width: 375 }], 'paneWidths'); // undefined
111
+ * ```
112
+ */
113
+ export function paneWidthsProblem(paneWidths, name) {
114
+ if (!Array.isArray(paneWidths) ||
115
+ paneWidths.some((paneWidth) => !isPlainObject(paneWidth) ||
116
+ typeof paneWidth.label !== 'string' ||
117
+ paneWidth.label.trim() === '' ||
118
+ !isPaneWidth(paneWidth.width)))
119
+ return `${name} must be a list of { label, width }: a label to pick it by, and a width in CSS pixels, a whole number from 1 to ${MAXIMUM_PANE_WIDTH}`;
120
+ const widths = paneWidths;
121
+ if (new Set(widths.map((paneWidth) => paneWidth.label)).size !== widths.length)
122
+ return `${name} must not name a label twice`;
123
+ if (new Set(widths.map((paneWidth) => paneWidth.width)).size !== widths.length)
124
+ return `${name} must not name a width twice`;
125
+ return undefined;
126
+ }
127
+ /** What is wrong with the shape of `preview.landing`, or nothing. */
128
+ function landingShapeProblem(landing) {
129
+ const shape = "preview.landing must be { view } or { component, scenario? }: a view's id, or a story's id and, if it is not to open on its baseline, a scenario's, as the preview's address names them";
130
+ if (!isPlainObject(landing))
131
+ return shape;
132
+ const candidate = landing;
133
+ const named = (key) => typeof candidate[key] === 'string' && candidate[key] !== '';
134
+ if ('view' in candidate) {
135
+ if (!named('view') || 'component' in candidate || 'scenario' in candidate)
136
+ return shape;
137
+ return undefined;
138
+ }
139
+ if (!named('component'))
140
+ return shape;
141
+ if ('scenario' in candidate && !named('scenario'))
142
+ return shape;
143
+ return undefined;
144
+ }
145
+ function validateSource(source, index) {
146
+ const candidate = source;
147
+ if (!candidate || typeof candidate !== 'object')
148
+ fail(`preview.sources[${index}] must be an object`);
149
+ for (const key of ['id', 'label', 'moduleBase']) {
150
+ if (typeof candidate[key] !== 'string' || !candidate[key]) {
151
+ fail(`preview.sources[${index}].${key} must be a non-empty string`);
152
+ }
153
+ }
154
+ if (typeof candidate.order !== 'number')
155
+ fail(`preview.sources[${index}].order must be a number`);
156
+ return candidate;
157
+ }
158
+ /**
159
+ * What is wrong with a `preview.shell` that is set, or `undefined` when it
160
+ * is a specifier or a `{ module, page }` of specifiers.
161
+ */
162
+ function shellSettingProblem(setting) {
163
+ if (typeof setting === 'string')
164
+ return undefined;
165
+ const shape = "preview.shell must be a package specifier or a module path, or an object with 'module' and optional 'page'";
166
+ if (typeof setting !== 'object' || setting === null || Array.isArray(setting))
167
+ return shape;
168
+ const { module, page, ...rest } = setting;
169
+ const unknown = Object.keys(rest);
170
+ if (unknown.length > 0)
171
+ return `unknown key 'preview.shell.${unknown[0]}'; preview.shell takes module and page`;
172
+ if (typeof module !== 'string' || !module)
173
+ return "preview.shell.module is required: the shell's module, as a package specifier or a module path relative to the kit root";
174
+ if (page !== undefined && (typeof page !== 'string' || !page))
175
+ return "preview.shell.page must be a package specifier or a module path relative to the kit root: the shell's page module";
176
+ return undefined;
177
+ }
178
+ /** The shell shipped with the preview, named when a kit configures none. */
179
+ export const CHARCELL_SHELL = '@scaffold-ui-kit/preview/shells/charcell';
180
+ /**
181
+ * Where `preview.shell` points: a module in the kit when it is
182
+ * written as a path (relative, absolute, or one that exists under the kit
183
+ * root, like the other module settings), else a package resolved from the
184
+ * kit's root the way the kit's own imports are.
185
+ */
186
+ function resolveShellModule(kitRoot, shell) {
187
+ if (isModulePath(kitRoot, shell)) {
188
+ const inKit = resolve(kitRoot, shell);
189
+ if (!existsSync(inKit)) {
190
+ fail(`preview.shell names a module that does not exist: ${inKit}`);
191
+ }
192
+ return inKit;
193
+ }
194
+ try {
195
+ return createRequire(resolve(kitRoot, 'package.json')).resolve(shell);
196
+ }
197
+ catch {
198
+ return fail(`preview.shell '${shell}' is neither a module in the kit nor a package installed in it (resolved from ${kitRoot}). ` +
199
+ `Install the package the shell comes from, or give a path relative to the kit root. ` +
200
+ `The shell shipped with the preview is '${CHARCELL_SHELL}'; it needs charcell and @zag-js/react installed.`);
201
+ }
202
+ }
203
+ /** Whether a module setting is written as a path: relative, absolute, or
204
+ * one that exists under the kit root. Anything else is a package specifier. */
205
+ function isModulePath(kitRoot, setting) {
206
+ return (setting.startsWith('.') ||
207
+ isAbsolute(setting) ||
208
+ existsSync(resolve(kitRoot, setting)));
209
+ }
210
+ /**
211
+ * Whether the package a specifier names lists `<specifier>/<subpath>` among
212
+ * its exports, read from its `package.json`; `false` when the package or its
213
+ * exports cannot be read.
214
+ */
215
+ function packageExports(fromKit, specifier, subpath) {
216
+ const [scopeOrName, name] = specifier.split('/');
217
+ const packageName = specifier.startsWith('@')
218
+ ? `${scopeOrName}/${name}`
219
+ : scopeOrName;
220
+ try {
221
+ const manifest = fromKit(`${packageName}/package.json`);
222
+ const key = `.${specifier.slice(packageName.length)}/${subpath}`;
223
+ return (typeof manifest.exports === 'object' &&
224
+ manifest.exports !== null &&
225
+ key in manifest.exports);
226
+ }
227
+ catch {
228
+ return false;
229
+ }
230
+ }
231
+ /**
232
+ * Where the shell's page module is: the one `preview.shell.page` names, a
233
+ * path in the kit or a package resolved from the kit's root, as the shell
234
+ * itself is; or, with none named, the module at the shell's specifier
235
+ * followed by `/page`, when the shell is named as a package specifier and
236
+ * its package has one. A shell with no page has none, and the host page
237
+ * starts empty.
238
+ *
239
+ * @param kitRoot - The kit's root directory.
240
+ * @param shell - The shell module, as `preview.shell` names it.
241
+ * @param page - The page module, as `preview.shell.page` names it.
242
+ * @returns The page module's absolute path, or `undefined` with no page.
243
+ */
244
+ function resolveShellPage(kitRoot, shell, page) {
245
+ const fromKit = createRequire(resolve(kitRoot, 'package.json'));
246
+ if (page === undefined) {
247
+ if (isModulePath(kitRoot, shell))
248
+ return undefined;
249
+ try {
250
+ return fromKit.resolve(`${shell}/page`);
251
+ }
252
+ catch (error) {
253
+ // A package that exports no page has none; one whose export cannot
254
+ // be loaded is a mistake worth naming.
255
+ const code = error.code;
256
+ if (code === 'ERR_PACKAGE_PATH_NOT_EXPORTED' ||
257
+ (code === 'MODULE_NOT_FOUND' && !packageExports(fromKit, shell, 'page'))) {
258
+ return undefined;
259
+ }
260
+ return fail(`the shell '${shell}' exports a page at '${shell}/page' that does not resolve: ${error.message}`);
261
+ }
262
+ }
263
+ if (isModulePath(kitRoot, page)) {
264
+ const inKit = resolve(kitRoot, page);
265
+ if (!existsSync(inKit)) {
266
+ fail(`preview.shell.page names a module that does not exist: ${inKit}`);
267
+ }
268
+ return inKit;
269
+ }
270
+ try {
271
+ return fromKit.resolve(page);
272
+ }
273
+ catch {
274
+ return fail(`preview.shell.page '${page}' is neither a module in the kit nor a package installed in it (resolved from ${kitRoot}). ` +
275
+ `Give a path relative to the kit root, or the page module of an installed shell, such as '${CHARCELL_SHELL}/page'.`);
276
+ }
277
+ }
278
+ /** Whether a module's source imports the shell shipped with the preview. */
279
+ const IMPORTS_CHARCELL_SHELL = new RegExp(`(?:from\\s*|import\\s*\\(\\s*|require\\s*\\(\\s*)['"]${CHARCELL_SHELL.replace(/\./g, '\\.')}['"]`);
280
+ /**
281
+ * Where the shell shipped with the preview is, when the kit's shell is it:
282
+ * the shell module itself when `preview.shell` names it, or the module it
283
+ * resolves to from a module of the kit's that imports it to configure it.
284
+ * `undefined` under a shell of the kit's own, or when the kit's module
285
+ * imports it from a package the kit has not installed.
286
+ */
287
+ function resolveCharcellShell(shell, shellModuleAbsolute) {
288
+ if (shell === CHARCELL_SHELL)
289
+ return shellModuleAbsolute;
290
+ let source;
291
+ try {
292
+ source = readFileSync(shellModuleAbsolute, 'utf8');
293
+ }
294
+ catch {
295
+ return undefined;
296
+ }
297
+ if (!IMPORTS_CHARCELL_SHELL.test(source))
298
+ return undefined;
299
+ try {
300
+ return createRequire(shellModuleAbsolute).resolve(CHARCELL_SHELL);
301
+ }
302
+ catch {
303
+ return undefined;
304
+ }
305
+ }
306
+ function readPackageVersion(kitRoot) {
307
+ const packagePath = resolve(kitRoot, 'package.json');
308
+ if (!existsSync(packagePath))
309
+ return undefined;
310
+ // A package.json that is not JSON has no version to read; `check` reports it.
311
+ let manifest;
312
+ try {
313
+ manifest = JSON.parse(readFileSync(packagePath, 'utf8'));
314
+ }
315
+ catch {
316
+ return undefined;
317
+ }
318
+ const version = manifest?.version;
319
+ return typeof version === 'string' ? version : undefined;
320
+ }
321
+ /**
322
+ * The kit configuration's `parts`, each root's list of part names; what is
323
+ * not a list of names is left out, as the kit's build reports it.
324
+ */
325
+ function partsOf(value) {
326
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
327
+ return {};
328
+ const parts = {};
329
+ for (const [root, rootParts] of Object.entries(value)) {
330
+ if (Array.isArray(rootParts))
331
+ parts[root] = rootParts.filter((part) => typeof part === 'string' && part !== '');
332
+ }
333
+ return parts;
334
+ }
335
+ /** scaffold-ui-kit, loaded as the CommonJS package it is. */
336
+ function scaffoldUiKit() {
337
+ return createRequire(import.meta.url)('scaffold-ui-kit');
338
+ }
339
+ /**
340
+ * The scaffold-ui-kit the kit has installed, when it has the resolver, so
341
+ * the preview reads the configuration as the kit's own build does; else the
342
+ * preview's own copy.
343
+ */
344
+ function kitResolver(kitRoot) {
345
+ const own = scaffoldUiKit();
346
+ let installed;
347
+ try {
348
+ installed = createRequire(resolve(kitRoot, 'package.json'))('scaffold-ui-kit');
349
+ }
350
+ catch {
351
+ return own;
352
+ }
353
+ return typeof installed.resolveKitConfiguration === 'function'
354
+ ? installed
355
+ : own;
356
+ }
357
+ /**
358
+ * The problems that keep a value from reaching the browser as JSON, each
359
+ * naming where in the value it sits: a function, `undefined` in a list, a
360
+ * number JSON has no form for, and an object other than a plain one or a
361
+ * list (a regular expression, a date, a map).
362
+ *
363
+ * @param value - The value.
364
+ * @param path - Where the value sits, as `preview.options`.
365
+ * @returns One message per problem.
366
+ * @example
367
+ * ```ts
368
+ * jsonProblems({ apply: () => {} }, 'preview.options[0]');
369
+ * // ["preview.options[0].apply is a function, which can't be sent to the browser as JSON"]
370
+ * ```
371
+ */
372
+ export function jsonProblems(value, path, ancestors = new WeakMap()) {
373
+ const unsendable = (what) => [
374
+ `${path} is ${what}, which can't be sent to the browser as JSON`,
375
+ ];
376
+ if (value === null)
377
+ return [];
378
+ switch (typeof value) {
379
+ case 'string':
380
+ case 'boolean':
381
+ return [];
382
+ case 'number':
383
+ return Number.isFinite(value) ? [] : unsendable(String(value));
384
+ case 'undefined':
385
+ return unsendable('undefined');
386
+ case 'function':
387
+ return unsendable('a function');
388
+ case 'bigint':
389
+ case 'symbol':
390
+ return unsendable(`a ${typeof value}`);
391
+ }
392
+ const object = value;
393
+ // An object inside itself has no end to write out.
394
+ const outer = ancestors.get(object);
395
+ if (outer !== undefined)
396
+ return [
397
+ `${path} is ${outer} again, a cycle that can't be sent to the browser as JSON`,
398
+ ];
399
+ const prototype = Object.getPrototypeOf(object);
400
+ if (!Array.isArray(object) &&
401
+ prototype !== Object.prototype &&
402
+ prototype !== null)
403
+ return unsendable(`${object.constructor?.name ?? 'an object'} object`);
404
+ ancestors.set(object, path);
405
+ const problems = Array.isArray(object)
406
+ ? object.flatMap((entry, index) => jsonProblems(entry, `${path}[${index}]`, ancestors))
407
+ : Object.entries(object)
408
+ .filter(([, entry]) => entry !== undefined)
409
+ .flatMap(([key, entry]) => jsonProblems(entry, `${path}.${key}`, ancestors));
410
+ // Only an ancestor makes a cycle; the same object twice side by side is fine.
411
+ ancestors.delete(object);
412
+ return problems;
413
+ }
414
+ /**
415
+ * Finds the kit configuration (`--configuration`, the kit's `package.json`
416
+ * pointer, or `scaffold-ui-kit.ts` or `scaffold-ui-kit.json` at the kit
417
+ * root) and resolves the preview context from its `preview` block.
418
+ *
419
+ * @param kitRoot - The kit root.
420
+ * @param options - The configuration file a command was given, if any.
421
+ * @returns The context.
422
+ */
423
+ export function loadPreviewContext(kitRoot, options = {}) {
424
+ const resolver = kitResolver(kitRoot);
425
+ let resolved;
426
+ try {
427
+ resolved = resolver.resolveKitConfiguration(kitRoot, options);
428
+ }
429
+ catch (error) {
430
+ fail(error instanceof Error ? error.message : String(error));
431
+ }
432
+ const configurationPath = resolved.file.path;
433
+ const configurationName = resolved.file.name;
434
+ const warnings = [...resolved.warnings];
435
+ const configurationModules = new Set(resolved.modules ?? []);
436
+ const kitConfiguration = resolved.configuration;
437
+ const authoredBlock = kitConfiguration.preview;
438
+ if (!isPlainObject(authoredBlock)) {
439
+ fail(`${configurationName} has no preview block${'extensions' in kitConfiguration
440
+ ? "; the preview's settings go in a top-level 'preview' block, not 'extensions'"
441
+ : ''}`);
442
+ }
443
+ const authored = authoredBlock;
444
+ // A misspelled key would otherwise go unread without a word: every unknown
445
+ // key fails, the block's and each object's inside it, all named at once.
446
+ const unknownKeys = [
447
+ ...unknownKeyProblems(authored, PREVIEW_KEYS, 'preview', "the 'preview' block", PREVIEW_KEY_REPLACEMENTS),
448
+ ...(Array.isArray(authored.sources) ? authored.sources : []).flatMap((source, index) => source && typeof source === 'object' && !Array.isArray(source)
449
+ ? unknownKeyProblems(source, SOURCE_KEYS, `preview.sources[${index}]`, 'a source', SOURCE_KEY_REPLACEMENTS)
450
+ : []),
451
+ ...(isPlainObject(authored.codeBlock)
452
+ ? unknownKeyProblems(authored.codeBlock, CODE_BLOCK_KEYS, 'preview.codeBlock', 'a code block')
453
+ : []),
454
+ ...(isPlainObject(authored.setupModules)
455
+ ? unknownKeyProblems(authored.setupModules, ALL_TARGETS, 'preview.setupModules', "the 'setupModules' map")
456
+ : []),
457
+ ...(isPlainObject(authored.landing)
458
+ ? unknownKeyProblems(authored.landing, LANDING_KEYS, 'preview.landing', 'the landing entry')
459
+ : []),
460
+ ...(isPlainObject(authored.handWrittenComponents)
461
+ ? Object.entries(authored.handWrittenComponents).flatMap(([name, mapping]) => isPlainObject(mapping)
462
+ ? unknownKeyProblems(mapping, HAND_WRITTEN_KEYS, `preview.handWrittenComponents.${name}`, 'a hand-written component')
463
+ : [])
464
+ : []),
465
+ ];
466
+ if (unknownKeys.length > 0)
467
+ fail(unknownKeys.join('\n[preview] '));
468
+ if (!Array.isArray(authored.sources) || authored.sources.length === 0) {
469
+ fail('preview.sources must be a non-empty array');
470
+ }
471
+ authored.sources.forEach(validateSource);
472
+ const shellSetting = authored.shell;
473
+ if (shellSetting === undefined || shellSetting === '') {
474
+ fail(`preview.shell is required: the shell that renders the preview's interface, as a package specifier or a module path relative to the kit root. ` +
475
+ `To use the shell shipped with the preview, install charcell and @zag-js/react and set shell: '${CHARCELL_SHELL}'.`);
476
+ }
477
+ const shellProblem = shellSettingProblem(shellSetting);
478
+ if (shellProblem !== undefined)
479
+ fail(shellProblem);
480
+ const shell = typeof shellSetting === 'string'
481
+ ? shellSetting
482
+ : shellSetting.module;
483
+ const shellPage = typeof shellSetting === 'string'
484
+ ? undefined
485
+ : shellSetting.page;
486
+ const canvas = authored.canvas;
487
+ if (canvas !== undefined &&
488
+ (!isPlainObject(canvas) ||
489
+ typeof canvas.namePrefix !== 'string' ||
490
+ canvas.namePrefix === '')) {
491
+ fail("preview.canvas must be { namePrefix, pageBackground?, extraCss? }: namePrefix the prefix for the names the preview sets in each frame, such as 'my-ui-kit'");
492
+ }
493
+ for (const key of ['options', 'themes']) {
494
+ if (authored[key] !== undefined && !Array.isArray(authored[key]))
495
+ fail(`preview.${key} must be a list`);
496
+ }
497
+ for (const key of [
498
+ 'mount',
499
+ 'viewCanvas',
500
+ 'controlHints',
501
+ 'labels',
502
+ ]) {
503
+ if (authored[key] !== undefined && !isPlainObject(authored[key]))
504
+ fail(`preview.${key} must be an object`);
505
+ }
506
+ // What the preview's pages read reaches them as JSON, so it must be JSON.
507
+ const unsendable = PREVIEW_BROWSER_KEYS.filter((key) => authored[key] !== undefined).flatMap((key) => jsonProblems(authored[key], `preview.${key}`));
508
+ if (unsendable.length > 0)
509
+ fail(unsendable.join('\n[preview] '));
510
+ const colorTokens = authored.controlHints?.colorTokens;
511
+ if (colorTokens !== undefined && !Array.isArray(colorTokens))
512
+ fail('preview.controlHints.colorTokens must be a list of tokens');
513
+ const configuration = {
514
+ ...authored,
515
+ options: authored.options ?? [],
516
+ themes: authored.themes ?? [],
517
+ // The kit's name, made safe for the names it prefixes, by default.
518
+ canvas: authored.canvas ?? {
519
+ namePrefix: (kitConfiguration.name ?? 'kit')
520
+ .replace(/[^A-Za-z0-9_-]/g, '-')
521
+ .replace(/^-+|-+$/g, ''),
522
+ },
523
+ shell,
524
+ ...(authored.controlHints
525
+ ? {
526
+ controlHints: {
527
+ ...authored.controlHints,
528
+ ...(colorTokens
529
+ ? { colorTokens: colorTokens.map((token) => token ?? null) }
530
+ : {}),
531
+ },
532
+ }
533
+ : {}),
534
+ };
535
+ let omitPattern;
536
+ if (configuration.omitPattern !== undefined) {
537
+ try {
538
+ omitPattern = new RegExp(configuration.omitPattern);
539
+ }
540
+ catch {
541
+ fail(`preview.omitPattern must be a regular expression: ${String(configuration.omitPattern)}`);
542
+ }
543
+ }
544
+ const handWritten = configuration.handWrittenComponents;
545
+ if (handWritten !== undefined &&
546
+ (typeof handWritten !== 'object' ||
547
+ handWritten === null ||
548
+ Array.isArray(handWritten) ||
549
+ Object.values(handWritten).some((value) => typeof value !== 'string' &&
550
+ (typeof value !== 'object' ||
551
+ value === null ||
552
+ typeof value.template !== 'string' ||
553
+ (value.slots !== undefined &&
554
+ (typeof value.slots !== 'object' ||
555
+ Object.values(value.slots).some((slot) => typeof slot !== 'string'))))))) {
556
+ fail("preview.handWrittenComponents must map each component name to 'content', a template name, or { template, slots }");
557
+ }
558
+ const dedupe = configuration.dedupe;
559
+ if (dedupe !== undefined &&
560
+ (!Array.isArray(dedupe) ||
561
+ dedupe.some((name) => typeof name !== 'string' || name === ''))) {
562
+ fail('preview.dedupe must be a list of package names');
563
+ }
564
+ const htmlStylesheets = configuration.htmlStylesheets;
565
+ if (htmlStylesheets !== undefined &&
566
+ (!Array.isArray(htmlStylesheets) ||
567
+ htmlStylesheets.some((path) => typeof path !== 'string' || path === ''))) {
568
+ fail('preview.htmlStylesheets must be a list of stylesheet paths, relative to the kit root');
569
+ }
570
+ const codeBlock = configuration.codeBlock;
571
+ if (codeBlock !== undefined &&
572
+ (typeof codeBlock !== 'object' ||
573
+ codeBlock === null ||
574
+ typeof codeBlock.module !== 'string' ||
575
+ codeBlock.module === '' ||
576
+ (codeBlock.framework !== undefined &&
577
+ !['react', 'vue', 'svelte', 'dom'].includes(codeBlock.framework)))) {
578
+ fail("preview.codeBlock must be { module, framework? }: a module path relative to the kit root, and 'react', 'vue', 'svelte' or 'dom'");
579
+ }
580
+ const persistence = configuration.persistence;
581
+ if (persistence !== undefined &&
582
+ persistence !== 'url' &&
583
+ persistence !== 'url-and-local-storage') {
584
+ fail(`preview.persistence must be 'url' or 'url-and-local-storage', not ${JSON.stringify(persistence)}`);
585
+ }
586
+ if (configuration.landing !== undefined) {
587
+ const problem = landingShapeProblem(configuration.landing);
588
+ if (problem)
589
+ fail(problem);
590
+ }
591
+ const sources = [...configuration.sources]
592
+ .sort((a, b) => a.order - b.order)
593
+ .map((source) => {
594
+ const templatesDirectoryAbsolute = source.templatesDirectory
595
+ ? resolve(kitRoot, source.templatesDirectory)
596
+ : undefined;
597
+ const storiesDirectoryAbsolute = source.storiesDirectory
598
+ ? resolve(kitRoot, source.storiesDirectory)
599
+ : undefined;
600
+ return {
601
+ ...source,
602
+ templatesDirectoryAbsolute,
603
+ manualModuleAbsolute: source.manualModule
604
+ ? resolve(kitRoot, source.manualModule)
605
+ : undefined,
606
+ storiesDirectoryAbsolute,
607
+ };
608
+ });
609
+ const kitTargets = kitConfiguration.targets;
610
+ if (kitTargets !== undefined &&
611
+ (!Array.isArray(kitTargets) ||
612
+ kitTargets.some((target) => typeof target !== 'string'))) {
613
+ fail(`${configurationName} targets must be a list of target names`);
614
+ }
615
+ const targets = kitTargets
616
+ ? ALL_TARGETS.filter((target) => kitTargets.includes(target))
617
+ : [...ALL_TARGETS];
618
+ const jiti = createJiti(import.meta.url);
619
+ const importModule = (absolutePath) => jiti.import(absolutePath);
620
+ const shellModuleAbsolute = resolveShellModule(kitRoot, shell);
621
+ const shellPageAbsolute = resolveShellPage(kitRoot, shell, shellPage);
622
+ const charcellShellAbsolute = resolveCharcellShell(shell, shellModuleAbsolute);
623
+ if (charcellShellAbsolute !== undefined && shellPageAbsolute === undefined) {
624
+ warnings.push(`preview.shell names a module that configures the charcell shell and no page, so the host page starts empty and light until the shell loads; ` +
625
+ `name the shell's page beside it: shell: { module: '${shell}', page: '${CHARCELL_SHELL}/page' }`);
626
+ }
627
+ const kitPackages = kitPackagesOf(kitRoot, kitConfiguration.packages);
628
+ const sharedSpecifiers = sharedSpecifiersOf(kitRoot, kitConfiguration.add?.shared, (kitPackages ?? []).map(({ directoryAbsolute }) => directoryAbsolute));
629
+ return {
630
+ kitRoot,
631
+ ...(kitPackages === undefined ? {} : { kitPackages }),
632
+ configurationPath,
633
+ configurationModules: [...configurationModules].filter((module) => module !== configurationPath),
634
+ warnings,
635
+ kitName: kitConfiguration.name ?? 'kit',
636
+ kitVersion: readPackageVersion(kitRoot),
637
+ targets,
638
+ buildsHtml: kitTargets?.includes('html') ?? false,
639
+ svelteMode: kitConfiguration.svelte?.mode === 'runes' ? 'runes' : 'legacy',
640
+ generatedRoot: resolve(kitRoot, '.preview-generated'),
641
+ staticRoot: resolve(kitRoot, '.preview-static'),
642
+ configuration,
643
+ sources,
644
+ viewsModuleAbsolute: configuration.viewsModule
645
+ ? resolve(kitRoot, configuration.viewsModule)
646
+ : undefined,
647
+ codeBlockModuleAbsolute: configuration.codeBlock
648
+ ? resolve(kitRoot, configuration.codeBlock.module)
649
+ : undefined,
650
+ shellModuleAbsolute: shellModuleAbsolute,
651
+ ...(shellPageAbsolute === undefined ? {} : { shellPageAbsolute }),
652
+ ...(charcellShellAbsolute === undefined ? {} : { charcellShellAbsolute }),
653
+ omitPattern,
654
+ importModule,
655
+ styling: {
656
+ extensions: kitConfiguration.styling ?? [],
657
+ placement: kitConfiguration.stylesheetPlacement ?? 'in-file',
658
+ ...(kitConfiguration.bem === undefined
659
+ ? {}
660
+ : { bem: kitConfiguration.bem }),
661
+ ...(kitConfiguration.tailwind === undefined
662
+ ? {}
663
+ : { tailwind: kitConfiguration.tailwind }),
664
+ },
665
+ parts: partsOf(kitConfiguration.parts),
666
+ sassLoadPathsAbsolute: (Array.isArray(kitConfiguration.loadPaths)
667
+ ? kitConfiguration.loadPaths
668
+ : [])
669
+ .filter((path) => typeof path === 'string')
670
+ .map((path) => resolve(kitRoot, path)),
671
+ sharedModulesAbsolute: sharedSpecifiers
672
+ .filter(({ source }) => /(^|\/)[^./]*$|\.[cm]?[jt]sx?$/.test(source))
673
+ .map(({ source }) => resolve(kitRoot, source)),
674
+ sharedSpecifiers,
675
+ helperModulesAbsolute: helperModulesOf([
676
+ kitRoot,
677
+ ...(kitPackages ?? []).map(({ directoryAbsolute }) => directoryAbsolute),
678
+ ]),
679
+ };
680
+ }
681
+ /** The package whose framework-free entries a story's html has in scope. */
682
+ const HELPERS_PACKAGE = '@scaffold-ui-kit/helpers';
683
+ /** The entries of `@scaffold-ui-kit/helpers` that import no framework. */
684
+ const FRAMEWORK_FREE_HELPER_ENTRIES = ['./runtime', './zag'];
685
+ /**
686
+ * Each framework-free helper entry as the first of the directories that has
687
+ * the package installed gives it: the package found in `node_modules` from
688
+ * that directory up, and the entry's file from its `exports` (`import`, else
689
+ * `default`). Nothing else is searched, so a kit without the package gets
690
+ * none. An entry the package does not export is left out.
691
+ */
692
+ function helperModulesOf(directories) {
693
+ for (const directory of directories) {
694
+ const packageDirectory = installedPackageDirectory(directory, HELPERS_PACKAGE);
695
+ if (packageDirectory === undefined)
696
+ continue;
697
+ let exportsField;
698
+ try {
699
+ exportsField = JSON.parse(readFileSync(resolve(packageDirectory, 'package.json'), 'utf8')).exports;
700
+ }
701
+ catch {
702
+ continue;
703
+ }
704
+ if (!isPlainObject(exportsField))
705
+ continue;
706
+ return FRAMEWORK_FREE_HELPER_ENTRIES.flatMap((entry) => {
707
+ const target = exportsField[entry];
708
+ const file = typeof target === 'string'
709
+ ? target
710
+ : isPlainObject(target)
711
+ ? (target.import ?? target.default)
712
+ : undefined;
713
+ return typeof file === 'string'
714
+ ? [realpathSync(resolve(packageDirectory, file))]
715
+ : [];
716
+ });
717
+ }
718
+ return [];
719
+ }
720
+ /**
721
+ * The directory of a package installed for `start`, as Node finds it: in
722
+ * `node_modules` of `start` or of a directory above it, its links resolved.
723
+ */
724
+ function installedPackageDirectory(start, name) {
725
+ let directory = resolve(start);
726
+ for (;;) {
727
+ const candidate = resolve(directory, 'node_modules', name);
728
+ if (existsSync(resolve(candidate, 'package.json'))) {
729
+ return realpathSync(candidate);
730
+ }
731
+ const parent = dirname(directory);
732
+ if (parent === directory)
733
+ return undefined;
734
+ directory = parent;
735
+ }
736
+ }
737
+ /**
738
+ * The kit's shared modules with the specifier its built files import each
739
+ * by: the one the kit's `package.json` exports give the module, else the
740
+ * `specifier` a configuration still names.
741
+ */
742
+ function sharedSpecifiersOf(kitRoot, sharedModules, packageDirectories) {
743
+ const authored = (Array.isArray(sharedModules) ? sharedModules : []).filter((sharedModule) => isPlainObject(sharedModule) && typeof sharedModule.source === 'string');
744
+ const { sharedModulesOf } = scaffoldUiKit();
745
+ const published = sharedModulesOf(kitRoot, authored.map(({ source }) => ({ source })), packageDirectories);
746
+ return authored.flatMap((sharedModule, index) => {
747
+ const specifier = published[index].import ??
748
+ (typeof sharedModule.specifier === 'string'
749
+ ? sharedModule.specifier
750
+ : undefined);
751
+ return specifier === undefined
752
+ ? []
753
+ : [{ source: sharedModule.source, specifier }];
754
+ });
755
+ }
756
+ /**
757
+ * The kit's packages besides the kit root's, from its configuration's
758
+ * `packages`: the styles package, then each target's, their
759
+ * directories resolved against the kit root. `undefined` for a kit without
760
+ * `packages`; an entry without a name and directory is left out, as the
761
+ * kit's build reports it.
762
+ */
763
+ function kitPackagesOf(kitRoot, packages) {
764
+ if (!isPlainObject(packages))
765
+ return undefined;
766
+ const roles = ['styles', ...ALL_TARGETS];
767
+ return roles.flatMap((role) => {
768
+ const entry = packages[role];
769
+ if (!isPlainObject(entry) ||
770
+ typeof entry.name !== 'string' ||
771
+ entry.name === '' ||
772
+ typeof entry.directory !== 'string' ||
773
+ entry.directory === '')
774
+ return [];
775
+ return [
776
+ {
777
+ role,
778
+ name: entry.name,
779
+ directoryAbsolute: resolve(kitRoot, entry.directory),
780
+ },
781
+ ];
782
+ });
783
+ }