@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,9 @@
|
|
|
1
|
+
import type { ResolvedStory, Target } from '../lib/types.js';
|
|
2
|
+
export interface SvelteWrapper {
|
|
3
|
+
fileName: string;
|
|
4
|
+
source: string;
|
|
5
|
+
}
|
|
6
|
+
/** One `<Name>.render.svelte` wrapper per component (always - keeps the registry
|
|
7
|
+
* uniform; slotless components render `<Name {...rest} />`). */
|
|
8
|
+
export declare function emitSvelteWrapper(model: ResolvedStory, moduleFor: (name: string, target: Target) => string, exportFor?: (name: string) => string): SvelteWrapper;
|
|
9
|
+
export declare function emitSvelteRegistry(models: ResolvedStory[]): string;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
// Emit the Svelte render layer. Svelte content regions aren't props, so each
|
|
2
|
+
// component gets a thin `<Name>.render.svelte` wrapper that forwards `props`
|
|
3
|
+
// (one object whose props are each a signal of their own, keeping controls
|
|
4
|
+
// live without remounting) and fills its regions. The registry maps component id → its wrapper
|
|
5
|
+
// component. The generated components are runes components that expose
|
|
6
|
+
// their regions as **named snippet props**, so the wrapper is a runes component
|
|
7
|
+
// filling them with `{#snippet name()}...{/snippet}` (the default region stays the
|
|
8
|
+
// implicit `children` snippet - loose content between the tags).
|
|
9
|
+
import { effectiveDefaultSlot, namedSlots, serialize, svelteText, } from './common.js';
|
|
10
|
+
import { storyRegistryId } from '../lib/scenario-story.js';
|
|
11
|
+
import { collectParts } from '../lib/slot-fill.js';
|
|
12
|
+
import { isVoidElement } from '../lib/void-elements.js';
|
|
13
|
+
function svelteProps(props) {
|
|
14
|
+
return props
|
|
15
|
+
? ' ' +
|
|
16
|
+
Object.entries(props)
|
|
17
|
+
.map(([name, value]) => typeof value === 'boolean' && value
|
|
18
|
+
? name
|
|
19
|
+
: `${name}={${serialize(value)}}`)
|
|
20
|
+
.join(' ')
|
|
21
|
+
: '';
|
|
22
|
+
}
|
|
23
|
+
// Fill a component's named region: a runes named snippet prop, `{#snippet
|
|
24
|
+
// name()}...{/snippet}` (the region name is the snippet prop the generated
|
|
25
|
+
// component declares). Shared by nested elements and buildWrapper's top-level
|
|
26
|
+
// named regions.
|
|
27
|
+
function namedSlotMarkup(slots) {
|
|
28
|
+
return Object.entries(slots ?? {})
|
|
29
|
+
.map(([slot, fill]) => {
|
|
30
|
+
// A scoped fill becomes a snippet named per target (`fill.slot.svelte`) receiving
|
|
31
|
+
// the destructured scope - Svelte 5's render-prop analog; empty body omits it.
|
|
32
|
+
if (fill.kind === 'scoped')
|
|
33
|
+
return fill.svelte
|
|
34
|
+
? `{#snippet ${fill.slot.svelte}({ ${fill.parameters.join(', ')} })}${fill.svelte}{/snippet}`
|
|
35
|
+
: '';
|
|
36
|
+
return `{#snippet ${slot}()}${slotSvelte(fill)}{/snippet}`;
|
|
37
|
+
})
|
|
38
|
+
.join('');
|
|
39
|
+
}
|
|
40
|
+
function elementSvelte(fill) {
|
|
41
|
+
const inner = `${fill.children ? slotSvelte(fill.children) : ''}${namedSlotMarkup(fill.slots)}`;
|
|
42
|
+
return `<${fill.component}${svelteProps(fill.props)}>${inner}</${fill.component}>`;
|
|
43
|
+
}
|
|
44
|
+
function htmlSvelte(fill) {
|
|
45
|
+
// A void element (img, input, br) takes no children and no closing tag.
|
|
46
|
+
if (isVoidElement(fill.tag))
|
|
47
|
+
return `<${fill.tag}${svelteProps(fill.props)} />`;
|
|
48
|
+
const inner = fill.children ? slotSvelte(fill.children) : '';
|
|
49
|
+
return `<${fill.tag}${svelteProps(fill.props)}>${inner}</${fill.tag}>`;
|
|
50
|
+
}
|
|
51
|
+
function slotSvelte(fill) {
|
|
52
|
+
if (fill.kind === 'text')
|
|
53
|
+
return svelteText(fill.value);
|
|
54
|
+
if (fill.kind === 'raw')
|
|
55
|
+
return fill.svelte;
|
|
56
|
+
// A call to a part: the snippet the wrapper declares at its top.
|
|
57
|
+
if (fill.kind === 'part')
|
|
58
|
+
return `{@render ${fill.name}(${serialize(fill.props ?? {})})}`;
|
|
59
|
+
// A scoped fill is only valid at a top-level named slot (namedSlotMarkup); ignored nested.
|
|
60
|
+
if (fill.kind === 'scoped')
|
|
61
|
+
return '';
|
|
62
|
+
if (fill.kind === 'fragment')
|
|
63
|
+
return fill.children.map(slotSvelte).join('');
|
|
64
|
+
if (fill.kind === 'html')
|
|
65
|
+
return htmlSvelte(fill);
|
|
66
|
+
return elementSvelte(fill);
|
|
67
|
+
}
|
|
68
|
+
function defaultSvelte(def) {
|
|
69
|
+
if (!def)
|
|
70
|
+
return '';
|
|
71
|
+
if (def.kind === 'propText')
|
|
72
|
+
return `{props[${JSON.stringify(def.prop)}]}`;
|
|
73
|
+
// Scoped content for the default slot is the `children` snippet.
|
|
74
|
+
if (def.kind === 'scoped')
|
|
75
|
+
return namedSlotMarkup({ default: def });
|
|
76
|
+
return slotSvelte(def);
|
|
77
|
+
}
|
|
78
|
+
function buildWrapper(model, def, named, moduleFor, exportFor) {
|
|
79
|
+
const imports = [model.name, ...model.slotComponents].filter((name, i, names) => names.indexOf(name) === i);
|
|
80
|
+
// Group imports by source module (insertion-ordered, the core source first) - a
|
|
81
|
+
// wrapper for another package's component imports it from that package's module and a core
|
|
82
|
+
// child from the core one.
|
|
83
|
+
const byModule = new Map();
|
|
84
|
+
for (const name of imports) {
|
|
85
|
+
const mod = moduleFor(name, 'svelte');
|
|
86
|
+
(byModule.get(mod) ?? byModule.set(mod, []).get(mod)).push(name);
|
|
87
|
+
}
|
|
88
|
+
// Alias another package's clean export to its unique preview name (`Toast as NoticeToast`).
|
|
89
|
+
const importSpecifier = (name) => exportFor(name) === name ? name : `${exportFor(name)} as ${name}`;
|
|
90
|
+
const importLines = [...byModule.entries()]
|
|
91
|
+
.map(([mod, names]) => ` import { ${names.map(importSpecifier).join(', ')} } from '${mod}';`)
|
|
92
|
+
.join('\n');
|
|
93
|
+
const stripsChild = def?.kind === 'propText';
|
|
94
|
+
// Runes wrapper: `props` is one object whose props are each a signal of
|
|
95
|
+
// their own, so the component reads each prop on its own and a change to
|
|
96
|
+
// one leaves the others as they are (a component's own two-way value
|
|
97
|
+
// among them). When the default region is a `propText` prop it is
|
|
98
|
+
// rendered as the child and left out of the spread by a view of `props`
|
|
99
|
+
// that still reads each prop on its own - a copy would read them all.
|
|
100
|
+
const reactive = stripsChild
|
|
101
|
+
? ` const rest = $derived(\n` +
|
|
102
|
+
` new Proxy(props, {\n` +
|
|
103
|
+
` get: (target, key) => (key === ${JSON.stringify(def.prop)} ? undefined : Reflect.get(target, key)),\n` +
|
|
104
|
+
` has: (target, key) => key !== ${JSON.stringify(def.prop)} && Reflect.has(target, key),\n` +
|
|
105
|
+
` ownKeys: (target) => Reflect.ownKeys(target).filter((key) => key !== ${JSON.stringify(def.prop)}),\n` +
|
|
106
|
+
` })\n` +
|
|
107
|
+
` );`
|
|
108
|
+
: ` const rest = $derived(props);`;
|
|
109
|
+
const namedMarkup = namedSlotMarkup(named);
|
|
110
|
+
const defaultMarkup = defaultSvelte(def);
|
|
111
|
+
// Svelte 5 turns a whitespace-only component body into a real (empty)
|
|
112
|
+
// `children` snippet, which suppresses the component's default-slot fallback
|
|
113
|
+
// - the opposite of a bare consumer usage. React trims whitespace-only JSX
|
|
114
|
+
// children and Vue drops the whitespace-only slot, so to keep the three
|
|
115
|
+
// targets' stories equivalent the wrapper self-closes when there is nothing
|
|
116
|
+
// to fill, and emits only non-empty region lines otherwise.
|
|
117
|
+
const regionLines = [defaultMarkup, namedMarkup]
|
|
118
|
+
.filter((markup) => markup !== '')
|
|
119
|
+
.map((markup) => ` ${markup}\n`)
|
|
120
|
+
.join('');
|
|
121
|
+
const componentMarkup = regionLines
|
|
122
|
+
? `<${model.name} {...rest}>\n${regionLines}</${model.name}>\n`
|
|
123
|
+
: `<${model.name} {...rest} />\n`;
|
|
124
|
+
// The content beside the component: its siblings, at the markup's top level.
|
|
125
|
+
const before = model.around?.before;
|
|
126
|
+
const after = model.around?.after;
|
|
127
|
+
const mountMarkup = (before ? `${slotSvelte(before)}\n` : '') +
|
|
128
|
+
componentMarkup +
|
|
129
|
+
(after ? `${slotSvelte(after)}\n` : '');
|
|
130
|
+
// The story's parts, snippets at the top of the markup: inside the
|
|
131
|
+
// component's tags a snippet would be a prop of the component's.
|
|
132
|
+
const partMarkup = [
|
|
133
|
+
...collectParts([
|
|
134
|
+
before,
|
|
135
|
+
model.slots?.default,
|
|
136
|
+
...Object.values(named),
|
|
137
|
+
after,
|
|
138
|
+
]).values(),
|
|
139
|
+
]
|
|
140
|
+
.map((part) => `{#snippet ${part.name}({ ${part.parameters.join(', ')} }: Record<string, any>)}${part.svelte}{/snippet}\n`)
|
|
141
|
+
.join('');
|
|
142
|
+
return (`<script lang="ts">\n` +
|
|
143
|
+
`${importLines}\n` +
|
|
144
|
+
` let { props = {} }: { props?: Record<string, any> } = $props();\n` +
|
|
145
|
+
`${reactive}\n` +
|
|
146
|
+
`</script>\n\n` +
|
|
147
|
+
partMarkup +
|
|
148
|
+
mountMarkup);
|
|
149
|
+
}
|
|
150
|
+
/** The name a story's wrapper file and its import take: the component's,
|
|
151
|
+
* or a scenario story's own. */
|
|
152
|
+
function renderNameOf(model) {
|
|
153
|
+
return model.scenarioStory?.renderName ?? model.name;
|
|
154
|
+
}
|
|
155
|
+
/** One `<Name>.render.svelte` wrapper per component (always - keeps the registry
|
|
156
|
+
* uniform; slotless components render `<Name {...rest} />`). */
|
|
157
|
+
export function emitSvelteWrapper(model, moduleFor, exportFor = (name) => name) {
|
|
158
|
+
const def = effectiveDefaultSlot(model);
|
|
159
|
+
const named = namedSlots(model);
|
|
160
|
+
return {
|
|
161
|
+
fileName: `${renderNameOf(model)}.render.svelte`,
|
|
162
|
+
source: buildWrapper(model, def, named, moduleFor, exportFor),
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export function emitSvelteRegistry(models) {
|
|
166
|
+
const imports = models
|
|
167
|
+
.map((model) => `import ${renderNameOf(model)}_render from './${renderNameOf(model)}.render.svelte';`)
|
|
168
|
+
.join('\n');
|
|
169
|
+
const entries = models
|
|
170
|
+
.map((model) => ` ${JSON.stringify(storyRegistryId(model))}: ${renderNameOf(model)}_render,`)
|
|
171
|
+
.join('\n');
|
|
172
|
+
return (`// Generated by scaffold-ui-kit-preview - do not edit.\n` +
|
|
173
|
+
`${imports}\n\n` +
|
|
174
|
+
`export const registry: Record<string, any> = {\n` +
|
|
175
|
+
entries +
|
|
176
|
+
`\n};\n`);
|
|
177
|
+
}
|
package/dist/emit/vue.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Emit the Vue render-registry: a map of component id → a render unit
|
|
2
|
+
// `{ components, template }`. Slots aren't props in Vue, so slot content is
|
|
3
|
+
// rendered through a template (default slot inline, named slots via
|
|
4
|
+
// <template #name>); `props` flows via v-bind="props". The preview entry mounts
|
|
5
|
+
// each unit with a reactive `props` and the unit's components exposed in
|
|
6
|
+
// setup, so a template expression reads a component as a value
|
|
7
|
+
// (`:as="Button"`), as a script-setup import is read in an SFC.
|
|
8
|
+
import { effectiveDefaultSlot, joinVue, namedSlots, vueBoundAttribute, vueText, } from './common.js';
|
|
9
|
+
import { storyRegistryId } from '../lib/scenario-story.js';
|
|
10
|
+
import { collectParts } from '../lib/slot-fill.js';
|
|
11
|
+
import { isVoidElement } from '../lib/void-elements.js';
|
|
12
|
+
function vueProps(props) {
|
|
13
|
+
return props
|
|
14
|
+
? ' ' +
|
|
15
|
+
Object.entries(props)
|
|
16
|
+
.map(([name, value]) => typeof value === 'boolean' && value
|
|
17
|
+
? name
|
|
18
|
+
: `:${name}=${vueBoundAttribute(value)}`)
|
|
19
|
+
.join(' ')
|
|
20
|
+
: '';
|
|
21
|
+
}
|
|
22
|
+
function elementVue(fill) {
|
|
23
|
+
// Named slots → `<template #name>` inside the element, like vueEntry's namedTemplates;
|
|
24
|
+
// default content first, then the named templates.
|
|
25
|
+
const namedTemplates = Object.entries(fill.slots ?? {})
|
|
26
|
+
.map(([slot, slotFill]) => namedTemplateVue(slot, slotFill))
|
|
27
|
+
.join('');
|
|
28
|
+
const inner = `${fill.children ? slotVue(fill.children) : ''}${namedTemplates}`;
|
|
29
|
+
return `<${fill.component}${vueProps(fill.props)}>${inner}</${fill.component}>`;
|
|
30
|
+
}
|
|
31
|
+
function htmlVue(fill) {
|
|
32
|
+
// A void element (img, input, br) takes no children and no closing tag.
|
|
33
|
+
if (isVoidElement(fill.tag))
|
|
34
|
+
return `<${fill.tag}${vueProps(fill.props)} />`;
|
|
35
|
+
const inner = fill.children ? slotVue(fill.children) : '';
|
|
36
|
+
return `<${fill.tag}${vueProps(fill.props)}>${inner}</${fill.tag}>`;
|
|
37
|
+
}
|
|
38
|
+
function slotVue(fill) {
|
|
39
|
+
if (fill.kind === 'text')
|
|
40
|
+
return vueText(fill.value);
|
|
41
|
+
if (fill.kind === 'raw')
|
|
42
|
+
return fill.vue;
|
|
43
|
+
// A call to a part: a component among the story's own.
|
|
44
|
+
if (fill.kind === 'part')
|
|
45
|
+
return `<${fill.name}${vueProps(fill.props)} />`;
|
|
46
|
+
// A scoped fill is only valid at a top-level named slot (vueEntry); ignored nested.
|
|
47
|
+
if (fill.kind === 'scoped')
|
|
48
|
+
return '';
|
|
49
|
+
if (fill.kind === 'fragment')
|
|
50
|
+
return joinVue(fill.children.map(slotVue));
|
|
51
|
+
if (fill.kind === 'html')
|
|
52
|
+
return htmlVue(fill);
|
|
53
|
+
return elementVue(fill);
|
|
54
|
+
}
|
|
55
|
+
/** A named-slot `<template>` - a scoped fill binds the slot's scope
|
|
56
|
+
* (`#name="{ args }"`), a plain fill renders inline. Empty scoped bodies omit the
|
|
57
|
+
* template. Shared by vueEntry and elementVue's nested named slots. */
|
|
58
|
+
function namedTemplateVue(slot, fill) {
|
|
59
|
+
if (fill.kind === 'scoped')
|
|
60
|
+
return fill.vue
|
|
61
|
+
? `<template #${fill.slot.vue}="{ ${fill.parameters.join(', ')} }">${fill.vue}</template>`
|
|
62
|
+
: '';
|
|
63
|
+
return `<template #${slot}>${slotVue(fill)}</template>`;
|
|
64
|
+
}
|
|
65
|
+
function defaultVue(def) {
|
|
66
|
+
if (!def)
|
|
67
|
+
return '';
|
|
68
|
+
if (def.kind === 'propText')
|
|
69
|
+
return `{{ props.${def.prop} }}`;
|
|
70
|
+
// Scoped content for the default slot binds its scope on `#default`.
|
|
71
|
+
if (def.kind === 'scoped')
|
|
72
|
+
return namedTemplateVue('default', def);
|
|
73
|
+
return slotVue(def);
|
|
74
|
+
}
|
|
75
|
+
// The `import * as` namespace per referenced module: the first module seen (the
|
|
76
|
+
// kit's own source - story order puts it first) keeps the terse `d`; every other
|
|
77
|
+
// module gets `d_<sanitized>`. First-seen assignment bakes in no kit name.
|
|
78
|
+
function vueNamespaces(models, moduleFor) {
|
|
79
|
+
const namespaces = new Map();
|
|
80
|
+
for (const model of models) {
|
|
81
|
+
for (const name of [model.name, ...model.slotComponents]) {
|
|
82
|
+
const mod = moduleFor(name, 'vue');
|
|
83
|
+
if (!namespaces.has(mod)) {
|
|
84
|
+
namespaces.set(mod, namespaces.size === 0
|
|
85
|
+
? 'd'
|
|
86
|
+
: 'd_' +
|
|
87
|
+
mod
|
|
88
|
+
.replace(/\/(vue)$/, '')
|
|
89
|
+
.replace(/[^a-z0-9]+/gi, '_')
|
|
90
|
+
.replace(/^_+|_+$/g, ''));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return namespaces;
|
|
95
|
+
}
|
|
96
|
+
function vueEntry(model, moduleFor, exportFor, namespaces) {
|
|
97
|
+
const def = effectiveDefaultSlot(model);
|
|
98
|
+
const named = namedSlots(model);
|
|
99
|
+
const namedTemplates = Object.entries(named)
|
|
100
|
+
.map(([slot, fill]) => namedTemplateVue(slot, fill))
|
|
101
|
+
.join('');
|
|
102
|
+
const inner = `${defaultVue(def)}${namedTemplates}`;
|
|
103
|
+
// The placeholder text is the default slot's content. Bound too, a prop the
|
|
104
|
+
// component doesn't declare falls through to its root element, where Vue
|
|
105
|
+
// sets it as a DOM property (`children` is read-only there), so it's left
|
|
106
|
+
// out of the binding; a declared prop of that name stays bound.
|
|
107
|
+
const bound = def?.kind === 'propText' &&
|
|
108
|
+
!model.propTypes.some((propType) => propType.name === def.prop)
|
|
109
|
+
? `(({ ${def.prop}: _, ...rest }) => rest)(props)`
|
|
110
|
+
: 'props';
|
|
111
|
+
const component = inner
|
|
112
|
+
? `<${model.name} v-bind="${bound}">${inner}</${model.name}>`
|
|
113
|
+
: `<${model.name} v-bind="${bound}" />`;
|
|
114
|
+
// The content beside the component: its siblings, as the template's other
|
|
115
|
+
// roots.
|
|
116
|
+
const before = model.around?.before;
|
|
117
|
+
const after = model.around?.after;
|
|
118
|
+
const template = `${before ? slotVue(before) : ''}${component}${after ? slotVue(after) : ''}`;
|
|
119
|
+
const comps = [model.name, ...model.slotComponents].filter((name, i, names) => names.indexOf(name) === i);
|
|
120
|
+
// Local template name → the module's exported member (aliased when they differ).
|
|
121
|
+
const componentMap = comps
|
|
122
|
+
.map((component) => `${component}: ${namespaces.get(moduleFor(component, 'vue'))}.${exportFor(component)}`)
|
|
123
|
+
.join(', ');
|
|
124
|
+
// A part is a component of the story's, named so its template can call
|
|
125
|
+
// itself, with the story's components in reach: as tags, and as values its
|
|
126
|
+
// expressions read (`:as="Button"`), as a script-setup import is in an SFC.
|
|
127
|
+
const parts = [
|
|
128
|
+
...collectParts([
|
|
129
|
+
before,
|
|
130
|
+
model.slots?.default,
|
|
131
|
+
...Object.values(named),
|
|
132
|
+
after,
|
|
133
|
+
]).values(),
|
|
134
|
+
].map((part) => `, ${part.name}: { name: ${JSON.stringify(part.name)}, props: ${JSON.stringify(part.parameters)}, components: { ${componentMap} }, setup: () => ({ ${componentMap} }), template: ${JSON.stringify(part.vue)} }`);
|
|
135
|
+
return ` ${JSON.stringify(storyRegistryId(model))}: { components: { ${componentMap}${parts.join('')} }, template: ${JSON.stringify(template)} },`;
|
|
136
|
+
}
|
|
137
|
+
export function emitVueRegistry(models, moduleFor, exportFor = (name) => name) {
|
|
138
|
+
// One `import * as <ns>` per referenced module, insertion-ordered (the kit's
|
|
139
|
+
// own source first).
|
|
140
|
+
const namespaces = vueNamespaces(models, moduleFor);
|
|
141
|
+
const importLines = [...namespaces.entries()]
|
|
142
|
+
.map(([mod, ns]) => `import * as ${ns} from '${mod}';`)
|
|
143
|
+
.join('\n');
|
|
144
|
+
return (`// Generated by scaffold-ui-kit-preview - do not edit.\n` +
|
|
145
|
+
`${importLines}\n\n` +
|
|
146
|
+
`export interface VueUnit { components: Record<string, any>; template: string; }\n\n` +
|
|
147
|
+
`export const registry: Record<string, VueUnit> = {\n` +
|
|
148
|
+
models
|
|
149
|
+
.map((model) => vueEntry(model, moduleFor, exportFor, namespaces))
|
|
150
|
+
.join('\n') +
|
|
151
|
+
`\n};\n`);
|
|
152
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// Discover component templates by reading a source's templates directory,
|
|
2
|
+
// as the kit's build reads it, and importing each (default export = the
|
|
3
|
+
// defineTemplate object, which is plain data - defineTemplate is identity).
|
|
4
|
+
// Hand-written components live elsewhere, so they are naturally excluded.
|
|
5
|
+
import { readdirSync } from 'node:fs';
|
|
6
|
+
import { resolve } from 'node:path';
|
|
7
|
+
import { componentNameFromFile } from './lib/naming.js';
|
|
8
|
+
import { isTemplateFile } from './lib/template-files.js';
|
|
9
|
+
import { DeclarationResolver, resolveNamedType } from './props-from.js';
|
|
10
|
+
import { partitionImports } from '@js-template-engine/core';
|
|
11
|
+
/** Deep-walk a template tree collecting every `{ type: 'slot' }`: an unnamed one
|
|
12
|
+
* sets the default flag, a named one is added to the set, and one whose content
|
|
13
|
+
* must take its scope (`scopeRequired` beside `exposes`) is added, by name or as
|
|
14
|
+
* `default`, to `scopeRequired`. Walks every object value, `children` included,
|
|
15
|
+
* so slots projected through a `slots: { ... }` map on a composed element (e.g.
|
|
16
|
+
* Button forwarding its `icon` slot into IconLabel) are found too. */
|
|
17
|
+
function collectSlots(node, into) {
|
|
18
|
+
if (!node || typeof node !== 'object')
|
|
19
|
+
return;
|
|
20
|
+
if (Array.isArray(node)) {
|
|
21
|
+
for (const child of node)
|
|
22
|
+
collectSlots(child, into);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const record = node;
|
|
26
|
+
if (record.type === 'slot') {
|
|
27
|
+
const exposes = record.exposes;
|
|
28
|
+
const scoped = Boolean(exposes && typeof exposes === 'object' && Object.keys(exposes).length > 0);
|
|
29
|
+
const named = typeof record.name === 'string' && record.name;
|
|
30
|
+
if (named)
|
|
31
|
+
into.named.add(named);
|
|
32
|
+
else {
|
|
33
|
+
into.default = true;
|
|
34
|
+
if (scoped)
|
|
35
|
+
into.defaultScoped = true;
|
|
36
|
+
}
|
|
37
|
+
if (scoped && record.scopeRequired === true)
|
|
38
|
+
into.scopeRequired.add(named || 'default');
|
|
39
|
+
}
|
|
40
|
+
for (const value of Object.values(record))
|
|
41
|
+
collectSlots(value, into);
|
|
42
|
+
}
|
|
43
|
+
/** Whether any element in a template tree sets `passthrough`. */
|
|
44
|
+
function hasPassthrough(node) {
|
|
45
|
+
if (!node || typeof node !== 'object')
|
|
46
|
+
return false;
|
|
47
|
+
if (Array.isArray(node))
|
|
48
|
+
return node.some(hasPassthrough);
|
|
49
|
+
const record = node;
|
|
50
|
+
return (record.passthrough === true || Object.values(record).some(hasPassthrough));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The props a discriminated surface root's branches declare, merged into one
|
|
54
|
+
* definition per name: the branches' types joined (a `true` in one and a
|
|
55
|
+
* `false` in another read as `boolean`), required only when every branch
|
|
56
|
+
* requires it. Empty for any other root.
|
|
57
|
+
*/
|
|
58
|
+
function branchProps(children) {
|
|
59
|
+
const roots = Array.isArray(children) ? children : [children];
|
|
60
|
+
if (roots.length !== 1)
|
|
61
|
+
return {};
|
|
62
|
+
const [root] = roots;
|
|
63
|
+
if (root?.type !== 'conditional' || !Array.isArray(root.conditions))
|
|
64
|
+
return {};
|
|
65
|
+
const branches = root.conditions.map((condition) => condition.props ?? {});
|
|
66
|
+
const names = [...new Set(branches.flatMap((props) => Object.keys(props)))];
|
|
67
|
+
const merged = {};
|
|
68
|
+
for (const name of names) {
|
|
69
|
+
const definitions = branches.map((props) => props[name]);
|
|
70
|
+
const present = definitions.filter((definition) => definition !== undefined);
|
|
71
|
+
const types = [...new Set(present.map((definition) => definition.type))];
|
|
72
|
+
const type = types.includes('true') && types.includes('false')
|
|
73
|
+
? [
|
|
74
|
+
'boolean',
|
|
75
|
+
...types.filter((each) => each !== 'true' && each !== 'false'),
|
|
76
|
+
].join(' | ')
|
|
77
|
+
: types.join(' | ');
|
|
78
|
+
const description = present.find((definition) => definition.description)?.description;
|
|
79
|
+
const documentedDefault = present.find((definition) => definition.documentedDefault)?.documentedDefault;
|
|
80
|
+
merged[name] = {
|
|
81
|
+
type,
|
|
82
|
+
...(definitions.every((definition) => definition?.required === true)
|
|
83
|
+
? { required: true }
|
|
84
|
+
: {}),
|
|
85
|
+
...(description ? { description } : {}),
|
|
86
|
+
...(documentedDefault ? { documentedDefault } : {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return merged;
|
|
90
|
+
}
|
|
91
|
+
/** A component's shared props followed by any branch prop it does not share. */
|
|
92
|
+
function withBranchProps(shared, children) {
|
|
93
|
+
const props = { ...shared };
|
|
94
|
+
for (const [name, definition] of Object.entries(branchProps(children))) {
|
|
95
|
+
if (!(name in props))
|
|
96
|
+
props[name] = definition;
|
|
97
|
+
}
|
|
98
|
+
return props;
|
|
99
|
+
}
|
|
100
|
+
/** The import statements a template declares, from every target's
|
|
101
|
+
* `extensions.<target>.imports`: the same names, from the same modules. */
|
|
102
|
+
function templateImports(extensions) {
|
|
103
|
+
if (!extensions || typeof extensions !== 'object')
|
|
104
|
+
return [];
|
|
105
|
+
const entries = Object.values(extensions)
|
|
106
|
+
.flatMap((target) => target && typeof target === 'object'
|
|
107
|
+
? (target.imports ?? [])
|
|
108
|
+
: [])
|
|
109
|
+
.filter((entry) => typeof entry === 'string');
|
|
110
|
+
return [...new Set(partitionImports(entries).importStatements)];
|
|
111
|
+
}
|
|
112
|
+
/** The props with each named type expanded through the template's imports, so
|
|
113
|
+
* a prop typed by an alias from another module reads as its members. */
|
|
114
|
+
function withResolvedTypes(props, templatePath, imports, resolver) {
|
|
115
|
+
if (imports.length === 0)
|
|
116
|
+
return props;
|
|
117
|
+
return Object.fromEntries(Object.entries(props).map(([name, definition]) => {
|
|
118
|
+
const resolvedType = resolveNamedType(definition.type, templatePath, imports, resolver);
|
|
119
|
+
return [
|
|
120
|
+
name,
|
|
121
|
+
resolvedType ? { ...definition, resolvedType } : definition,
|
|
122
|
+
];
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
export async function enumerateComponents(componentsDirectory, importModule) {
|
|
126
|
+
const files = readdirSync(componentsDirectory).filter(isTemplateFile).sort();
|
|
127
|
+
const resolver = new DeclarationResolver(componentsDirectory);
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
const mod = (await importModule(resolve(componentsDirectory, file)));
|
|
131
|
+
const template = mod.default;
|
|
132
|
+
if (!template || typeof template !== 'object')
|
|
133
|
+
continue;
|
|
134
|
+
const slots = {
|
|
135
|
+
default: false,
|
|
136
|
+
defaultScoped: false,
|
|
137
|
+
named: new Set(),
|
|
138
|
+
scopeRequired: new Set(),
|
|
139
|
+
};
|
|
140
|
+
collectSlots(template.children, slots);
|
|
141
|
+
out.push({
|
|
142
|
+
name: template.name ?? componentNameFromFile(file),
|
|
143
|
+
file,
|
|
144
|
+
template: template,
|
|
145
|
+
...(typeof template.description === 'string'
|
|
146
|
+
? { description: template.description }
|
|
147
|
+
: {}),
|
|
148
|
+
props: withResolvedTypes(withBranchProps(template.props ?? {}, template.children), resolve(componentsDirectory, file), templateImports(template.extensions), resolver),
|
|
149
|
+
hasDefaultSlot: slots.default,
|
|
150
|
+
...(slots.defaultScoped ? { defaultSlotScoped: true } : {}),
|
|
151
|
+
slotNames: [...slots.named],
|
|
152
|
+
...(slots.scopeRequired.size > 0
|
|
153
|
+
? { scopeRequiredSlots: [...slots.scopeRequired] }
|
|
154
|
+
: {}),
|
|
155
|
+
...(hasPassthrough(template.children) ? { passthrough: true } : {}),
|
|
156
|
+
...(typeof template.rest?.tag === 'string'
|
|
157
|
+
? { rest: { tag: template.rest.tag } }
|
|
158
|
+
: {}),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { PreviewContext } from './context.js';
|
|
2
|
+
/** How `generatePreview` treats the generated tree already on disk. */
|
|
3
|
+
export interface GeneratePreviewOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Whether the generated tree is emptied before writing (the default). With
|
|
6
|
+
* `false`, as a running development server regenerates, the tree is kept:
|
|
7
|
+
* Vite's dependency cache and the scenario editor's story stay, a file is
|
|
8
|
+
* written only when its content changed, and a registry file no component
|
|
9
|
+
* produces any more is removed.
|
|
10
|
+
*/
|
|
11
|
+
clean?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Whether to print the html source of every story's scenarios into the
|
|
14
|
+
* served tree, one `html-source/<component id>.json` per component, as the
|
|
15
|
+
* static build reads it. Off by default: the development server prints
|
|
16
|
+
* html on request.
|
|
17
|
+
*/
|
|
18
|
+
htmlSources?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/** What `generatePreview` changed on disk. */
|
|
21
|
+
export interface GeneratePreviewResult {
|
|
22
|
+
/** The absolute paths written, because they were new or their content changed. */
|
|
23
|
+
written: string[];
|
|
24
|
+
/** The absolute paths of registry files removed because nothing produces them. */
|
|
25
|
+
removed: string[];
|
|
26
|
+
/** The absolute paths of the kit's theme sources the generation copied:
|
|
27
|
+
* each theme's stylesheet and its assets file or directory. */
|
|
28
|
+
themeSources: string[];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Generates the preview into `<kitRoot>/.preview-generated/`: a render
|
|
32
|
+
* registry per target, the Svelte wrappers, the module-resolution data the
|
|
33
|
+
* scenario editor reads, the host manifest, and the themes' stylesheets.
|
|
34
|
+
*
|
|
35
|
+
* @param context - The kit's preview context.
|
|
36
|
+
* @param options - Whether the generated tree is emptied first.
|
|
37
|
+
* @returns The files written and removed.
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* await generatePreview(loadPreviewContext(kitRoot));
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function generatePreview(context: PreviewContext, options?: GeneratePreviewOptions): Promise<GeneratePreviewResult>;
|