@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.
- package/LICENSE +21 -0
- package/README.md +1868 -2
- package/application/host/bootstrap.ts +43 -0
- package/application/host/main.ts +31 -0
- package/application/host/shell-host.ts +47 -0
- package/application/index.html +12 -0
- package/application/lib/base-url.ts +15 -0
- package/application/lib/no-code-block.ts +7 -0
- package/application/lib/no-setup.ts +3 -0
- package/application/lib/no-views.ts +6 -0
- package/application/preview/code/code.html +12 -0
- package/application/preview/code/main.ts +89 -0
- package/application/preview/globals.ts +254 -0
- package/application/preview/html/main.ts +93 -0
- package/application/preview/html/preview.html +12 -0
- package/application/preview/react/main.tsx +146 -0
- package/application/preview/react/preview.html +12 -0
- package/application/preview/svelte/Preview.svelte +104 -0
- package/application/preview/svelte/main.ts +13 -0
- package/application/preview/svelte/preview.html +12 -0
- package/application/preview/svelte/story-props.ts +172 -0
- package/application/preview/view/main.ts +124 -0
- package/application/preview/view/view.html +12 -0
- package/application/preview/vue/main.ts +145 -0
- package/application/preview/vue/preview.html +12 -0
- package/application/shells/charcell/components/Application/index.css +37 -0
- package/application/shells/charcell/components/Application/index.tsx +65 -0
- package/application/shells/charcell/components/Bar/index.css +68 -0
- package/application/shells/charcell/components/Bar/index.tsx +60 -0
- package/application/shells/charcell/components/CodePane/index.css +60 -0
- package/application/shells/charcell/components/CodePane/index.tsx +113 -0
- package/application/shells/charcell/components/ComponentStage/index.css +26 -0
- package/application/shells/charcell/components/ComponentStage/index.tsx +110 -0
- package/application/shells/charcell/components/Control/index.css +20 -0
- package/application/shells/charcell/components/Control/index.tsx +118 -0
- package/application/shells/charcell/components/CopyLink/index.tsx +35 -0
- package/application/shells/charcell/components/Description/index.css +6 -0
- package/application/shells/charcell/components/Description/index.tsx +24 -0
- package/application/shells/charcell/components/EditorAddDialog/index.css +9 -0
- package/application/shells/charcell/components/EditorAddDialog/index.tsx +158 -0
- package/application/shells/charcell/components/EditorPanel/index.css +48 -0
- package/application/shells/charcell/components/EditorPanel/index.tsx +496 -0
- package/application/shells/charcell/components/EventsRow/index.css +42 -0
- package/application/shells/charcell/components/EventsRow/index.tsx +68 -0
- package/application/shells/charcell/components/HeadingRow/index.css +11 -0
- package/application/shells/charcell/components/HeadingRow/index.tsx +16 -0
- package/application/shells/charcell/components/Hint/index.css +16 -0
- package/application/shells/charcell/components/Hint/index.tsx +81 -0
- package/application/shells/charcell/components/HtmlWarnings/index.tsx +45 -0
- package/application/shells/charcell/components/KeyHints/index.css +27 -0
- package/application/shells/charcell/components/KeyHints/index.tsx +106 -0
- package/application/shells/charcell/components/KitCodeBlock/index.css +55 -0
- package/application/shells/charcell/components/KitCodeBlock/index.tsx +18 -0
- package/application/shells/charcell/components/Markdown/index.tsx +16 -0
- package/application/shells/charcell/components/Navigation/index.css +13 -0
- package/application/shells/charcell/components/Navigation/index.tsx +98 -0
- package/application/shells/charcell/components/NavigationSplit/index.css +16 -0
- package/application/shells/charcell/components/NavigationSplit/index.tsx +82 -0
- package/application/shells/charcell/components/OptionControls/index.tsx +93 -0
- package/application/shells/charcell/components/PaneStatus/index.css +7 -0
- package/application/shells/charcell/components/PaneStatus/index.tsx +47 -0
- package/application/shells/charcell/components/PaneWidth/index.tsx +45 -0
- package/application/shells/charcell/components/PreviewGrid/index.css +39 -0
- package/application/shells/charcell/components/PreviewGrid/index.tsx +344 -0
- package/application/shells/charcell/components/PreviewPane/index.css +127 -0
- package/application/shells/charcell/components/PreviewPane/index.tsx +182 -0
- package/application/shells/charcell/components/PromptBar/index.tsx +133 -0
- package/application/shells/charcell/components/ScenarioRow/index.css +36 -0
- package/application/shells/charcell/components/ScenarioRow/index.tsx +71 -0
- package/application/shells/charcell/components/Select/index.tsx +48 -0
- package/application/shells/charcell/components/SlotOutlet/index.css +7 -0
- package/application/shells/charcell/components/SlotOutlet/index.tsx +25 -0
- package/application/shells/charcell/components/StageBar/index.css +7 -0
- package/application/shells/charcell/components/StageBar/index.tsx +109 -0
- package/application/shells/charcell/components/StageSplit/index.css +11 -0
- package/application/shells/charcell/components/StageSplit/index.tsx +99 -0
- package/application/shells/charcell/components/StatusBar/index.tsx +30 -0
- package/application/shells/charcell/components/ViewControls/index.css +15 -0
- package/application/shells/charcell/components/ViewControls/index.tsx +58 -0
- package/application/shells/charcell/components/ViewHeading/index.css +13 -0
- package/application/shells/charcell/components/ViewHeading/index.tsx +8 -0
- package/application/shells/charcell/components/ViewStage/index.css +9 -0
- package/application/shells/charcell/components/ViewStage/index.tsx +25 -0
- package/application/shells/charcell/components/WrapToggle/index.tsx +27 -0
- package/application/shells/charcell/configuration.ts +45 -0
- package/application/shells/charcell/controller-context.ts +27 -0
- package/application/shells/charcell/fit-labels.ts +42 -0
- package/application/shells/charcell/html-warnings.ts +18 -0
- package/application/shells/charcell/index.tsx +82 -0
- package/application/shells/charcell/page-loading.html +57 -0
- package/application/shells/charcell/placement.ts +24 -0
- package/application/shells/charcell/shell-options.ts +23 -0
- package/application/shells/charcell/shell.css +37 -0
- package/application/shells/charcell/shell.tsx +25 -0
- package/application/shells/charcell/split-reset.ts +47 -0
- package/application/shells/charcell/stacked-rows.ts +117 -0
- package/application/shells/charcell/stage-toggles.ts +93 -0
- package/application/types/manifest.d.ts +6 -0
- package/application/types/preview-configuration.d.ts +10 -0
- package/application/types/preview-shell.d.ts +7 -0
- package/application/types/react-registry.d.ts +15 -0
- package/application/types/vue-registry.d.ts +6 -0
- package/dist/build.d.ts +24 -0
- package/dist/build.js +34 -0
- package/dist/check.d.ts +48 -0
- package/dist/check.js +781 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +88 -0
- package/dist/context.d.ts +179 -0
- package/dist/context.js +783 -0
- package/dist/controller/catalog.d.ts +339 -0
- package/dist/controller/catalog.js +367 -0
- package/dist/controller/create-preview-controller.d.ts +532 -0
- package/dist/controller/create-preview-controller.js +1707 -0
- package/dist/controller/editor-tree.d.ts +161 -0
- package/dist/controller/editor-tree.js +373 -0
- package/dist/controller/html-source.d.ts +62 -0
- package/dist/controller/html-source.js +61 -0
- package/dist/controller/index.d.ts +9 -0
- package/dist/controller/index.js +12 -0
- package/dist/controller/persistence.d.ts +191 -0
- package/dist/controller/persistence.js +315 -0
- package/dist/controller/regeneration.d.ts +30 -0
- package/dist/controller/regeneration.js +27 -0
- package/dist/controller/source.d.ts +80 -0
- package/dist/controller/source.js +236 -0
- package/dist/controller/store.d.ts +27 -0
- package/dist/controller/store.js +40 -0
- package/dist/declarations/application/shells/charcell/components/Application/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Bar/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/CodePane/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/ComponentStage/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Control/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/CopyLink/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/Description/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/EditorAddDialog/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/EditorPanel/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/EventsRow/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/HeadingRow/index.d.ts +6 -0
- package/dist/declarations/application/shells/charcell/components/Hint/index.d.ts +11 -0
- package/dist/declarations/application/shells/charcell/components/HtmlWarnings/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/KeyHints/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/KitCodeBlock/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/Markdown/index.d.ts +3 -0
- package/dist/declarations/application/shells/charcell/components/Navigation/index.d.ts +8 -0
- package/dist/declarations/application/shells/charcell/components/NavigationSplit/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/OptionControls/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PaneStatus/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/PaneWidth/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/PreviewGrid/index.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/components/PreviewPane/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/PromptBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ScenarioRow/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/Select/index.d.ts +16 -0
- package/dist/declarations/application/shells/charcell/components/SlotOutlet/index.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/components/StageBar/index.d.ts +2 -0
- package/dist/declarations/application/shells/charcell/components/StageSplit/index.d.ts +17 -0
- package/dist/declarations/application/shells/charcell/components/StatusBar/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/components/ViewControls/index.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/components/ViewHeading/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/ViewStage/index.d.ts +4 -0
- package/dist/declarations/application/shells/charcell/components/WrapToggle/index.d.ts +1 -0
- package/dist/declarations/application/shells/charcell/configuration.d.ts +31 -0
- package/dist/declarations/application/shells/charcell/controller-context.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/fit-labels.d.ts +18 -0
- package/dist/declarations/application/shells/charcell/html-warnings.d.ts +5 -0
- package/dist/declarations/application/shells/charcell/index.d.ts +39 -0
- package/dist/declarations/application/shells/charcell/placement.d.ts +7 -0
- package/dist/declarations/application/shells/charcell/shell-options.d.ts +15 -0
- package/dist/declarations/application/shells/charcell/shell.d.ts +9 -0
- package/dist/declarations/application/shells/charcell/split-reset.d.ts +30 -0
- package/dist/declarations/application/shells/charcell/stacked-rows.d.ts +58 -0
- package/dist/declarations/application/shells/charcell/stage-toggles.d.ts +28 -0
- package/dist/declarations/src/controller/catalog.d.ts +339 -0
- package/dist/declarations/src/controller/create-preview-controller.d.ts +532 -0
- package/dist/declarations/src/controller/editor-tree.d.ts +161 -0
- package/dist/declarations/src/controller/html-source.d.ts +62 -0
- package/dist/declarations/src/controller/index.d.ts +9 -0
- package/dist/declarations/src/controller/persistence.d.ts +191 -0
- package/dist/declarations/src/controller/regeneration.d.ts +30 -0
- package/dist/declarations/src/controller/source.d.ts +80 -0
- package/dist/declarations/src/controller/store.d.ts +27 -0
- package/dist/declarations/src/emit/common.d.ts +103 -0
- package/dist/declarations/src/emit/manifest.d.ts +138 -0
- package/dist/declarations/src/emit/source.d.ts +87 -0
- package/dist/declarations/src/lib/jsx-roots.d.ts +22 -0
- package/dist/declarations/src/lib/module-of-source.d.ts +18 -0
- package/dist/declarations/src/lib/naming.d.ts +29 -0
- package/dist/declarations/src/lib/react-spelling.d.ts +68 -0
- package/dist/declarations/src/lib/scenario-story.d.ts +41 -0
- package/dist/declarations/src/lib/slot-fill.d.ts +15 -0
- package/dist/declarations/src/lib/types.d.ts +741 -0
- package/dist/declarations/src/options.d.ts +224 -0
- package/dist/declarations/src/protocol.d.ts +541 -0
- package/dist/declarations/src/react-view.d.ts +22 -0
- package/dist/declarations/src/react.d.ts +17 -0
- package/dist/declarations/src/shell.d.ts +99 -0
- package/dist/declarations/src/views.d.ts +152 -0
- package/dist/development-server.d.ts +121 -0
- package/dist/development-server.js +331 -0
- package/dist/emit/common.d.ts +103 -0
- package/dist/emit/common.js +170 -0
- package/dist/emit/html-source.d.ts +46 -0
- package/dist/emit/html-source.js +322 -0
- package/dist/emit/manifest.d.ts +138 -0
- package/dist/emit/manifest.js +74 -0
- package/dist/emit/react.d.ts +16 -0
- package/dist/emit/react.js +210 -0
- package/dist/emit/scratch.d.ts +40 -0
- package/dist/emit/scratch.js +116 -0
- package/dist/emit/source.d.ts +87 -0
- package/dist/emit/source.js +789 -0
- package/dist/emit/svelte.d.ts +9 -0
- package/dist/emit/svelte.js +177 -0
- package/dist/emit/vue.d.ts +2 -0
- package/dist/emit/vue.js +152 -0
- package/dist/enumerate.d.ts +2 -0
- package/dist/enumerate.js +162 -0
- package/dist/generate.d.ts +43 -0
- package/dist/generate.js +302 -0
- package/dist/html-stylesheet.d.ts +43 -0
- package/dist/html-stylesheet.js +145 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +21 -0
- package/dist/landing.d.ts +16 -0
- package/dist/landing.js +45 -0
- package/dist/lib/exported-types.d.ts +42 -0
- package/dist/lib/exported-types.js +86 -0
- package/dist/lib/html-attributes.d.ts +2 -0
- package/dist/lib/html-attributes.js +111 -0
- package/dist/lib/html-warnings.d.ts +19 -0
- package/dist/lib/html-warnings.js +79 -0
- package/dist/lib/jsx-roots.d.ts +22 -0
- package/dist/lib/jsx-roots.js +243 -0
- package/dist/lib/module-of-source.d.ts +18 -0
- package/dist/lib/module-of-source.js +24 -0
- package/dist/lib/naming.d.ts +29 -0
- package/dist/lib/naming.js +56 -0
- package/dist/lib/parse-type.d.ts +32 -0
- package/dist/lib/parse-type.js +103 -0
- package/dist/lib/react-spelling.d.ts +68 -0
- package/dist/lib/react-spelling.js +197 -0
- package/dist/lib/scenario-story.d.ts +41 -0
- package/dist/lib/scenario-story.js +84 -0
- package/dist/lib/slot-fill.d.ts +15 -0
- package/dist/lib/slot-fill.js +92 -0
- package/dist/lib/template-files.d.ts +14 -0
- package/dist/lib/template-files.js +25 -0
- package/dist/lib/types.d.ts +741 -0
- package/dist/lib/types.js +13 -0
- package/dist/lib/void-elements.d.ts +7 -0
- package/dist/lib/void-elements.js +13 -0
- package/dist/load.d.ts +37 -0
- package/dist/load.js +82 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.js +464 -0
- package/dist/options.d.ts +224 -0
- package/dist/options.js +223 -0
- package/dist/prop-types.d.ts +26 -0
- package/dist/prop-types.js +102 -0
- package/dist/props-from.d.ts +84 -0
- package/dist/props-from.js +510 -0
- package/dist/protocol.d.ts +541 -0
- package/dist/protocol.js +819 -0
- package/dist/react-view.d.ts +22 -0
- package/dist/react-view.js +36 -0
- package/dist/react.d.ts +17 -0
- package/dist/react.js +23 -0
- package/dist/sass-importers.d.ts +26 -0
- package/dist/sass-importers.js +32 -0
- package/dist/shell-acceptance.d.ts +21 -0
- package/dist/shell-acceptance.js +37 -0
- package/dist/shell-page.d.ts +36 -0
- package/dist/shell-page.js +90 -0
- package/dist/shell-peers.d.ts +38 -0
- package/dist/shell-peers.js +61 -0
- package/dist/shell.d.ts +99 -0
- package/dist/shell.js +52 -0
- package/dist/shells/charcell/page.d.ts +8 -0
- package/dist/shells/charcell/page.js +69 -0
- package/dist/story-source.d.ts +113 -0
- package/dist/story-source.js +271 -0
- package/dist/story.d.ts +5 -0
- package/dist/story.js +28 -0
- package/dist/testing.d.ts +218 -0
- package/dist/testing.js +284 -0
- package/dist/views.d.ts +152 -0
- package/dist/views.js +7 -0
- package/dist/vite-configuration.d.ts +114 -0
- package/dist/vite-configuration.js +578 -0
- package/package.json +123 -3
- package/src/build.ts +51 -0
- package/src/check.ts +1062 -0
- package/src/cli.ts +123 -0
- package/src/context.ts +1168 -0
- package/src/controller/catalog.ts +719 -0
- package/src/controller/create-preview-controller.ts +2683 -0
- package/src/controller/editor-tree.ts +530 -0
- package/src/controller/html-source.ts +114 -0
- package/src/controller/index.ts +105 -0
- package/src/controller/persistence.ts +403 -0
- package/src/controller/regeneration.ts +55 -0
- package/src/controller/source.ts +333 -0
- package/src/controller/store.ts +55 -0
- package/src/development-server.ts +442 -0
- package/src/emit/common.ts +201 -0
- package/src/emit/html-source.ts +402 -0
- package/src/emit/manifest.ts +241 -0
- package/src/emit/react.ts +263 -0
- package/src/emit/scratch.ts +175 -0
- package/src/emit/source.ts +1094 -0
- package/src/emit/svelte.ts +230 -0
- package/src/emit/vue.ts +194 -0
- package/src/enumerate.ts +220 -0
- package/src/generate.ts +443 -0
- package/src/html-stylesheet.ts +173 -0
- package/src/index.ts +60 -0
- package/src/landing.ts +58 -0
- package/src/lib/exported-types.ts +114 -0
- package/src/lib/html-attributes.ts +113 -0
- package/src/lib/html-warnings.ts +81 -0
- package/src/lib/jsx-roots.ts +238 -0
- package/src/lib/module-of-source.ts +26 -0
- package/src/lib/naming.ts +65 -0
- package/src/lib/parse-type.ts +143 -0
- package/src/lib/react-spelling.ts +235 -0
- package/src/lib/scenario-story.ts +97 -0
- package/src/lib/slot-fill.ts +102 -0
- package/src/lib/template-files.ts +31 -0
- package/src/lib/types.ts +778 -0
- package/src/lib/void-elements.ts +15 -0
- package/src/load.ts +144 -0
- package/src/model.ts +568 -0
- package/src/options.ts +435 -0
- package/src/prop-types.ts +133 -0
- package/src/props-from.ts +678 -0
- package/src/protocol.ts +1135 -0
- package/src/react-view.ts +45 -0
- package/src/react.ts +34 -0
- package/src/sass-importers.ts +45 -0
- package/src/shell-acceptance.ts +45 -0
- package/src/shell-page.ts +133 -0
- package/src/shell-peers.ts +70 -0
- package/src/shell.ts +194 -0
- package/src/shells/charcell/page.ts +82 -0
- package/src/story-source.ts +391 -0
- package/src/story.ts +42 -0
- package/src/testing.ts +497 -0
- package/src/views.ts +183 -0
- package/src/vite-configuration.ts +675 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
// Emit the React render-registry: a map of component id → a render function
|
|
2
|
+
// `(props) => <Component {...props}>...slots...</Component>`. React maps the default
|
|
3
|
+
// slot to JSX children and named slots to props (e.g. a popover's `trigger`).
|
|
4
|
+
// A story spells a component's attributes as HTML does in every target; the
|
|
5
|
+
// fills here are spelled as React takes them (lib/react-spelling.ts), and the
|
|
6
|
+
// registry exports what the React pane spells a story's live props with.
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
effectiveDefaultSlot,
|
|
10
|
+
namedSlots,
|
|
11
|
+
serialize,
|
|
12
|
+
type DefaultSlot,
|
|
13
|
+
} from './common.js';
|
|
14
|
+
import { storyRegistryId } from '../lib/scenario-story.js';
|
|
15
|
+
import { REACT_ATTRIBUTE_NAMES } from '@js-template-engine/extension-react';
|
|
16
|
+
import { reactFill, styleProperties } from '../lib/react-spelling.js';
|
|
17
|
+
import { singleJsxRoot } from '../lib/jsx-roots.js';
|
|
18
|
+
import { collectParts } from '../lib/slot-fill.js';
|
|
19
|
+
import { isVoidElement } from '../lib/void-elements.js';
|
|
20
|
+
import type {
|
|
21
|
+
JsonValue,
|
|
22
|
+
PartFill,
|
|
23
|
+
SlotFill,
|
|
24
|
+
ResolvedStory,
|
|
25
|
+
Target,
|
|
26
|
+
} from '../lib/types.js';
|
|
27
|
+
|
|
28
|
+
// A story file `style` string ('padding: 0 1ch;') → the React style object literal
|
|
29
|
+
// source. Story files author plain CSS text (the natural cross-target form - Vue and
|
|
30
|
+
// Svelte take it verbatim); only React needs the object mapping.
|
|
31
|
+
function styleObjectSource(css: string): string {
|
|
32
|
+
const entries = Object.entries(styleProperties(css)).map(
|
|
33
|
+
([name, value]) =>
|
|
34
|
+
`${name.startsWith('--') ? JSON.stringify(name) : name}: ${JSON.stringify(value)}`
|
|
35
|
+
);
|
|
36
|
+
return `{ ${entries.join(', ')} }`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// A JSX attribute list from `props` (named slots become slot-as-prop attributes,
|
|
40
|
+
// like reactEntry's top-level `namedProps`). For HTML tags, `class` maps to the
|
|
41
|
+
// JSX `className` spelling and a `style` string becomes the object mapping React
|
|
42
|
+
// requires.
|
|
43
|
+
function jsxAttributes(
|
|
44
|
+
props: Record<string, JsonValue> | undefined,
|
|
45
|
+
slots: Record<string, SlotFill> | undefined,
|
|
46
|
+
html: boolean
|
|
47
|
+
): string {
|
|
48
|
+
const parts: string[] = [];
|
|
49
|
+
for (const [propName, value] of Object.entries(props ?? {})) {
|
|
50
|
+
const key = html && propName === 'class' ? 'className' : propName;
|
|
51
|
+
if (html && propName === 'style' && typeof value === 'string') {
|
|
52
|
+
parts.push(`style={${styleObjectSource(value)}}`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
parts.push(
|
|
56
|
+
typeof value === 'boolean' && value ? key : `${key}={${serialize(value)}}`
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
for (const [slot, fill] of Object.entries(slots ?? {}))
|
|
60
|
+
parts.push(`${slot}={${slotExpression(fill)}}`);
|
|
61
|
+
return parts.length ? ' ' + parts.join(' ') : '';
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function elementJsx(fill: Extract<SlotFill, { kind: 'element' }>): string {
|
|
65
|
+
const inner = fill.children ? slotJsx(fill.children) : '';
|
|
66
|
+
return `<${fill.component}${jsxAttributes(fill.props, fill.slots, false)}>${inner}</${fill.component}>`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function htmlJsx(fill: Extract<SlotFill, { kind: 'html' }>): string {
|
|
70
|
+
// A void element (img, input, br) takes no children and no closing tag.
|
|
71
|
+
if (isVoidElement(fill.tag))
|
|
72
|
+
return `<${fill.tag}${jsxAttributes(fill.props, undefined, true)} />`;
|
|
73
|
+
const inner = fill.children ? slotJsx(fill.children) : '';
|
|
74
|
+
return `<${fill.tag}${jsxAttributes(fill.props, undefined, true)}>${inner}</${fill.tag}>`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// A call to a part: the function component the story's entry declares.
|
|
78
|
+
function partJsx(fill: PartFill): string {
|
|
79
|
+
return `<${fill.name}${jsxAttributes(fill.props, undefined, false)} />`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function slotJsx(fill: SlotFill): string {
|
|
83
|
+
if (fill.kind === 'text') return `{${JSON.stringify(fill.value)}}`;
|
|
84
|
+
if (fill.kind === 'raw') return fill.react;
|
|
85
|
+
if (fill.kind === 'part') return partJsx(fill);
|
|
86
|
+
// A scoped fill is only valid at a top-level named slot (reactEntry); ignored nested.
|
|
87
|
+
if (fill.kind === 'scoped') return '';
|
|
88
|
+
if (fill.kind === 'fragment') return fill.children.map(slotJsx).join('');
|
|
89
|
+
if (fill.kind === 'html') return htmlJsx(fill);
|
|
90
|
+
return elementJsx(fill);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// A scoped fill's React arrow head. Vue and Svelte always destructure `parameters` off
|
|
94
|
+
// one scope object; React takes either shape, so the fill says which. The default
|
|
95
|
+
// is the single scope object a generated component's scoped slot supplies - a
|
|
96
|
+
// render prop there is always called with one object, so the same `parameters` list is
|
|
97
|
+
// correct in all three emitters. `positional` keeps the separate-parameters form
|
|
98
|
+
// for a hand-written driver prop whose React side really is positional.
|
|
99
|
+
function reactArrowHead(fill: Extract<SlotFill, { kind: 'scoped' }>): string {
|
|
100
|
+
return fill.reactScope === 'positional'
|
|
101
|
+
? `(${fill.parameters.join(', ')})`
|
|
102
|
+
: `({ ${fill.parameters.join(', ')} })`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// A slot fill in *prop* position (a named slot mapped to a React prop, e.g.
|
|
106
|
+
// `description={...}`). Unlike slotJsx (children position), the value must be a bare
|
|
107
|
+
// JSX *expression* - so text is a plain string literal (not `{"..."}`, which would
|
|
108
|
+
// double-brace) and a multi-child fragment is wrapped in `<>...</>` so it stays a
|
|
109
|
+
// single expression.
|
|
110
|
+
function slotExpression(fill: SlotFill): string {
|
|
111
|
+
if (fill.kind === 'text') return JSON.stringify(fill.value);
|
|
112
|
+
// A raw body of sibling roots, or of text, is one expression in a fragment.
|
|
113
|
+
if (fill.kind === 'raw') return singleJsxRoot(fill.react);
|
|
114
|
+
if (fill.kind === 'part') return partJsx(fill);
|
|
115
|
+
// A scoped fill becomes a render-prop arrow (handled in reactEntry, not here).
|
|
116
|
+
if (fill.kind === 'scoped')
|
|
117
|
+
return `${reactArrowHead(fill)} => (${singleJsxRoot(fill.react)})`;
|
|
118
|
+
if (fill.kind === 'fragment')
|
|
119
|
+
return `<>${fill.children.map(slotJsx).join('')}</>`;
|
|
120
|
+
if (fill.kind === 'html') return htmlJsx(fill);
|
|
121
|
+
return elementJsx(fill);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function defaultJsx(def: DefaultSlot): string {
|
|
125
|
+
if (!def) return '';
|
|
126
|
+
if (def.kind === 'propText') return `{props.${def.prop}}`;
|
|
127
|
+
// Scoped content for the default slot is the function child.
|
|
128
|
+
if (def.kind === 'scoped') return def.react ? `{${slotExpression(def)}}` : '';
|
|
129
|
+
return slotJsx(def);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function reactEntry(
|
|
133
|
+
model: ResolvedStory,
|
|
134
|
+
declares: (component: string, name: string) => boolean
|
|
135
|
+
): string {
|
|
136
|
+
const given = effectiveDefaultSlot(model);
|
|
137
|
+
const def: DefaultSlot =
|
|
138
|
+
given?.kind === 'propText'
|
|
139
|
+
? given
|
|
140
|
+
: reactFill(given, declares, REACT_ATTRIBUTE_NAMES);
|
|
141
|
+
const named = Object.fromEntries(
|
|
142
|
+
Object.entries(namedSlots(model)).map(([slot, fill]) => [
|
|
143
|
+
slot,
|
|
144
|
+
reactFill(fill, declares, REACT_ATTRIBUTE_NAMES),
|
|
145
|
+
])
|
|
146
|
+
);
|
|
147
|
+
const namedProps = Object.entries(named)
|
|
148
|
+
.map(([slot, fill]) => {
|
|
149
|
+
// A scoped fill maps to a render-prop named per target (`fill.slot.react`), not
|
|
150
|
+
// the map key; an empty target body omits the prop.
|
|
151
|
+
if (fill.kind === 'scoped')
|
|
152
|
+
return fill.react
|
|
153
|
+
? ` ${fill.slot.react}={${slotExpression(fill)}}`
|
|
154
|
+
: '';
|
|
155
|
+
return ` ${slot}={${slotExpression(fill)}}`;
|
|
156
|
+
})
|
|
157
|
+
.join('');
|
|
158
|
+
const children = defaultJsx(def);
|
|
159
|
+
// The content beside the component: its siblings, in a fragment with it.
|
|
160
|
+
const before = reactFill(
|
|
161
|
+
model.around?.before,
|
|
162
|
+
declares,
|
|
163
|
+
REACT_ATTRIBUTE_NAMES
|
|
164
|
+
);
|
|
165
|
+
const after = reactFill(model.around?.after, declares, REACT_ATTRIBUTE_NAMES);
|
|
166
|
+
const beside = before !== undefined || after !== undefined;
|
|
167
|
+
const open = `${beside ? `<>${before ? slotJsx(before) : ''}` : ''}<${model.name} {...props}${namedProps}>`;
|
|
168
|
+
const close = `</${model.name}>${beside ? `${after ? slotJsx(after) : ''}</>` : ''}`;
|
|
169
|
+
const body = children ? `\n ${children}\n ` : '';
|
|
170
|
+
const key = JSON.stringify(storyRegistryId(model));
|
|
171
|
+
const parts = collectParts([
|
|
172
|
+
before,
|
|
173
|
+
model.slots?.default,
|
|
174
|
+
...Object.values(named),
|
|
175
|
+
after,
|
|
176
|
+
]);
|
|
177
|
+
if (parts.size === 0)
|
|
178
|
+
return ` ${key}: (props) => (\n ${open}${body}${close}\n ),`;
|
|
179
|
+
// The story's parts are function components declared in its entry, so two
|
|
180
|
+
// stories can each declare a part of one name.
|
|
181
|
+
const declarations = [...parts.values()]
|
|
182
|
+
.map(
|
|
183
|
+
(part) =>
|
|
184
|
+
` function ${part.name}({ ${part.parameters.join(', ')} }: Record<string, any>) {\n` +
|
|
185
|
+
` return ${part.react ? `(${singleJsxRoot(part.react)})` : 'null'};\n` +
|
|
186
|
+
` }\n`
|
|
187
|
+
)
|
|
188
|
+
.join('');
|
|
189
|
+
return (
|
|
190
|
+
` ${key}: (() => {\n${declarations}` +
|
|
191
|
+
` return (props: Record<string, any>) => (\n ${open}${children ? `\n ${children}\n ` : ''}${close}\n );\n` +
|
|
192
|
+
` })(),`
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Group a component into its source's module bucket (insertion-ordered, so the core
|
|
197
|
+
// source's import line comes first and other sources' lines append after it).
|
|
198
|
+
function addImport(
|
|
199
|
+
byModule: Map<string, Set<string>>,
|
|
200
|
+
mod: string,
|
|
201
|
+
name: string
|
|
202
|
+
): void {
|
|
203
|
+
let set = byModule.get(mod);
|
|
204
|
+
if (!set) byModule.set(mod, (set = new Set()));
|
|
205
|
+
set.add(name);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* The React render-registry module: each story's id → its render function.
|
|
210
|
+
*
|
|
211
|
+
* @param models - The stories React renders.
|
|
212
|
+
* @param moduleFor - The module a component is imported from, per target.
|
|
213
|
+
* @param exportFor - The name a component is exported under.
|
|
214
|
+
* @param declaredAttributes - The names a component declares that React
|
|
215
|
+
* would otherwise respell (`class`, `style`, an attribute React renames),
|
|
216
|
+
* which it takes as they are; every other attribute a story gives a
|
|
217
|
+
* component is spelled as React takes it.
|
|
218
|
+
* @returns The registry module's source: the `registry`, and the
|
|
219
|
+
* `reactAttributeNames` and `declaredAttributes` (of each story's root, by
|
|
220
|
+
* id) the React pane spells a story's live props with.
|
|
221
|
+
*/
|
|
222
|
+
export function emitReactRegistry(
|
|
223
|
+
models: ResolvedStory[],
|
|
224
|
+
moduleFor: (name: string, target: Target) => string,
|
|
225
|
+
exportFor: (name: string) => string = (name) => name,
|
|
226
|
+
declaredAttributes: (component: string) => readonly string[] = () => []
|
|
227
|
+
): string {
|
|
228
|
+
const declares = (component: string, name: string): boolean =>
|
|
229
|
+
declaredAttributes(component).includes(name);
|
|
230
|
+
const rootDeclarations = Object.fromEntries(
|
|
231
|
+
models
|
|
232
|
+
.map((model) => [storyRegistryId(model), declaredAttributes(model.name)])
|
|
233
|
+
.filter(([, names]) => names.length > 0)
|
|
234
|
+
);
|
|
235
|
+
const byModule = new Map<string, Set<string>>();
|
|
236
|
+
for (const model of models) {
|
|
237
|
+
addImport(byModule, moduleFor(model.name, 'react'), model.name);
|
|
238
|
+
for (const slotComponent of model.slotComponents)
|
|
239
|
+
addImport(byModule, moduleFor(slotComponent, 'react'), slotComponent);
|
|
240
|
+
}
|
|
241
|
+
// A component whose module export differs from its preview name is aliased on import
|
|
242
|
+
// (`Toast as NoticeToast`) so another package's clean export can't clash with a core name.
|
|
243
|
+
const importSpecifier = (name: string): string =>
|
|
244
|
+
exportFor(name) === name ? name : `${exportFor(name)} as ${name}`;
|
|
245
|
+
const importLines = [...byModule.entries()]
|
|
246
|
+
.map(
|
|
247
|
+
([mod, names]) =>
|
|
248
|
+
`import { ${[...names].map(importSpecifier).join(', ')} } from '${mod}';`
|
|
249
|
+
)
|
|
250
|
+
.join('\n');
|
|
251
|
+
return (
|
|
252
|
+
`// Generated by scaffold-ui-kit-preview - do not edit.\n` +
|
|
253
|
+
`${importLines}\n\n` +
|
|
254
|
+
`type RenderFn = (props: Record<string, any>) => any;\n\n` +
|
|
255
|
+
`export const registry: Record<string, RenderFn> = {\n` +
|
|
256
|
+
models.map((model) => reactEntry(model, declares)).join('\n') +
|
|
257
|
+
`\n};\n\n` +
|
|
258
|
+
`// How React spells the attributes it renames, by their HTML names.\n` +
|
|
259
|
+
`export const reactAttributeNames: Record<string, string> = ${JSON.stringify(REACT_ATTRIBUTE_NAMES)};\n\n` +
|
|
260
|
+
`// The names each story's root declares that React would otherwise respell.\n` +
|
|
261
|
+
`export const declaredAttributes: Record<string, string[]> = ${JSON.stringify(rootDeclarations)};\n`
|
|
262
|
+
);
|
|
263
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// The scenario editor's regeneration target. The host's editable node tree is, in effect,
|
|
2
|
+
// a one-entry story: a root component with `props` and a slot plan. We build a
|
|
3
|
+
// synthetic `ResolvedStory` from the editor payload and run it through the *existing*
|
|
4
|
+
// emitters verbatim - no new per-framework codegen - writing a `scratch/` sibling
|
|
5
|
+
// of the generated registries. The preview's normal .preview-generated → Vite-HMR path
|
|
6
|
+
// then reloads the previews.
|
|
7
|
+
//
|
|
8
|
+
// Loaded by the Vite dev middleware via `ssrLoadModule` (so it runs in Node with
|
|
9
|
+
// fs + the TS emitters compiled on the fly). `writeScratchStubs` is also called at
|
|
10
|
+
// `preview:generate` time so the previews' static scratch imports always resolve.
|
|
11
|
+
|
|
12
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
13
|
+
import { resolve } from 'node:path';
|
|
14
|
+
import { emitReactRegistry } from './react.js';
|
|
15
|
+
import { emitVueRegistry } from './vue.js';
|
|
16
|
+
import { emitSvelteRegistry, emitSvelteWrapper } from './svelte.js';
|
|
17
|
+
import { collectSlotComponents } from '../lib/slot-fill.js';
|
|
18
|
+
import { moduleOfSource } from '../lib/module-of-source.js';
|
|
19
|
+
import { ALL_TARGETS } from '../lib/types.js';
|
|
20
|
+
import type {
|
|
21
|
+
JsonValue,
|
|
22
|
+
SlotPlan,
|
|
23
|
+
StoryAround,
|
|
24
|
+
ResolvedStory,
|
|
25
|
+
Target,
|
|
26
|
+
} from '../lib/types.js';
|
|
27
|
+
|
|
28
|
+
/** The module-resolution data the generator serializes beside the registries
|
|
29
|
+
* (`<generatedRoot>/modules.json`), so this dev-middleware module needs no kit
|
|
30
|
+
* imports: the default module for unregistered slot children, each manual
|
|
31
|
+
* component's source module, per-target overrides, and export aliases. */
|
|
32
|
+
export interface ScratchModules {
|
|
33
|
+
defaultModule: string;
|
|
34
|
+
moduleByName: Record<string, string>;
|
|
35
|
+
importOverrideByName: Record<string, Partial<Record<Target, string>>>;
|
|
36
|
+
exportByName: Record<string, string>;
|
|
37
|
+
/** The scripted targets the kit builds; a scratch registry for any other stays
|
|
38
|
+
* empty. All three when absent. */
|
|
39
|
+
targets?: Target[];
|
|
40
|
+
/** The names each component declares that React would otherwise respell
|
|
41
|
+
* (`class`, `style`, an attribute React renames), which it takes as they
|
|
42
|
+
* are. A component missing here declares none. */
|
|
43
|
+
declaredAttributes?: Record<string, string[]>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// A component's per-target import module for the scratch registry, mirroring
|
|
47
|
+
// generate.ts: a per-component `importModule` override (a playground fixture
|
|
48
|
+
// outside its source's barrel) wins; otherwise the component's source module,
|
|
49
|
+
// defaulting to the kit's own source. Read from the generator's modules.json.
|
|
50
|
+
function loadResolvers(generatedRoot: string): {
|
|
51
|
+
moduleFor: (name: string, target: Target) => string;
|
|
52
|
+
exportFor: (name: string) => string;
|
|
53
|
+
targets: Target[];
|
|
54
|
+
declaredAttributes: (name: string) => string[];
|
|
55
|
+
} {
|
|
56
|
+
const data = JSON.parse(
|
|
57
|
+
readFileSync(resolve(generatedRoot, 'modules.json'), 'utf8')
|
|
58
|
+
) as ScratchModules;
|
|
59
|
+
return {
|
|
60
|
+
targets: data.targets ?? ALL_TARGETS,
|
|
61
|
+
moduleFor: (name, target) =>
|
|
62
|
+
data.importOverrideByName[name]?.[target] ??
|
|
63
|
+
moduleOfSource(data.moduleByName[name] ?? data.defaultModule, target),
|
|
64
|
+
exportFor: (name) => data.exportByName[name] ?? name,
|
|
65
|
+
declaredAttributes: (name) => data.declaredAttributes?.[name] ?? [],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Reserved story id the editor renders under, in every target registry. */
|
|
70
|
+
export const SCRATCH_ID = 'scratch';
|
|
71
|
+
|
|
72
|
+
/** The host posts this shape on every structural edit. */
|
|
73
|
+
export interface ScratchPayload {
|
|
74
|
+
/** The root component name (e.g. `Button`), imported per the modules.json mapping. */
|
|
75
|
+
rootComponent: string;
|
|
76
|
+
/** The root component's own scalar props (also delivered live via the props channel). */
|
|
77
|
+
props: Record<string, JsonValue>;
|
|
78
|
+
/** The editable tree: the root's default-slot fill (and any named slots). */
|
|
79
|
+
slots?: SlotPlan;
|
|
80
|
+
/** The content beside the root, carried unchanged from the story. */
|
|
81
|
+
around?: StoryAround;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildScratchModel(payload: ScratchPayload): ResolvedStory {
|
|
85
|
+
const slotComponents = new Set<string>();
|
|
86
|
+
collectSlotComponents(payload.slots?.default, slotComponents);
|
|
87
|
+
for (const fill of Object.values(payload.slots?.named ?? {})) {
|
|
88
|
+
collectSlotComponents(fill, slotComponents);
|
|
89
|
+
}
|
|
90
|
+
collectSlotComponents(payload.around?.before, slotComponents);
|
|
91
|
+
collectSlotComponents(payload.around?.after, slotComponents);
|
|
92
|
+
slotComponents.delete(payload.rootComponent);
|
|
93
|
+
return {
|
|
94
|
+
name: payload.rootComponent,
|
|
95
|
+
title: SCRATCH_ID,
|
|
96
|
+
omit: false,
|
|
97
|
+
standalone: true,
|
|
98
|
+
unlisted: false,
|
|
99
|
+
propTypes: [],
|
|
100
|
+
defaultProps: payload.props ?? {},
|
|
101
|
+
slots: payload.slots,
|
|
102
|
+
...(payload.around ? { around: payload.around } : {}),
|
|
103
|
+
scenarios: [],
|
|
104
|
+
slotComponents: [...slotComponents],
|
|
105
|
+
// The scratch story is rendered, never introspected for add-targets.
|
|
106
|
+
contentSlots: { default: false, named: [] },
|
|
107
|
+
// Unused by the scratch emitters (they emit the single model unconditionally), but
|
|
108
|
+
// the model shape requires it; the editor composes across all targets.
|
|
109
|
+
targets: ALL_TARGETS,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const EMPTY_REGISTRY = 'export const registry: Record<string, any> = {};\n';
|
|
114
|
+
// The React page reads the names each story's root declares beside its
|
|
115
|
+
// registry, so React's placeholder exports them too, none.
|
|
116
|
+
const EMPTY_REACT_REGISTRY =
|
|
117
|
+
EMPTY_REGISTRY +
|
|
118
|
+
'export const declaredAttributes: Record<string, string[]> = {};\n';
|
|
119
|
+
|
|
120
|
+
function scratchDirectory(generatedRoot: string): string {
|
|
121
|
+
const directory = resolve(generatedRoot, 'scratch');
|
|
122
|
+
mkdirSync(directory, { recursive: true });
|
|
123
|
+
return directory;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Placeholder registries so the previews' static scratch imports resolve before
|
|
127
|
+
* the first edit. Written at `preview:generate` time. */
|
|
128
|
+
export function writeScratchStubs(generatedRoot: string): void {
|
|
129
|
+
const directory = scratchDirectory(generatedRoot);
|
|
130
|
+
writeFileSync(resolve(directory, 'react.tsx'), EMPTY_REACT_REGISTRY);
|
|
131
|
+
writeFileSync(resolve(directory, 'vue.ts'), EMPTY_REGISTRY);
|
|
132
|
+
writeFileSync(resolve(directory, 'registry.ts'), EMPTY_REGISTRY);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Regenerate the scratch registries from an editor payload (one synthetic story
|
|
136
|
+
* through the real emitters). Returns the absolute paths written, so the caller
|
|
137
|
+
* can invalidate them in Vite's module graph before the previews reload (a plain
|
|
138
|
+
* reload can otherwise serve Vite's stale transform if the file-change event
|
|
139
|
+
* hasn't been processed yet). */
|
|
140
|
+
export function writeScratch(
|
|
141
|
+
payload: ScratchPayload,
|
|
142
|
+
generatedRoot: string
|
|
143
|
+
): string[] {
|
|
144
|
+
const model = buildScratchModel(payload);
|
|
145
|
+
const directory = scratchDirectory(generatedRoot);
|
|
146
|
+
const { moduleFor, exportFor, targets, declaredAttributes } =
|
|
147
|
+
loadResolvers(generatedRoot);
|
|
148
|
+
// A target the kit does not build keeps its empty registry: its
|
|
149
|
+
// components have no module to import.
|
|
150
|
+
const files: [string, string][] = [
|
|
151
|
+
[
|
|
152
|
+
resolve(directory, 'react.tsx'),
|
|
153
|
+
targets.includes('react')
|
|
154
|
+
? emitReactRegistry([model], moduleFor, exportFor, declaredAttributes)
|
|
155
|
+
: EMPTY_REACT_REGISTRY,
|
|
156
|
+
],
|
|
157
|
+
[
|
|
158
|
+
resolve(directory, 'vue.ts'),
|
|
159
|
+
targets.includes('vue')
|
|
160
|
+
? emitVueRegistry([model], moduleFor, exportFor)
|
|
161
|
+
: EMPTY_REGISTRY,
|
|
162
|
+
],
|
|
163
|
+
];
|
|
164
|
+
if (targets.includes('svelte')) {
|
|
165
|
+
const wrapper = emitSvelteWrapper(model, moduleFor, exportFor);
|
|
166
|
+
files.push(
|
|
167
|
+
[resolve(directory, 'registry.ts'), emitSvelteRegistry([model])],
|
|
168
|
+
[resolve(directory, wrapper.fileName), wrapper.source]
|
|
169
|
+
);
|
|
170
|
+
} else {
|
|
171
|
+
files.push([resolve(directory, 'registry.ts'), EMPTY_REGISTRY]);
|
|
172
|
+
}
|
|
173
|
+
for (const [path, source] of files) writeFileSync(path, source);
|
|
174
|
+
return files.map(([path]) => path);
|
|
175
|
+
}
|