@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,442 @@
|
|
|
1
|
+
// The `dev` command's server: generate, serve, and keep the preview in step
|
|
2
|
+
// with the kit while it runs. The kit's templates, story files and manual
|
|
3
|
+
// component modules load in Node for generation, not through Vite, so Vite
|
|
4
|
+
// does not watch them; this module adds them to Vite's watcher and
|
|
5
|
+
// regenerates when one changes. The generated registries and manifest are in
|
|
6
|
+
// Vite's module graph, so writing a changed one reloads the pages that import
|
|
7
|
+
// it. An edit to the kit configuration starts a new server from the new
|
|
8
|
+
// configuration, since it can change the port, the sources, the shell, the
|
|
9
|
+
// aliases and the data the pages are served.
|
|
10
|
+
|
|
11
|
+
import { realpathSync } from 'node:fs';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
import { dirname, resolve, sep } from 'node:path';
|
|
14
|
+
import type { ConfigurationLookupOptions } from 'scaffold-ui-kit';
|
|
15
|
+
import { createServer, type Plugin, type ViteDevServer } from 'vite';
|
|
16
|
+
import { loadPreviewContext, type PreviewContext } from './context.js';
|
|
17
|
+
import { generatePreview, type GeneratePreviewResult } from './generate.js';
|
|
18
|
+
import {
|
|
19
|
+
buildViteConfiguration,
|
|
20
|
+
type DevelopmentServerOverrides,
|
|
21
|
+
} from './vite-configuration.js';
|
|
22
|
+
import { isTemplateFile } from './lib/template-files.js';
|
|
23
|
+
|
|
24
|
+
/** How long a burst of saves settles before the preview regenerates. */
|
|
25
|
+
export const REGENERATION_DELAY_MILLISECONDS = 100;
|
|
26
|
+
|
|
27
|
+
/** The module cache the kit's modules load into (Node's, shared by every importer). */
|
|
28
|
+
const moduleCache = createRequire(import.meta.url).cache;
|
|
29
|
+
|
|
30
|
+
/** The kit root as written and as resolved, since a loaded module is cached
|
|
31
|
+
* under the path it resolved to. */
|
|
32
|
+
function kitRoots(context: PreviewContext): string[] {
|
|
33
|
+
const roots = new Set([context.kitRoot]);
|
|
34
|
+
try {
|
|
35
|
+
roots.add(realpathSync(context.kitRoot));
|
|
36
|
+
} catch {
|
|
37
|
+
// A kit root that does not resolve is only known as written.
|
|
38
|
+
}
|
|
39
|
+
return [...roots].map((root) => root + sep);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Whether `path` is one of the kit's own modules: under its root, and
|
|
43
|
+
* neither an installed package nor generated output. */
|
|
44
|
+
function isKitModule(
|
|
45
|
+
path: string,
|
|
46
|
+
context: PreviewContext,
|
|
47
|
+
roots: readonly string[]
|
|
48
|
+
): boolean {
|
|
49
|
+
return (
|
|
50
|
+
roots.some((root) => path.startsWith(root)) &&
|
|
51
|
+
!path.includes(`${sep}node_modules${sep}`) &&
|
|
52
|
+
!path.startsWith(context.generatedRoot + sep)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The kit's own modules generation has loaded: the templates and story files,
|
|
58
|
+
* and every module of the kit they import.
|
|
59
|
+
*
|
|
60
|
+
* @param context - The kit's preview context.
|
|
61
|
+
* @returns Their absolute paths.
|
|
62
|
+
*/
|
|
63
|
+
export function loadedKitModules(context: PreviewContext): string[] {
|
|
64
|
+
const roots = kitRoots(context);
|
|
65
|
+
return Object.keys(moduleCache).filter((path) =>
|
|
66
|
+
isKitModule(path, context, roots)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Forgets the kit's loaded modules, so the next generation imports each one
|
|
72
|
+
* afresh from disk. Installed packages stay loaded.
|
|
73
|
+
*
|
|
74
|
+
* @param context - The kit's preview context.
|
|
75
|
+
* @returns The absolute paths forgotten.
|
|
76
|
+
*/
|
|
77
|
+
export function forgetKitModules(context: PreviewContext): string[] {
|
|
78
|
+
const forgotten = loadedKitModules(context);
|
|
79
|
+
for (const path of forgotten) delete moduleCache[path];
|
|
80
|
+
return forgotten;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** What the last successful generation read from a kit, beyond the paths
|
|
84
|
+
* its preview context names. */
|
|
85
|
+
export interface GenerationInputs {
|
|
86
|
+
/** The kit modules it loaded (`loadedKitModules`). */
|
|
87
|
+
loadedModules?: readonly string[];
|
|
88
|
+
/** The theme stylesheets and assets it copied (`generatePreview`'s
|
|
89
|
+
* `themeSources`). */
|
|
90
|
+
themeSources?: readonly string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** What the development server watches in a kit. */
|
|
94
|
+
export interface KitWatchPaths {
|
|
95
|
+
/** The files and directories whose changes regenerate the preview. */
|
|
96
|
+
regeneration: string[];
|
|
97
|
+
/** The kit configuration file, whose changes restart the server. */
|
|
98
|
+
kitConfiguration: string;
|
|
99
|
+
/** The kit's modules a `.ts` configuration imports. A change to one
|
|
100
|
+
* restarts the server too. */
|
|
101
|
+
configurationModules: string[];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* The paths the development server watches in a kit: each source's templates
|
|
106
|
+
* and stories directories (so an added or deleted component is noticed),
|
|
107
|
+
* its manual components module, the shared
|
|
108
|
+
* modules templates import, every other kit module generation loaded, and
|
|
109
|
+
* the themes' stylesheets and assets. The custom views module, the shell and
|
|
110
|
+
* the setup modules are left to Vite, which serves them and reloads them
|
|
111
|
+
* itself; generation reads none of them.
|
|
112
|
+
*
|
|
113
|
+
* @param context - The kit's preview context.
|
|
114
|
+
* @param inputs - What the last successful generation read.
|
|
115
|
+
* @returns The paths, absolute.
|
|
116
|
+
*/
|
|
117
|
+
export function kitWatchPaths(
|
|
118
|
+
context: PreviewContext,
|
|
119
|
+
inputs: GenerationInputs = {}
|
|
120
|
+
): KitWatchPaths {
|
|
121
|
+
const regeneration = new Set<string>();
|
|
122
|
+
for (const source of context.sources) {
|
|
123
|
+
for (const path of [
|
|
124
|
+
source.templatesDirectoryAbsolute,
|
|
125
|
+
source.storiesDirectoryAbsolute,
|
|
126
|
+
source.manualModuleAbsolute,
|
|
127
|
+
]) {
|
|
128
|
+
if (path) regeneration.add(path);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
for (const path of context.sharedModulesAbsolute) regeneration.add(path);
|
|
132
|
+
for (const path of inputs.loadedModules ?? []) regeneration.add(path);
|
|
133
|
+
for (const path of inputs.themeSources ?? []) regeneration.add(path);
|
|
134
|
+
return {
|
|
135
|
+
regeneration: [...regeneration],
|
|
136
|
+
kitConfiguration: context.configurationPath,
|
|
137
|
+
configurationModules: context.configurationModules,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** What a change to a file means for the development server. */
|
|
142
|
+
export type KitChange = 'regenerate' | 'restart';
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* What a change to `path` asks of the development server: a restart for the
|
|
146
|
+
* kit configuration file and the kit modules it loads; a regeneration for a template (a TypeScript module
|
|
147
|
+
* directly in a templates directory), a story file, a theme's stylesheet or a
|
|
148
|
+
* file in its assets, or a module named in `kitWatchPaths`; nothing for any
|
|
149
|
+
* other file.
|
|
150
|
+
*
|
|
151
|
+
* @param path - The absolute path that changed, was added or was deleted.
|
|
152
|
+
* @param context - The kit's preview context.
|
|
153
|
+
* @param inputs - What the last successful generation read.
|
|
154
|
+
* @returns The change, or `undefined` when the file is none of the kit's inputs.
|
|
155
|
+
*/
|
|
156
|
+
export function classifyKitChange(
|
|
157
|
+
path: string,
|
|
158
|
+
context: PreviewContext,
|
|
159
|
+
inputs: GenerationInputs = {}
|
|
160
|
+
): KitChange | undefined {
|
|
161
|
+
const paths = kitWatchPaths(context, inputs);
|
|
162
|
+
if (
|
|
163
|
+
path === paths.kitConfiguration ||
|
|
164
|
+
paths.configurationModules.includes(path)
|
|
165
|
+
)
|
|
166
|
+
return 'restart';
|
|
167
|
+
const directory = dirname(path);
|
|
168
|
+
for (const source of context.sources) {
|
|
169
|
+
if (
|
|
170
|
+
directory === source.templatesDirectoryAbsolute &&
|
|
171
|
+
isTemplateFile(path)
|
|
172
|
+
) {
|
|
173
|
+
return 'regenerate';
|
|
174
|
+
}
|
|
175
|
+
if (
|
|
176
|
+
directory === source.storiesDirectoryAbsolute &&
|
|
177
|
+
path.endsWith('.story.ts')
|
|
178
|
+
) {
|
|
179
|
+
return 'regenerate';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (
|
|
183
|
+
(inputs.themeSources ?? []).some(
|
|
184
|
+
(source) => path === source || path.startsWith(source + sep)
|
|
185
|
+
)
|
|
186
|
+
) {
|
|
187
|
+
return 'regenerate';
|
|
188
|
+
}
|
|
189
|
+
const files = new Set(
|
|
190
|
+
paths.regeneration.filter(
|
|
191
|
+
(each) =>
|
|
192
|
+
!context.sources.some(
|
|
193
|
+
(source) =>
|
|
194
|
+
each === source.templatesDirectoryAbsolute ||
|
|
195
|
+
each === source.storiesDirectoryAbsolute
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
return files.has(path) ? 'regenerate' : undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/** What the kit-watching plugin is told to do beyond regenerating. */
|
|
203
|
+
export interface KitWatchOptions {
|
|
204
|
+
/** Called when the kit configuration file changes; settles once the
|
|
205
|
+
* server is replaced, or kept because the new configuration did not load. */
|
|
206
|
+
onKitConfigurationChange: () => Promise<void>;
|
|
207
|
+
/** What the generation before the server started read; later ones
|
|
208
|
+
* replace it. */
|
|
209
|
+
inputs?: GenerationInputs;
|
|
210
|
+
/** How long a burst of saves settles first. */
|
|
211
|
+
delayMilliseconds?: number;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* A Vite plugin that watches a kit's preview inputs while the development
|
|
216
|
+
* server runs and regenerates the preview when one changes. Saves arriving
|
|
217
|
+
* within the delay of each other regenerate once. A regeneration imports the
|
|
218
|
+
* kit's modules afresh, keeps Vite's cache and the scenario editor's story,
|
|
219
|
+
* and writes only the files whose content changed; Vite then reloads the
|
|
220
|
+
* pages importing them. A kit with an html pane also reloads its pages when
|
|
221
|
+
* a regeneration writes nothing, since the pane prints the templates' markup
|
|
222
|
+
* itself and a change to it writes no generated file. A kit module that fails to load is
|
|
223
|
+
* logged and the last good output stays until a later save loads.
|
|
224
|
+
*
|
|
225
|
+
* @param context - The kit's preview context.
|
|
226
|
+
* @param options - What a change to the kit configuration file does, and the delay.
|
|
227
|
+
* @returns The plugin.
|
|
228
|
+
*/
|
|
229
|
+
export function kitWatchPlugin(
|
|
230
|
+
context: PreviewContext,
|
|
231
|
+
options: KitWatchOptions
|
|
232
|
+
): Plugin {
|
|
233
|
+
const delay = options.delayMilliseconds ?? REGENERATION_DELAY_MILLISECONDS;
|
|
234
|
+
return {
|
|
235
|
+
name: 'preview-kit-watch',
|
|
236
|
+
configureServer(server) {
|
|
237
|
+
const logger = server.config.logger;
|
|
238
|
+
let inputs: GenerationInputs = {
|
|
239
|
+
loadedModules: loadedKitModules(context),
|
|
240
|
+
...options.inputs,
|
|
241
|
+
};
|
|
242
|
+
let watched = new Set<string>();
|
|
243
|
+
// Watch what the latest generation read, and stop watching what it no
|
|
244
|
+
// longer reads, unless Vite serves that file itself.
|
|
245
|
+
const watch = (): void => {
|
|
246
|
+
const paths = kitWatchPaths(context, inputs);
|
|
247
|
+
const next = new Set([
|
|
248
|
+
...paths.regeneration,
|
|
249
|
+
paths.kitConfiguration,
|
|
250
|
+
...paths.configurationModules,
|
|
251
|
+
]);
|
|
252
|
+
const dropped = [...watched].filter(
|
|
253
|
+
(path) =>
|
|
254
|
+
!next.has(path) && !server.moduleGraph?.getModulesByFile(path)?.size
|
|
255
|
+
);
|
|
256
|
+
if (dropped.length > 0) server.watcher.unwatch(dropped);
|
|
257
|
+
server.watcher.add([...next].filter((path) => !watched.has(path)));
|
|
258
|
+
watched = next;
|
|
259
|
+
};
|
|
260
|
+
watch();
|
|
261
|
+
|
|
262
|
+
let closed = false;
|
|
263
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
264
|
+
let running: Promise<void> | undefined;
|
|
265
|
+
let pendingRegeneration = false;
|
|
266
|
+
let pendingRestart = false;
|
|
267
|
+
|
|
268
|
+
const regenerate = async (): Promise<void> => {
|
|
269
|
+
forgetKitModules(context);
|
|
270
|
+
try {
|
|
271
|
+
const { written, removed, themeSources } = await generatePreview(
|
|
272
|
+
context,
|
|
273
|
+
{ clean: false }
|
|
274
|
+
);
|
|
275
|
+
inputs = { loadedModules: loadedKitModules(context), themeSources };
|
|
276
|
+
watch();
|
|
277
|
+
const changed = written.length + removed.length;
|
|
278
|
+
logger.info(
|
|
279
|
+
changed === 0
|
|
280
|
+
? '[preview] regenerated: no generated files changed'
|
|
281
|
+
: `[preview] regenerated: ${changed} ${changed === 1 ? 'file' : 'files'} changed`,
|
|
282
|
+
{ timestamp: true }
|
|
283
|
+
);
|
|
284
|
+
// The theme stylesheets are served as files, outside the module
|
|
285
|
+
// graph: a changed one reloads the pages here. The html pane's
|
|
286
|
+
// markup is printed on request from the templates just loaded, and
|
|
287
|
+
// a change to a template's markup writes no generated file, so
|
|
288
|
+
// that kit reloads here when nothing was written for Vite to act on.
|
|
289
|
+
const publicRoot = resolve(context.generatedRoot, 'public') + sep;
|
|
290
|
+
if (
|
|
291
|
+
(context.buildsHtml && changed === 0) ||
|
|
292
|
+
written.some((path) => path.startsWith(publicRoot))
|
|
293
|
+
) {
|
|
294
|
+
server.ws.send({ type: 'full-reload', path: '*' });
|
|
295
|
+
}
|
|
296
|
+
} catch (error) {
|
|
297
|
+
const detail =
|
|
298
|
+
error instanceof Error ? (error.stack ?? error.message) : error;
|
|
299
|
+
logger.error(
|
|
300
|
+
`[preview] regeneration failed; the preview keeps its last output until a save loads:\n${String(detail)}`,
|
|
301
|
+
{ timestamp: true }
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
const work = async (): Promise<void> => {
|
|
307
|
+
if (pendingRestart) {
|
|
308
|
+
pendingRestart = false;
|
|
309
|
+
await options.onKitConfigurationChange();
|
|
310
|
+
}
|
|
311
|
+
if (pendingRegeneration && !closed) {
|
|
312
|
+
pendingRegeneration = false;
|
|
313
|
+
await regenerate();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const settle = (): void => {
|
|
318
|
+
timer = undefined;
|
|
319
|
+
if (closed) return;
|
|
320
|
+
if (running) return;
|
|
321
|
+
running = work().finally(() => {
|
|
322
|
+
running = undefined;
|
|
323
|
+
if ((pendingRestart || pendingRegeneration) && !closed && !timer) {
|
|
324
|
+
settle();
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
server.watcher.on('all', (event, path) => {
|
|
330
|
+
if (closed) return;
|
|
331
|
+
if (event !== 'add' && event !== 'change' && event !== 'unlink') return;
|
|
332
|
+
const change = classifyKitChange(path, context, inputs);
|
|
333
|
+
if (!change) return;
|
|
334
|
+
if (change === 'restart') pendingRestart = true;
|
|
335
|
+
else pendingRegeneration = true;
|
|
336
|
+
if (timer) clearTimeout(timer);
|
|
337
|
+
timer = setTimeout(settle, delay);
|
|
338
|
+
});
|
|
339
|
+
server.httpServer?.once('close', () => {
|
|
340
|
+
closed = true;
|
|
341
|
+
if (timer) clearTimeout(timer);
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** A running development server. */
|
|
348
|
+
export interface DevelopmentServer {
|
|
349
|
+
/** The Vite server currently serving; replaced when the kit configuration changes. */
|
|
350
|
+
readonly server: ViteDevServer;
|
|
351
|
+
/** Stops serving. */
|
|
352
|
+
close(): Promise<void>;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Generates a kit's preview and serves it, regenerating as the kit's
|
|
357
|
+
* preview inputs change and starting a new server, from the new
|
|
358
|
+
* configuration, when the kit configuration changes. A configuration that
|
|
359
|
+
* fails to load is logged and the running server kept.
|
|
360
|
+
*
|
|
361
|
+
* @param kitRoot - The kit root (where its package.json lives).
|
|
362
|
+
* @param overrides - The port and host, over the kit's configured ones.
|
|
363
|
+
* @param options - The kit configuration file, when it is not the one the
|
|
364
|
+
* kit root gives.
|
|
365
|
+
* @returns The running server.
|
|
366
|
+
* @example
|
|
367
|
+
* ```ts
|
|
368
|
+
* const development = await startDevelopmentServer(kitRoot, { port: 6100 });
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
export async function startDevelopmentServer(
|
|
372
|
+
kitRoot: string,
|
|
373
|
+
overrides: DevelopmentServerOverrides = {},
|
|
374
|
+
options: ConfigurationLookupOptions = {}
|
|
375
|
+
): Promise<DevelopmentServer> {
|
|
376
|
+
let server: ViteDevServer;
|
|
377
|
+
let restarting: Promise<void> | undefined;
|
|
378
|
+
|
|
379
|
+
const listen = async (
|
|
380
|
+
context: PreviewContext,
|
|
381
|
+
generated: GeneratePreviewResult
|
|
382
|
+
): Promise<ViteDevServer> => {
|
|
383
|
+
const configuration = buildViteConfiguration(context, overrides);
|
|
384
|
+
configuration.plugins = [
|
|
385
|
+
...(configuration.plugins ?? []),
|
|
386
|
+
kitWatchPlugin(context, {
|
|
387
|
+
onKitConfigurationChange: () => (restarting = restart()),
|
|
388
|
+
inputs: {
|
|
389
|
+
loadedModules: loadedKitModules(context),
|
|
390
|
+
themeSources: generated.themeSources,
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
];
|
|
394
|
+
const started = await createServer(configuration);
|
|
395
|
+
await started.listen();
|
|
396
|
+
started.printUrls();
|
|
397
|
+
return started;
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const restart = async (): Promise<void> => {
|
|
401
|
+
const logger = server.config.logger;
|
|
402
|
+
let context: PreviewContext;
|
|
403
|
+
let generated: GeneratePreviewResult;
|
|
404
|
+
try {
|
|
405
|
+
context = loadPreviewContext(kitRoot, options);
|
|
406
|
+
forgetKitModules(context);
|
|
407
|
+
generated = await generatePreview(context, { clean: false });
|
|
408
|
+
} catch (error) {
|
|
409
|
+
logger.error(
|
|
410
|
+
`[preview] the kit configuration changed but did not load; the server keeps its previous configuration until a save loads:\n${error instanceof Error ? error.message : String(error)}`,
|
|
411
|
+
{ timestamp: true }
|
|
412
|
+
);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
logger.info('[preview] the kit configuration changed: restarting', {
|
|
416
|
+
timestamp: true,
|
|
417
|
+
});
|
|
418
|
+
await server.close();
|
|
419
|
+
try {
|
|
420
|
+
server = await listen(context, generated);
|
|
421
|
+
} catch (error) {
|
|
422
|
+
logger.error(
|
|
423
|
+
`[preview] the server did not restart:\n${error instanceof Error ? error.message : String(error)}`,
|
|
424
|
+
{ timestamp: true }
|
|
425
|
+
);
|
|
426
|
+
process.exitCode = 1;
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const context = loadPreviewContext(kitRoot, options);
|
|
431
|
+
for (const warning of context.warnings) console.warn(`[preview] ${warning}`);
|
|
432
|
+
server = await listen(context, await generatePreview(context));
|
|
433
|
+
return {
|
|
434
|
+
get server() {
|
|
435
|
+
return server;
|
|
436
|
+
},
|
|
437
|
+
async close() {
|
|
438
|
+
await restarting;
|
|
439
|
+
await server.close();
|
|
440
|
+
},
|
|
441
|
+
};
|
|
442
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
// Shared helpers used by every per-target emitter. The propTypes/props are
|
|
2
|
+
// identical across targets (same model); only imports, render syntax, and
|
|
3
|
+
// slot-fill translation differ - those live in the per-target emitters.
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
PropTypeSpecification,
|
|
7
|
+
JsonValue,
|
|
8
|
+
SlotFill,
|
|
9
|
+
ResolvedStory,
|
|
10
|
+
} from '../lib/types.js';
|
|
11
|
+
|
|
12
|
+
export function serialize(value: JsonValue): string {
|
|
13
|
+
if (value === undefined) return 'undefined';
|
|
14
|
+
if (value === null) return 'null';
|
|
15
|
+
if (Array.isArray(value)) return `[${value.map(serialize).join(', ')}]`;
|
|
16
|
+
if (typeof value === 'string') return JSON.stringify(value);
|
|
17
|
+
if (typeof value === 'object') {
|
|
18
|
+
const body = Object.entries(value)
|
|
19
|
+
.map(
|
|
20
|
+
([key, entryValue]) =>
|
|
21
|
+
`${JSON.stringify(key)}: ${serialize(entryValue as JsonValue)}`
|
|
22
|
+
)
|
|
23
|
+
.join(', ');
|
|
24
|
+
return `{ ${body} }`;
|
|
25
|
+
}
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Text as a Vue or Svelte template reads it back: `&` and `<` escaped, as
|
|
31
|
+
* either would start a character reference or open a tag.
|
|
32
|
+
*
|
|
33
|
+
* @param value - The text as the story gives it.
|
|
34
|
+
* @returns The text as markup.
|
|
35
|
+
*/
|
|
36
|
+
export function markupText(value: string): string {
|
|
37
|
+
return value.replace(/&/g, '&').replace(/</g, '<');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether a text has whitespace a template does not keep as written:
|
|
42
|
+
* whitespace at an edge, a run of it or a line break, which a template
|
|
43
|
+
* trims, collapses, or loses where the printed element breaks over lines.
|
|
44
|
+
*
|
|
45
|
+
* @param value - The text as the story gives it.
|
|
46
|
+
* @returns Whether the text has to be printed as a string expression.
|
|
47
|
+
*/
|
|
48
|
+
export function hasFragileWhitespace(value: string): boolean {
|
|
49
|
+
return /^\s|\s$|\s\s|[\t\n\r\f]/.test(value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Text as a Vue template reads it back: markup text with each `{{` held in
|
|
54
|
+
* an interpolation of its own, so it does not open one.
|
|
55
|
+
*
|
|
56
|
+
* @param value - The text as the story gives it.
|
|
57
|
+
* @returns The text as Vue template content.
|
|
58
|
+
* @example
|
|
59
|
+
* vueText('Type {{ name }} here');
|
|
60
|
+
* // 'Type {{ "{{" }} name }} here'
|
|
61
|
+
*/
|
|
62
|
+
export function vueText(value: string): string {
|
|
63
|
+
return markupText(value).replace(/\{\{/g, '{{ "{{" }}');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Text as a printed Vue template keeps it: {@link vueText}, or one string
|
|
68
|
+
* interpolation for a text with whitespace at an edge, a run of it or a
|
|
69
|
+
* line break, which Vue condenses and a printed element's line breaks
|
|
70
|
+
* replace.
|
|
71
|
+
*
|
|
72
|
+
* @param value - The text as the story gives it.
|
|
73
|
+
* @returns The text as Vue template content.
|
|
74
|
+
* @example
|
|
75
|
+
* vueSourceText(' 1 ripburger');
|
|
76
|
+
* // '{{ " 1 ripburger" }}'
|
|
77
|
+
*/
|
|
78
|
+
export function vueSourceText(value: string): string {
|
|
79
|
+
if (hasFragileWhitespace(value)) {
|
|
80
|
+
// A `}}` inside the string would close the interpolation.
|
|
81
|
+
return `{{ ${JSON.stringify(value).replace(/\}\}/g, '}\\u007d')} }}`;
|
|
82
|
+
}
|
|
83
|
+
return vueText(value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The Vue template content before a junction, given the content joined onto
|
|
88
|
+
* it: a trailing `{` before content starting with one is held in an
|
|
89
|
+
* interpolation of its own, as the two would otherwise open one.
|
|
90
|
+
*
|
|
91
|
+
* @param before - The content before the junction.
|
|
92
|
+
* @param next - The content joined onto it.
|
|
93
|
+
* @returns The content before the junction, safe to join.
|
|
94
|
+
* @example
|
|
95
|
+
* vueJunction('Price: {', '{ amount }}');
|
|
96
|
+
* // "Price: {{ '{' }}"
|
|
97
|
+
*/
|
|
98
|
+
export function vueJunction(before: string, next: string): string {
|
|
99
|
+
return before.endsWith('{') && next.startsWith('{')
|
|
100
|
+
? `${before.slice(0, -1)}{{ '{' }}`
|
|
101
|
+
: before;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Vue template pieces joined into one, each junction separated by
|
|
106
|
+
* {@link vueJunction}.
|
|
107
|
+
*
|
|
108
|
+
* @param pieces - The template content, in order.
|
|
109
|
+
* @returns The joined content.
|
|
110
|
+
*/
|
|
111
|
+
export function joinVue(pieces: string[]): string {
|
|
112
|
+
return pieces.reduce(
|
|
113
|
+
(joined, piece) => (joined ? vueJunction(joined, piece) : joined) + piece,
|
|
114
|
+
''
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Text as a Svelte template reads it back: markup text with each brace held
|
|
120
|
+
* in an expression of its own, so none opens one. Svelte trims the whitespace
|
|
121
|
+
* at the edges of an element's or a component's content and collapses a run
|
|
122
|
+
* of it, so a text with whitespace at an edge, a run of it or a line break
|
|
123
|
+
* is one string expression instead, which Svelte renders as it is.
|
|
124
|
+
*
|
|
125
|
+
* @param value - The text as the story gives it.
|
|
126
|
+
* @returns The text as Svelte markup.
|
|
127
|
+
* @example
|
|
128
|
+
* svelteText('Price: {amount}');
|
|
129
|
+
* // "Price: {'{'}amount{'}'}"
|
|
130
|
+
* svelteText(' 1 ripburger');
|
|
131
|
+
* // '{" 1 ripburger"}'
|
|
132
|
+
*/
|
|
133
|
+
export function svelteText(value: string): string {
|
|
134
|
+
if (hasFragileWhitespace(value)) {
|
|
135
|
+
return `{${JSON.stringify(value)}}`;
|
|
136
|
+
}
|
|
137
|
+
return markupText(value).replace(/[{}]/g, (brace) => `{'${brace}'}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A value as a Vue template's bound attribute holds it, single-quoted:
|
|
142
|
+
* the value's expression with `&` and `'` as character references, which
|
|
143
|
+
* the template reads back as the characters, so neither ends the attribute
|
|
144
|
+
* nor starts a reference of its own.
|
|
145
|
+
*
|
|
146
|
+
* @param value - The prop's value.
|
|
147
|
+
* @returns The attribute's text, quotes included.
|
|
148
|
+
* @example
|
|
149
|
+
* vueBoundAttribute("Don't");
|
|
150
|
+
* // `'"Don't"'`
|
|
151
|
+
*/
|
|
152
|
+
export function vueBoundAttribute(value: JsonValue): string {
|
|
153
|
+
return `'${serialize(value).replace(/&/g, '&').replace(/'/g, ''')}'`;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function needsColor(model: ResolvedStory): boolean {
|
|
157
|
+
return model.propTypes.some(
|
|
158
|
+
(propType) => propType.include && propType.control === 'color-select'
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function propTypeLine(specification: PropTypeSpecification): string {
|
|
163
|
+
if (!specification.include)
|
|
164
|
+
return ` ${specification.name}: { table: { disable: true } },`;
|
|
165
|
+
switch (specification.control) {
|
|
166
|
+
case 'color-select':
|
|
167
|
+
return ` ${specification.name}: { control: 'select', options: colorOptions },`;
|
|
168
|
+
case 'select':
|
|
169
|
+
return ` ${specification.name}: { control: 'select', options: ${serialize(specification.options ?? [])} },`;
|
|
170
|
+
case 'number':
|
|
171
|
+
return specification.number
|
|
172
|
+
? ` ${specification.name}: { control: { type: 'number', min: ${specification.number.min}, max: ${specification.number.max}, step: ${specification.number.step} } },`
|
|
173
|
+
: ` ${specification.name}: { control: 'number' },`;
|
|
174
|
+
case 'boolean':
|
|
175
|
+
return ` ${specification.name}: { control: 'boolean' },`;
|
|
176
|
+
case 'text':
|
|
177
|
+
return ` ${specification.name}: { control: 'text' },`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function propTypesBody(model: ResolvedStory): string {
|
|
182
|
+
return model.propTypes.map(propTypeLine).join('\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** The effective default-slot fill: an explicit story file fill, or the `children`
|
|
186
|
+
* text prop for components that have a default slot and no story file fill. */
|
|
187
|
+
export type DefaultSlot =
|
|
188
|
+
| SlotFill
|
|
189
|
+
| { kind: 'propText'; prop: string }
|
|
190
|
+
| undefined;
|
|
191
|
+
|
|
192
|
+
export function effectiveDefaultSlot(model: ResolvedStory): DefaultSlot {
|
|
193
|
+
if (model.slots?.default) return model.slots.default;
|
|
194
|
+
if (model.defaultProps.children !== undefined)
|
|
195
|
+
return { kind: 'propText', prop: 'children' };
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function namedSlots(model: ResolvedStory): Record<string, SlotFill> {
|
|
200
|
+
return model.slots?.named ?? {};
|
|
201
|
+
}
|