@jfrader/storybook-docs 0.4.3 → 0.5.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/dist/build/story-sources.d.ts +15 -0
- package/dist/controls.d.ts +2 -1
- package/dist/css.d.ts +1 -0
- package/dist/index.css +3 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +91 -34
- package/dist/sections.d.ts +2 -1
- package/dist/styles.css +3 -6
- package/dist/toc.d.ts +9 -0
- package/dist/virtual.d.ts +4 -0
- package/dist/vite-plugin.js +234 -0
- package/docs/RELEASING.md +26 -0
- package/package.json +11 -18
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface HelperExpansion {
|
|
2
|
+
inlined: string[];
|
|
3
|
+
remaining: {
|
|
4
|
+
name: string;
|
|
5
|
+
reason: string;
|
|
6
|
+
}[];
|
|
7
|
+
}
|
|
8
|
+
export declare function extractStorySourceDetails(source: string): {
|
|
9
|
+
sources: Record<string, string>;
|
|
10
|
+
helpers: Record<string, HelperExpansion>;
|
|
11
|
+
};
|
|
12
|
+
export declare function extractStorySources(source: string): Record<string, string>;
|
|
13
|
+
export declare function storySourcesIndexCode(slugs: Iterable<string>): string;
|
|
14
|
+
export declare function storySourcesCode(sources: Record<string, string>): string;
|
|
15
|
+
export {};
|
package/dist/controls.d.ts
CHANGED
|
@@ -40,10 +40,11 @@ export declare function getStoryControls(meta: StoryShape, story: StoryShape, co
|
|
|
40
40
|
controls: StoryControl[];
|
|
41
41
|
unavailable: string | null;
|
|
42
42
|
};
|
|
43
|
-
export declare function ControlledStoryFrame({ module, story, componentName, componentDefaults }: {
|
|
43
|
+
export declare function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }: {
|
|
44
44
|
module: Record<string, unknown>;
|
|
45
45
|
story: string;
|
|
46
46
|
componentName: string;
|
|
47
47
|
/** Defaults read from the component source, shown until the reader changes them. */
|
|
48
48
|
componentDefaults?: Record<string, unknown>;
|
|
49
|
+
renderSource?: string;
|
|
49
50
|
}): import("react").JSX.Element;
|
package/dist/css.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module '*.css';
|
package/dist/index.css
CHANGED
|
@@ -114,11 +114,8 @@
|
|
|
114
114
|
display: block;
|
|
115
115
|
grid-column: 3;
|
|
116
116
|
grid-row: 2;
|
|
117
|
-
position: sticky;
|
|
118
|
-
top: var(--sd-header-height, 56px);
|
|
119
117
|
align-self: start;
|
|
120
|
-
|
|
121
|
-
overflow-y: auto;
|
|
118
|
+
transform: translateY(var(--sd-rail-follow, 0px));
|
|
122
119
|
padding: 34px 18px 40px 6px;
|
|
123
120
|
font-size: 12.5px;
|
|
124
121
|
}
|
|
@@ -173,13 +170,12 @@
|
|
|
173
170
|
margin: 0;
|
|
174
171
|
padding: 0 20px 18px;
|
|
175
172
|
min-width: 0;
|
|
176
|
-
border: 0;
|
|
177
173
|
background: none;
|
|
178
174
|
color: var(--sd-text-muted);
|
|
179
175
|
font-size: 13px;
|
|
180
176
|
}
|
|
181
|
-
.sd-controls
|
|
182
|
-
|
|
177
|
+
.sd-controls-label {
|
|
178
|
+
margin: 16px 0 10px;
|
|
183
179
|
font-size: 11px;
|
|
184
180
|
font-weight: 600;
|
|
185
181
|
letter-spacing: 0.06em;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference path="./virtual.d.ts" />
|
|
2
|
+
/// <reference path="./css.d.ts" />
|
|
2
3
|
export { start } from './start.js';
|
|
3
4
|
export { ComponentDemo, type ComponentDemoProps } from './component-demo.js';
|
|
4
5
|
export type { StorybookDocsConfig, CatalogEntry, ThemeRole } from './config.js';
|
package/dist/index.js
CHANGED
|
@@ -5,12 +5,12 @@ import { createRoot } from "react-dom/client";
|
|
|
5
5
|
import { site as site3, themeCss, theme as theme2 } from "virtual:storybook-docs/site";
|
|
6
6
|
|
|
7
7
|
// src/app.tsx
|
|
8
|
-
import { useEffect as
|
|
9
|
-
import { catalog as catalog3, groups as groups2
|
|
8
|
+
import { useEffect as useEffect8, useRef as useRef5, useState as useState9 } from "react";
|
|
9
|
+
import { catalog as catalog3, groups as groups2 } from "virtual:storybook-docs/catalog";
|
|
10
10
|
import { pages, site as site2, theme } from "virtual:storybook-docs/site";
|
|
11
11
|
|
|
12
12
|
// src/pages/component-page.tsx
|
|
13
|
-
import { useMemo as useMemo3 } from "react";
|
|
13
|
+
import { useEffect as useEffect6, useMemo as useMemo3, useState as useState7 } from "react";
|
|
14
14
|
import { storySource as storySource2 } from "virtual:storybook-docs/catalog";
|
|
15
15
|
|
|
16
16
|
// src/guidance.tsx
|
|
@@ -374,7 +374,8 @@ function usageSnippet(componentName, args) {
|
|
|
374
374
|
const children = typeof args.children === "string" && !/[&<>{}]/.test(args.children) ? args.children : `{${JSON.stringify(args.children)}}`;
|
|
375
375
|
return `${opening}>${children}</${componentName}>`;
|
|
376
376
|
}
|
|
377
|
-
function buildUsageSnippet({ componentName, from, args, source, jsxName, component }) {
|
|
377
|
+
function buildUsageSnippet({ componentName, from, args, source, renderSource, jsxName, component }) {
|
|
378
|
+
if (!source?.trim() && renderSource?.trim()) return renderSource;
|
|
378
379
|
const rootName = componentName.split(".")[0];
|
|
379
380
|
const identity = component;
|
|
380
381
|
const tag = jsxName ?? (typeof identity?.displayName === "string" && identity.displayName.startsWith(`${rootName}.`) ? identity.displayName : componentName);
|
|
@@ -531,6 +532,39 @@ function storyLink(location, story) {
|
|
|
531
532
|
return `${base}${hashPath(location.hash)}?${new URLSearchParams({ [STORY_PARAM]: story })}`;
|
|
532
533
|
}
|
|
533
534
|
var slugify = (text) => text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
535
|
+
function useRailFollow(ref, ready = true) {
|
|
536
|
+
useEffect3(() => {
|
|
537
|
+
const element = ref.current;
|
|
538
|
+
if (!element) return;
|
|
539
|
+
let frame = 0;
|
|
540
|
+
let applied = 0;
|
|
541
|
+
const limit = () => window.innerHeight - (Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--sd-header-height")) || 56) - 20;
|
|
542
|
+
const apply = () => {
|
|
543
|
+
frame = 0;
|
|
544
|
+
const bottomInDocument = element.getBoundingClientRect().bottom + window.scrollY - applied;
|
|
545
|
+
const crossing = bottomInDocument - limit();
|
|
546
|
+
const next = Math.max(0, window.scrollY - crossing);
|
|
547
|
+
if (next !== applied) {
|
|
548
|
+
applied = next;
|
|
549
|
+
element.style.setProperty("--sd-rail-follow", `${next}px`);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
552
|
+
const schedule = () => {
|
|
553
|
+
if (!frame) frame = requestAnimationFrame(apply);
|
|
554
|
+
};
|
|
555
|
+
apply();
|
|
556
|
+
window.addEventListener("scroll", schedule, { passive: true });
|
|
557
|
+
window.addEventListener("resize", schedule);
|
|
558
|
+
const observer = new ResizeObserver(schedule);
|
|
559
|
+
observer.observe(element);
|
|
560
|
+
return () => {
|
|
561
|
+
window.removeEventListener("scroll", schedule);
|
|
562
|
+
window.removeEventListener("resize", schedule);
|
|
563
|
+
observer.disconnect();
|
|
564
|
+
if (frame) cancelAnimationFrame(frame);
|
|
565
|
+
};
|
|
566
|
+
}, [ready, ref]);
|
|
567
|
+
}
|
|
534
568
|
function scanPage(root, pageKey) {
|
|
535
569
|
const used = /* @__PURE__ */ new Set();
|
|
536
570
|
const unique = (prefix, text) => {
|
|
@@ -542,9 +576,12 @@ function scanPage(root, pageKey) {
|
|
|
542
576
|
};
|
|
543
577
|
const sections = [];
|
|
544
578
|
const frames = [];
|
|
545
|
-
const subHeadings = [...root.querySelectorAll("h3")].filter(
|
|
579
|
+
const subHeadings = [...root.querySelectorAll("h3")].filter(
|
|
580
|
+
(heading) => heading.textContent && !heading.closest(".story-lane") && !heading.closest(".frame")
|
|
581
|
+
);
|
|
546
582
|
root.querySelectorAll("h2").forEach((heading) => {
|
|
547
583
|
if (!heading.textContent) return;
|
|
584
|
+
if (heading.closest(".story-lane") || heading.closest(".frame")) return;
|
|
548
585
|
heading.id ||= unique("section", heading.textContent);
|
|
549
586
|
heading.dataset.toc = "";
|
|
550
587
|
const section = heading.parentElement?.tagName === "SECTION" ? heading.parentElement : null;
|
|
@@ -570,6 +607,13 @@ function scanPage(root, pageKey) {
|
|
|
570
607
|
});
|
|
571
608
|
sections.push({ id: heading.id, title: heading.textContent, element: section ?? heading, heading, stories, subs });
|
|
572
609
|
});
|
|
610
|
+
const counts = /* @__PURE__ */ new Map();
|
|
611
|
+
for (const item of frames) counts.set(item.name, (counts.get(item.name) ?? 0) + 1);
|
|
612
|
+
for (const item of frames) {
|
|
613
|
+
if ((counts.get(item.name) ?? 0) > 1 && item.story && item.story !== item.name) {
|
|
614
|
+
item.name = `${item.name} \xB7 ${item.story}`;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
573
617
|
return { pageKey, sections, frames };
|
|
574
618
|
}
|
|
575
619
|
var signature = (scan) => `${scan.pageKey}|${scan.sections.map((s) => `${s.id}(${s.subs.map((sub) => sub.id).join("+")}):${s.stories.map((st) => `${st.id}:${st.name}`).join(",")}`).join(";")}`;
|
|
@@ -657,10 +701,12 @@ function PageRail({ pageKey }) {
|
|
|
657
701
|
window.setTimeout(() => match.element.classList.remove("toc-flash"), FLASH_MS);
|
|
658
702
|
setTarget(null);
|
|
659
703
|
}, [target, scan, pageKey]);
|
|
704
|
+
const railRef = useRef2(null);
|
|
705
|
+
useRailFollow(railRef, scan !== null);
|
|
660
706
|
const active = useActiveIds(scan);
|
|
661
707
|
const go = useCallback((element) => scrollToElement(element), []);
|
|
662
708
|
if (!scan) return null;
|
|
663
|
-
return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", children: [
|
|
709
|
+
return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
|
|
664
710
|
/* @__PURE__ */ jsx5("div", { className: "toc-title", children: "On this page" }),
|
|
665
711
|
/* @__PURE__ */ jsx5("ul", { children: scan.sections.map((section) => /* @__PURE__ */ jsxs3("li", { children: [
|
|
666
712
|
/* @__PURE__ */ jsx5(
|
|
@@ -799,6 +845,7 @@ function ComponentDemo({
|
|
|
799
845
|
children
|
|
800
846
|
}) {
|
|
801
847
|
const [codeOpen, setCodeOpen] = useState4(false);
|
|
848
|
+
const controlsLabelId = useId();
|
|
802
849
|
const canvasRef = useRef3(null);
|
|
803
850
|
const [scrollable, setScrollable] = useState4(false);
|
|
804
851
|
useEffect4(() => {
|
|
@@ -844,8 +891,8 @@ function ComponentDemo({
|
|
|
844
891
|
skipped.join(", "),
|
|
845
892
|
". See it in Storybook."
|
|
846
893
|
] }) : null,
|
|
847
|
-
unavailable ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: unavailable }) : /* @__PURE__ */ jsxs4("
|
|
848
|
-
/* @__PURE__ */ jsxs4("
|
|
894
|
+
unavailable ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: unavailable }) : /* @__PURE__ */ jsxs4("div", { className: "sd-controls", role: "group", "aria-labelledby": controlsLabelId, children: [
|
|
895
|
+
/* @__PURE__ */ jsxs4("p", { className: "sd-controls-label", id: controlsLabelId, children: [
|
|
849
896
|
"Controls for ",
|
|
850
897
|
storyName
|
|
851
898
|
] }),
|
|
@@ -934,7 +981,7 @@ function getStoryControls(meta, story, componentDefaults = {}) {
|
|
|
934
981
|
unavailable: controls.length === 0 ? "No controls." : null
|
|
935
982
|
};
|
|
936
983
|
}
|
|
937
|
-
function ControlledStoryFrame({ module, story, componentName, componentDefaults }) {
|
|
984
|
+
function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }) {
|
|
938
985
|
const meta = module.default ?? {};
|
|
939
986
|
const definition = module[story] ?? {};
|
|
940
987
|
const { args: defaults, controls, unavailable } = getStoryControls(meta, definition, componentDefaults);
|
|
@@ -950,12 +997,13 @@ function ControlledStoryFrame({ module, story, componentName, componentDefaults
|
|
|
950
997
|
const storyName = definition.name ?? story;
|
|
951
998
|
const skipped = ["loaders", "beforeEach"].filter((key) => meta[key] || definition[key]);
|
|
952
999
|
if (definition.parameters?.viewport ?? meta.parameters?.viewport) skipped.push("viewport");
|
|
953
|
-
const codeLanguage = docsOverride?.source?.language ?? docsMeta?.source?.language ?? "tsx";
|
|
1000
|
+
const codeLanguage = authoredCode?.trim() ? docsOverride?.source?.language ?? docsMeta?.source?.language ?? "tsx" : "tsx";
|
|
954
1001
|
const snippet = buildUsageSnippet({
|
|
955
1002
|
componentName,
|
|
956
1003
|
from: storySource,
|
|
957
1004
|
args: { ...meta.args, ...definition.args, ...edits },
|
|
958
1005
|
source: authoredCode,
|
|
1006
|
+
renderSource,
|
|
959
1007
|
// A story name is not a component name: only a part-qualified name (the rare `Dialog.Content`
|
|
960
1008
|
// story) is worth using as the JSX tag, otherwise the component's own name is the honest one.
|
|
961
1009
|
jsxName: storyName.includes(".") ? storyName : void 0,
|
|
@@ -1003,6 +1051,7 @@ function useProps(slug) {
|
|
|
1003
1051
|
}
|
|
1004
1052
|
|
|
1005
1053
|
// src/pages/component-page.tsx
|
|
1054
|
+
import { loadStorySources } from "virtual:storybook-docs/story-sources";
|
|
1006
1055
|
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1007
1056
|
function formatValue(value) {
|
|
1008
1057
|
if (value === void 0) return "\u2014";
|
|
@@ -1129,6 +1178,19 @@ function PropsTable({
|
|
|
1129
1178
|
}
|
|
1130
1179
|
function ComponentPage({ entry }) {
|
|
1131
1180
|
const { module, error } = useStoryModule(entry);
|
|
1181
|
+
const [storySources, setStorySources] = useState7(null);
|
|
1182
|
+
useEffect6(() => {
|
|
1183
|
+
let active = true;
|
|
1184
|
+
loadStorySources(entry.slug).then(({ sources: sources2 }) => {
|
|
1185
|
+
if (active) setStorySources({ slug: entry.slug, sources: sources2 });
|
|
1186
|
+
}).catch(() => {
|
|
1187
|
+
if (active) setStorySources(null);
|
|
1188
|
+
});
|
|
1189
|
+
return () => {
|
|
1190
|
+
active = false;
|
|
1191
|
+
};
|
|
1192
|
+
}, [entry.slug]);
|
|
1193
|
+
const sources = storySources?.slug === entry.slug ? storySources.sources : {};
|
|
1132
1194
|
const props = useProps(entry.slug);
|
|
1133
1195
|
const meta = module?.default ?? {};
|
|
1134
1196
|
const argTypes = meta.argTypes ?? {};
|
|
@@ -1200,7 +1262,7 @@ function ComponentPage({ entry }) {
|
|
|
1200
1262
|
/* @__PURE__ */ jsx8(Skeleton, { height: 150 })
|
|
1201
1263
|
] }) : null,
|
|
1202
1264
|
module ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
1203
|
-
example ? /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story: example, componentName: entry.name, componentDefaults }, `${entry.slug}:${example}`) : null,
|
|
1265
|
+
example ? /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story: example, componentName: entry.name, componentDefaults, renderSource: sources[example] }, `${entry.slug}:${example}`) : null,
|
|
1204
1266
|
entry.hasComponent ? /* @__PURE__ */ jsx8("h2", { children: "Usage" }) : null,
|
|
1205
1267
|
snippet ? /* @__PURE__ */ jsx8(CodeBlock, { code: snippet, language: codeLanguage }) : entry.hasComponent ? /* @__PURE__ */ jsx8("p", { children: "No usage code is authored for this component." }) : null,
|
|
1206
1268
|
usage ? /* @__PURE__ */ jsxs5(Fragment3, { children: [
|
|
@@ -1210,7 +1272,7 @@ function ComponentPage({ entry }) {
|
|
|
1210
1272
|
/* @__PURE__ */ jsx8(Guidance, { slug: entry.slug }),
|
|
1211
1273
|
sections.map((section) => /* @__PURE__ */ jsxs5("section", { children: [
|
|
1212
1274
|
/* @__PURE__ */ jsx8("h2", { children: section.title }),
|
|
1213
|
-
section.stories.map((story) => /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story, componentName: entry.name, componentDefaults }, `${entry.slug}:${story}`))
|
|
1275
|
+
section.stories.map((story) => /* @__PURE__ */ jsx8(ControlledStoryFrame, { module, story, componentName: entry.name, componentDefaults, renderSource: sources[story] }, `${entry.slug}:${story}`))
|
|
1214
1276
|
] }, section.title))
|
|
1215
1277
|
] }) : null,
|
|
1216
1278
|
/* @__PURE__ */ jsx8("h2", { children: "Props" }),
|
|
@@ -1531,7 +1593,7 @@ function ReleasesPage() {
|
|
|
1531
1593
|
}
|
|
1532
1594
|
|
|
1533
1595
|
// src/search.tsx
|
|
1534
|
-
import { useEffect as
|
|
1596
|
+
import { useEffect as useEffect7, useMemo as useMemo4, useRef as useRef4, useState as useState8 } from "react";
|
|
1535
1597
|
import { catalog as catalog2 } from "virtual:storybook-docs/catalog";
|
|
1536
1598
|
|
|
1537
1599
|
// src/search-match.ts
|
|
@@ -1582,19 +1644,19 @@ function renderHighlighted(text, query) {
|
|
|
1582
1644
|
);
|
|
1583
1645
|
}
|
|
1584
1646
|
function SearchBox() {
|
|
1585
|
-
const [query, setQuery] =
|
|
1586
|
-
const [open, setOpen] =
|
|
1587
|
-
const [active, setActive] =
|
|
1647
|
+
const [query, setQuery] = useState8("");
|
|
1648
|
+
const [open, setOpen] = useState8(false);
|
|
1649
|
+
const [active, setActive] = useState8(0);
|
|
1588
1650
|
const inputRef = useRef4(null);
|
|
1589
1651
|
const listId = "storybook-docs-search-results";
|
|
1590
1652
|
const optionId = (key) => `${listId}-${key}`;
|
|
1591
1653
|
const results = useMemo4(() => {
|
|
1592
1654
|
return searchCatalog(catalog2, query);
|
|
1593
1655
|
}, [query]);
|
|
1594
|
-
|
|
1656
|
+
useEffect7(() => {
|
|
1595
1657
|
setActive(0);
|
|
1596
1658
|
}, [query]);
|
|
1597
|
-
|
|
1659
|
+
useEffect7(() => {
|
|
1598
1660
|
const onGlobalKeyDown = (event) => {
|
|
1599
1661
|
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
|
|
1600
1662
|
event.preventDefault();
|
|
@@ -1617,7 +1679,7 @@ function SearchBox() {
|
|
|
1617
1679
|
const showDropdown = open && (hasResults || hasQuery);
|
|
1618
1680
|
const activeRow = hasResults && results[active] ? results[active] : void 0;
|
|
1619
1681
|
const activeOptionId = activeRow ? optionId(rowKey(activeRow)) : void 0;
|
|
1620
|
-
|
|
1682
|
+
useEffect7(() => {
|
|
1621
1683
|
if (showDropdown && activeOptionId) {
|
|
1622
1684
|
const el = document.getElementById(activeOptionId);
|
|
1623
1685
|
el?.scrollIntoView({ block: "nearest" });
|
|
@@ -1753,12 +1815,14 @@ function parseRoute(hash) {
|
|
|
1753
1815
|
return { kind: "overview" };
|
|
1754
1816
|
}
|
|
1755
1817
|
function DocsApp() {
|
|
1756
|
-
const [route, setRoute] =
|
|
1757
|
-
const [mode, setMode] =
|
|
1758
|
-
const [navOpen, setNavOpen] =
|
|
1818
|
+
const [route, setRoute] = useState9(() => parseRoute(window.location.hash));
|
|
1819
|
+
const [mode, setMode] = useState9(theme.values?.[0] ?? "dark");
|
|
1820
|
+
const [navOpen, setNavOpen] = useState9(false);
|
|
1821
|
+
const navRef = useRef5(null);
|
|
1822
|
+
useRailFollow(navRef);
|
|
1759
1823
|
const headerRef = useRef5(null);
|
|
1760
1824
|
const mainRef = useRef5(null);
|
|
1761
|
-
|
|
1825
|
+
useEffect8(() => {
|
|
1762
1826
|
const header = headerRef.current;
|
|
1763
1827
|
if (!header) return;
|
|
1764
1828
|
const root = document.documentElement;
|
|
@@ -1773,7 +1837,7 @@ function DocsApp() {
|
|
|
1773
1837
|
else root.style.removeProperty("--sd-header-height");
|
|
1774
1838
|
};
|
|
1775
1839
|
}, []);
|
|
1776
|
-
|
|
1840
|
+
useEffect8(() => {
|
|
1777
1841
|
const onHash = () => {
|
|
1778
1842
|
setRoute(parseRoute(window.location.hash));
|
|
1779
1843
|
setNavOpen(false);
|
|
@@ -1783,7 +1847,7 @@ function DocsApp() {
|
|
|
1783
1847
|
window.addEventListener("hashchange", onHash);
|
|
1784
1848
|
return () => window.removeEventListener("hashchange", onHash);
|
|
1785
1849
|
}, []);
|
|
1786
|
-
|
|
1850
|
+
useEffect8(() => {
|
|
1787
1851
|
document.documentElement.setAttribute(theme.attribute ?? "data-theme", mode);
|
|
1788
1852
|
}, [mode]);
|
|
1789
1853
|
const [darkMode, lightMode] = theme.values ?? ["dark", "light"];
|
|
@@ -1845,7 +1909,7 @@ function DocsApp() {
|
|
|
1845
1909
|
/* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u2197" })
|
|
1846
1910
|
] }, link.href))
|
|
1847
1911
|
] }),
|
|
1848
|
-
/* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", children: [
|
|
1912
|
+
/* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
|
|
1849
1913
|
/* @__PURE__ */ jsx16("a", { href: routeHref("/"), className: route.kind === "overview" ? "on" : void 0, "aria-current": route.kind === "overview" ? "page" : void 0, children: "Overview" }),
|
|
1850
1914
|
releases2.changelog ? /* @__PURE__ */ jsxs13(
|
|
1851
1915
|
"a",
|
|
@@ -1889,14 +1953,7 @@ function DocsApp() {
|
|
|
1889
1953
|
},
|
|
1890
1954
|
entry.slug
|
|
1891
1955
|
))
|
|
1892
|
-
] }, group))
|
|
1893
|
-
/* @__PURE__ */ jsxs13("div", { className: "nav-note", children: [
|
|
1894
|
-
"navigation, examples and API tables generated from ",
|
|
1895
|
-
stats2.components,
|
|
1896
|
-
" story files \xB7 ",
|
|
1897
|
-
stats2.stories,
|
|
1898
|
-
" stories"
|
|
1899
|
-
] })
|
|
1956
|
+
] }, group))
|
|
1900
1957
|
] }),
|
|
1901
1958
|
/* @__PURE__ */ jsx16("main", { id: "main", className: "main", tabIndex: -1, ref: mainRef, children: route.kind === "component" ? /* @__PURE__ */ jsx16(ComponentPage, { entry: catalog3.find((entry) => entry.slug === route.slug) }) : route.kind === "color" ? /* @__PURE__ */ jsx16(ColorPage, {}) : route.kind === "typography" ? /* @__PURE__ */ jsx16(TypePage, {}) : route.kind === "spacing" ? /* @__PURE__ */ jsx16(SpacingPage, {}) : route.kind === "effects" ? /* @__PURE__ */ jsx16(EffectsPage, {}) : route.kind === "releases" ? /* @__PURE__ */ jsx16(ReleasesPage, {}) : /* @__PURE__ */ jsx16(OverviewPage, {}) }),
|
|
1902
1959
|
route.kind !== "overview" ? /* @__PURE__ */ jsx16(PageRail, { pageKey: activeSlug ?? route.kind }) : null
|
package/dist/sections.d.ts
CHANGED
|
@@ -42,11 +42,12 @@ export interface ExampleStory {
|
|
|
42
42
|
export declare function selectExample(stories: ExampleStory[], parts?: string[], defaults?: Record<string, unknown> | null): string | null;
|
|
43
43
|
/** Only story-authored values: never synthesize children or component defaults. */
|
|
44
44
|
export declare function usageSnippet(componentName: string, args: Record<string, unknown> | undefined): string | null;
|
|
45
|
-
export declare function buildUsageSnippet({ componentName, from, args, source, jsxName, component }: {
|
|
45
|
+
export declare function buildUsageSnippet({ componentName, from, args, source, renderSource, jsxName, component }: {
|
|
46
46
|
componentName: string;
|
|
47
47
|
from: string;
|
|
48
48
|
args?: Record<string, unknown>;
|
|
49
49
|
source?: string;
|
|
50
|
+
renderSource?: string;
|
|
50
51
|
jsxName?: string;
|
|
51
52
|
component?: unknown;
|
|
52
53
|
}): string | null;
|
package/dist/styles.css
CHANGED
|
@@ -190,6 +190,9 @@ body {
|
|
|
190
190
|
border-right: 0;
|
|
191
191
|
background: var(--sd-surface);
|
|
192
192
|
padding: 14px 12px 80px;
|
|
193
|
+
/* Follows the document like the right rail, parking when its own end is visible. */
|
|
194
|
+
align-self: start;
|
|
195
|
+
transform: translateY(var(--sd-rail-follow, 0px));
|
|
193
196
|
}
|
|
194
197
|
.nav a {
|
|
195
198
|
display: block;
|
|
@@ -217,12 +220,6 @@ body {
|
|
|
217
220
|
color: var(--sd-text-muted);
|
|
218
221
|
font-weight: 700;
|
|
219
222
|
}
|
|
220
|
-
.nav-note {
|
|
221
|
-
margin: 22px 10px 0;
|
|
222
|
-
font-size: 11.5px;
|
|
223
|
-
line-height: 1.6;
|
|
224
|
-
color: var(--sd-text-muted);
|
|
225
|
-
}
|
|
226
223
|
|
|
227
224
|
.main {
|
|
228
225
|
padding: 48px clamp(28px, 4vw, 64px) 160px;
|
package/dist/toc.d.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
import { type RefObject } from 'react';
|
|
1
3
|
import './toc.css';
|
|
2
4
|
/** The hash without its query: `#/components/button?story=Variants` → `#/components/button`. */
|
|
3
5
|
export declare function hashPath(hash: string): string;
|
|
4
6
|
/** The `?story=` deep-link target of a hash, if any. */
|
|
5
7
|
export declare function storyFromHash(hash: string): string | null;
|
|
6
8
|
export declare function storyLink(location: Location, story: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* A rail travels with the document until its own end would pass the fold, then it parks there —
|
|
11
|
+
* the reader keeps seeing where it continues, and scrolling up lets it fall back into flow. This is
|
|
12
|
+
* the scroll-linked translation docs sites use, because `position: sticky` cannot move an element
|
|
13
|
+
* taller than the viewport.
|
|
14
|
+
*/
|
|
15
|
+
export declare function useRailFollow(ref: RefObject<HTMLElement | null>, ready?: boolean): void;
|
|
7
16
|
/** The "On this page" rail and the `?story=` deep-link landing. */
|
|
8
17
|
export declare function PageRail({ pageKey }: {
|
|
9
18
|
pageKey: string;
|
package/dist/virtual.d.ts
CHANGED
|
@@ -28,6 +28,10 @@ declare module 'virtual:storybook-docs/guidance' {
|
|
|
28
28
|
export function loadGuidance(slug: string): Promise<{ markdown: string }>;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
declare module 'virtual:storybook-docs/story-sources' {
|
|
32
|
+
export function loadStorySources(slug: string): Promise<{ sources: Record<string, string> }>;
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
declare module 'virtual:storybook-docs/releases' {
|
|
32
36
|
export const releases: { version: string | null; changelog: string | null };
|
|
33
37
|
}
|
package/dist/vite-plugin.js
CHANGED
|
@@ -608,6 +608,222 @@ export const fireEvent = () => true;
|
|
|
608
608
|
export const cleanup = () => {};
|
|
609
609
|
`;
|
|
610
610
|
|
|
611
|
+
// src/build/story-sources.ts
|
|
612
|
+
import ts2 from "typescript";
|
|
613
|
+
function unwrap(node) {
|
|
614
|
+
while (ts2.isParenthesizedExpression(node) || ts2.isAsExpression(node) || ts2.isSatisfiesExpression(node) || ts2.isTypeAssertionExpression(node)) node = node.expression;
|
|
615
|
+
return node;
|
|
616
|
+
}
|
|
617
|
+
var MAX_HELPER_DEPTH = 3;
|
|
618
|
+
function extractStorySourceDetails(source) {
|
|
619
|
+
const file = ts2.createSourceFile("stories.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
620
|
+
const declarations = /* @__PURE__ */ new Map();
|
|
621
|
+
const exports = /* @__PURE__ */ new Map();
|
|
622
|
+
for (const statement of file.statements) {
|
|
623
|
+
const exported = ts2.canHaveModifiers(statement) && ts2.getModifiers(statement)?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword);
|
|
624
|
+
if (ts2.isVariableStatement(statement)) {
|
|
625
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
626
|
+
if (!ts2.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
627
|
+
declarations.set(declaration.name.text, declaration.initializer);
|
|
628
|
+
if (exported) exports.set(declaration.name.text, declaration.name.text);
|
|
629
|
+
}
|
|
630
|
+
} else if (ts2.isFunctionDeclaration(statement) && statement.name) {
|
|
631
|
+
declarations.set(statement.name.text, statement);
|
|
632
|
+
if (exported) exports.set(statement.name.text, statement.name.text);
|
|
633
|
+
} else if (ts2.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
|
|
634
|
+
for (const element of statement.exportClause.elements) {
|
|
635
|
+
exports.set(element.name.text, (element.propertyName ?? element.name).text);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
const renderFor = (node, seen = /* @__PURE__ */ new Set()) => {
|
|
640
|
+
if (ts2.isFunctionDeclaration(node)) return node;
|
|
641
|
+
node = unwrap(node);
|
|
642
|
+
if (ts2.isIdentifier(node)) {
|
|
643
|
+
if (seen.has(node.text)) return;
|
|
644
|
+
seen.add(node.text);
|
|
645
|
+
const declaration = declarations.get(node.text);
|
|
646
|
+
return declaration ? renderFor(declaration, seen) : void 0;
|
|
647
|
+
}
|
|
648
|
+
if (ts2.isPropertyAccessExpression(node) && node.name.text === "render" || ts2.isElementAccessExpression(node) && ts2.isStringLiteral(node.argumentExpression) && node.argumentExpression.text === "render") return renderFor(node.expression, seen);
|
|
649
|
+
if (ts2.isArrowFunction(node) || ts2.isFunctionExpression(node)) return node;
|
|
650
|
+
if (!ts2.isObjectLiteralExpression(node)) return;
|
|
651
|
+
for (const property of [...node.properties].reverse()) {
|
|
652
|
+
if (ts2.isSpreadAssignment(property)) {
|
|
653
|
+
const render = renderFor(property.expression, new Set(seen));
|
|
654
|
+
if (render !== void 0) return render;
|
|
655
|
+
} else if (property.name && (ts2.isIdentifier(property.name) || ts2.isStringLiteral(property.name)) && property.name.text === "render") {
|
|
656
|
+
if (ts2.isMethodDeclaration(property)) return property;
|
|
657
|
+
if (ts2.isPropertyAssignment(property)) return renderFor(property.initializer, seen) ?? null;
|
|
658
|
+
if (ts2.isShorthandPropertyAssignment(property)) return renderFor(property.name, seen) ?? null;
|
|
659
|
+
return null;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
const hasJsx = (node) => ts2.isJsxElement(node) || ts2.isJsxSelfClosingElement(node) || ts2.isJsxFragment(node) || (ts2.forEachChild(node, hasJsx) ?? false);
|
|
664
|
+
const returnsCache = /* @__PURE__ */ new Map();
|
|
665
|
+
const returnCounts = /* @__PURE__ */ new Map();
|
|
666
|
+
const returnedJsx = (render) => {
|
|
667
|
+
const cached = returnsCache.get(render);
|
|
668
|
+
if (cached) return cached;
|
|
669
|
+
const expressions = [];
|
|
670
|
+
if (!render.body) return expressions;
|
|
671
|
+
let returnCount = 0;
|
|
672
|
+
if (ts2.isBlock(render.body)) {
|
|
673
|
+
const visit = (node) => {
|
|
674
|
+
if (ts2.isFunctionLike(node)) return;
|
|
675
|
+
if (ts2.isReturnStatement(node)) {
|
|
676
|
+
returnCount += 1;
|
|
677
|
+
if (node.expression && hasJsx(node.expression)) expressions.push(unwrap(node.expression));
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
ts2.forEachChild(node, visit);
|
|
681
|
+
};
|
|
682
|
+
ts2.forEachChild(render.body, visit);
|
|
683
|
+
} else {
|
|
684
|
+
returnCount = 1;
|
|
685
|
+
if (hasJsx(render.body)) expressions.push(unwrap(render.body));
|
|
686
|
+
}
|
|
687
|
+
returnsCache.set(render, expressions);
|
|
688
|
+
returnCounts.set(render, returnCount);
|
|
689
|
+
return expressions;
|
|
690
|
+
};
|
|
691
|
+
const helperFor = (name, seen = /* @__PURE__ */ new Set()) => {
|
|
692
|
+
if (seen.has(name)) return;
|
|
693
|
+
seen.add(name);
|
|
694
|
+
const declaration = declarations.get(name);
|
|
695
|
+
if (!declaration) return;
|
|
696
|
+
if (ts2.isFunctionDeclaration(declaration)) return declaration;
|
|
697
|
+
const expression = unwrap(declaration);
|
|
698
|
+
if (ts2.isIdentifier(expression)) return helperFor(expression.text, seen);
|
|
699
|
+
if (ts2.isArrowFunction(expression) || ts2.isFunctionExpression(expression)) return expression;
|
|
700
|
+
};
|
|
701
|
+
const bindingContains = (binding, name) => ts2.isIdentifier(binding) ? binding.text === name : binding.elements.some((element) => ts2.isBindingElement(element) && bindingContains(element.name, name));
|
|
702
|
+
const isShadowed = (expression, name) => {
|
|
703
|
+
for (let node = expression.parent; node; node = node.parent) {
|
|
704
|
+
if (ts2.isFunctionLike(node) && node.parameters.some((parameter) => bindingContains(parameter.name, name))) return true;
|
|
705
|
+
if (ts2.isBlock(node) && node.statements.some((statement) => ts2.isVariableStatement(statement) && statement.declarationList.declarations.some((declaration) => bindingContains(declaration.name, name)) || ts2.isFunctionDeclaration(statement) && statement.name?.text === name)) return true;
|
|
706
|
+
}
|
|
707
|
+
return false;
|
|
708
|
+
};
|
|
709
|
+
const mapCache = /* @__PURE__ */ new Map();
|
|
710
|
+
const hasUnknownMap = (helper, jsx) => {
|
|
711
|
+
const cached = mapCache.get(jsx);
|
|
712
|
+
if (cached !== void 0) return cached;
|
|
713
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
714
|
+
for (const [name, declaration] of declarations) {
|
|
715
|
+
bindings.set(name, ts2.isFunctionDeclaration(declaration) ? void 0 : declaration);
|
|
716
|
+
}
|
|
717
|
+
const bind = (scope2, name, initializer) => {
|
|
718
|
+
if (ts2.isIdentifier(name)) scope2.set(name.text, initializer);
|
|
719
|
+
else for (const element of name.elements) {
|
|
720
|
+
if (ts2.isBindingElement(element)) bind(scope2, element.name, element.initializer);
|
|
721
|
+
}
|
|
722
|
+
};
|
|
723
|
+
const addScope = (node, parent) => {
|
|
724
|
+
const scope2 = new Map(parent);
|
|
725
|
+
if (ts2.isFunctionLike(node)) {
|
|
726
|
+
for (const parameter of node.parameters) bind(scope2, parameter.name, parameter.initializer);
|
|
727
|
+
}
|
|
728
|
+
if (ts2.isBlock(node)) {
|
|
729
|
+
for (const statement of node.statements) {
|
|
730
|
+
if (ts2.isVariableStatement(statement)) {
|
|
731
|
+
for (const declaration of statement.declarationList.declarations) bind(scope2, declaration.name, declaration.initializer);
|
|
732
|
+
} else if (ts2.isFunctionDeclaration(statement) && statement.name) scope2.set(statement.name.text, void 0);
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return scope2;
|
|
736
|
+
};
|
|
737
|
+
const ancestors = [];
|
|
738
|
+
for (let node = jsx.parent; node !== helper; node = node.parent) ancestors.push(node);
|
|
739
|
+
let scope = addScope(helper, bindings);
|
|
740
|
+
for (const node of ancestors.reverse()) scope = addScope(node, scope);
|
|
741
|
+
const knownCollection = (expression, bindings2, seen = /* @__PURE__ */ new Set()) => {
|
|
742
|
+
expression = unwrap(expression);
|
|
743
|
+
if (ts2.isArrayLiteralExpression(expression)) {
|
|
744
|
+
return expression.elements.every((element) => !ts2.isSpreadElement(element) || knownCollection(element.expression, bindings2, new Set(seen)));
|
|
745
|
+
}
|
|
746
|
+
if (ts2.isIdentifier(expression)) {
|
|
747
|
+
if (seen.has(expression.text)) return false;
|
|
748
|
+
seen.add(expression.text);
|
|
749
|
+
const initializer = bindings2.get(expression.text);
|
|
750
|
+
return Boolean(initializer && knownCollection(initializer, bindings2, seen));
|
|
751
|
+
}
|
|
752
|
+
if (ts2.isCallExpression(expression) && ts2.isPropertyAccessExpression(expression.expression) && ["slice", "filter", "toSorted", "toReversed"].includes(expression.expression.name.text)) {
|
|
753
|
+
return knownCollection(expression.expression.expression, bindings2, seen);
|
|
754
|
+
}
|
|
755
|
+
return false;
|
|
756
|
+
};
|
|
757
|
+
const visit = (node, bindings2) => {
|
|
758
|
+
if (ts2.isFunctionLike(node) || ts2.isBlock(node)) bindings2 = addScope(node, bindings2);
|
|
759
|
+
if (ts2.isCallExpression(node)) {
|
|
760
|
+
const target = node.expression;
|
|
761
|
+
const map = ts2.isPropertyAccessExpression(target) && target.name.text === "map" || ts2.isElementAccessExpression(target) && ts2.isStringLiteral(target.argumentExpression) && target.argumentExpression.text === "map";
|
|
762
|
+
if (map && !knownCollection(target.expression, bindings2)) return true;
|
|
763
|
+
}
|
|
764
|
+
return ts2.forEachChild(node, (child) => visit(child, bindings2)) ?? false;
|
|
765
|
+
};
|
|
766
|
+
const unknown = visit(jsx, scope);
|
|
767
|
+
mapCache.set(jsx, unknown);
|
|
768
|
+
return unknown;
|
|
769
|
+
};
|
|
770
|
+
const inlineRoot = (expression, detail, trail = []) => {
|
|
771
|
+
const tag = ts2.isJsxElement(expression) ? expression.openingElement.tagName : ts2.isJsxSelfClosingElement(expression) ? expression.tagName : void 0;
|
|
772
|
+
const name = tag && ts2.isIdentifier(tag) && /^[A-Z]/.test(tag.text) ? tag.text : void 0;
|
|
773
|
+
const helper = name && helperFor(name);
|
|
774
|
+
if (!name || !declarations.has(name)) return expression.getText(file);
|
|
775
|
+
if (!helper) {
|
|
776
|
+
const reason2 = "local definition is not a direct function";
|
|
777
|
+
detail.remaining.push({ name, reason: reason2 });
|
|
778
|
+
return `// ${name} not expanded: ${reason2}.
|
|
779
|
+
${expression.getText(file)}`;
|
|
780
|
+
}
|
|
781
|
+
const returned = returnedJsx(helper);
|
|
782
|
+
const jsx = returned[0];
|
|
783
|
+
const reason = isShadowed(expression, name) ? "file helper is shadowed in this scope" : trail.includes(name) ? "recursive helper cycle" : trail.length >= MAX_HELPER_DEPTH ? `helper depth limit (${MAX_HELPER_DEPTH})` : returnCounts.get(helper) !== 1 || returned.length !== 1 || !jsx || !(ts2.isJsxElement(jsx) || ts2.isJsxSelfClosingElement(jsx) || ts2.isJsxFragment(jsx)) ? "no single literal JSX return" : hasUnknownMap(helper, jsx) ? "maps over a collection without a local array definition" : void 0;
|
|
784
|
+
if (reason) {
|
|
785
|
+
detail.remaining.push({ name, reason });
|
|
786
|
+
return `// ${name} not expanded: ${reason}.
|
|
787
|
+
${expression.getText(file)}`;
|
|
788
|
+
}
|
|
789
|
+
detail.inlined.push(name);
|
|
790
|
+
return `// JSX from ${name}, as authored.
|
|
791
|
+
${inlineRoot(jsx, detail, [...trail, name])}`;
|
|
792
|
+
};
|
|
793
|
+
const sources = {};
|
|
794
|
+
const helpers = {};
|
|
795
|
+
for (const [name, local] of exports) {
|
|
796
|
+
const declaration = declarations.get(local);
|
|
797
|
+
const render = declaration && renderFor(declaration);
|
|
798
|
+
if (!render?.body) continue;
|
|
799
|
+
const expressions = returnedJsx(render);
|
|
800
|
+
if (expressions.length) {
|
|
801
|
+
const detail = { inlined: [], remaining: [] };
|
|
802
|
+
const jsx = expressions.map((expression) => inlineRoot(expression, detail)).join("\n\n");
|
|
803
|
+
sources[name] = expressions.length > 1 ? `// JSX return branches as authored in the story.
|
|
804
|
+
${jsx}` : jsx;
|
|
805
|
+
if (detail.inlined.length || detail.remaining.length) helpers[name] = detail;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return { sources, helpers };
|
|
809
|
+
}
|
|
810
|
+
function extractStorySources(source) {
|
|
811
|
+
return extractStorySourceDetails(source).sources;
|
|
812
|
+
}
|
|
813
|
+
function storySourcesIndexCode(slugs) {
|
|
814
|
+
return `
|
|
815
|
+
export function loadStorySources(slug) {
|
|
816
|
+
switch (slug) {
|
|
817
|
+
${[...slugs].map((slug) => ` case ${JSON.stringify(slug)}: return import(${JSON.stringify(`${VIRTUAL_PREFIX}story-sources/${slug}`)});`).join("\n")}
|
|
818
|
+
default: return Promise.resolve({ sources: {} });
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
`;
|
|
822
|
+
}
|
|
823
|
+
function storySourcesCode(sources) {
|
|
824
|
+
return `export const sources = ${JSON.stringify(sources)};`;
|
|
825
|
+
}
|
|
826
|
+
|
|
611
827
|
// src/vite-plugin.ts
|
|
612
828
|
function storybookDocs(options) {
|
|
613
829
|
const { config } = options;
|
|
@@ -616,6 +832,7 @@ function storybookDocs(options) {
|
|
|
616
832
|
catalog.files.clear();
|
|
617
833
|
const engineStyles = path7.join(import.meta.dirname, "styles.css");
|
|
618
834
|
const propsCache = /* @__PURE__ */ new Map();
|
|
835
|
+
const storySourcesCache = /* @__PURE__ */ new Map();
|
|
619
836
|
const propsCollector = createPropsCollector();
|
|
620
837
|
let guidanceFiles = /* @__PURE__ */ new Map();
|
|
621
838
|
const loadProps = (slug) => {
|
|
@@ -636,6 +853,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
636
853
|
"stub-storybook-test": () => STORYBOOK_TEST_STUB,
|
|
637
854
|
props: () => propsIndexCode(catalog.entries.map((entry) => entry.slug)),
|
|
638
855
|
guidance: () => guidanceIndexCode(guidanceFiles.keys()),
|
|
856
|
+
"story-sources": () => storySourcesIndexCode(catalog.files.keys()),
|
|
639
857
|
releases: () => releasesCode(readReleases(config, projectRoot)),
|
|
640
858
|
code: () => codeModuleCode(resolveCodeModule(config.code?.module, projectRoot))
|
|
641
859
|
};
|
|
@@ -665,6 +883,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
665
883
|
},
|
|
666
884
|
buildStart() {
|
|
667
885
|
propsCache.clear();
|
|
886
|
+
storySourcesCache.clear();
|
|
668
887
|
propsCollector.clearPrograms();
|
|
669
888
|
},
|
|
670
889
|
closeBundle() {
|
|
@@ -682,11 +901,26 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
|
|
|
682
901
|
const module = Object.hasOwn(modules, name) ? modules[name] : void 0;
|
|
683
902
|
if (module) return module();
|
|
684
903
|
if (name.startsWith("props/")) return loadProps(name.slice("props/".length));
|
|
904
|
+
if (name.startsWith("story-sources/")) {
|
|
905
|
+
const storyFile = catalog.files.get(name.slice("story-sources/".length));
|
|
906
|
+
if (!storyFile) return storySourcesCode({});
|
|
907
|
+
const file = realpathSync(storyFile);
|
|
908
|
+
let sources = storySourcesCache.get(file);
|
|
909
|
+
if (!sources) {
|
|
910
|
+
sources = extractStorySources(readFileSync3(file, "utf8"));
|
|
911
|
+
storySourcesCache.set(file, sources);
|
|
912
|
+
}
|
|
913
|
+
this.addWatchFile(file);
|
|
914
|
+
return storySourcesCode(sources);
|
|
915
|
+
}
|
|
685
916
|
if (name.startsWith("guidance/")) {
|
|
686
917
|
const file = guidanceFiles.get(name.slice("guidance/".length));
|
|
687
918
|
return guidanceCode(file ? readFileSync3(file, "utf8") : "");
|
|
688
919
|
}
|
|
689
920
|
return null;
|
|
921
|
+
},
|
|
922
|
+
watchChange(file) {
|
|
923
|
+
storySourcesCache.delete(file);
|
|
690
924
|
}
|
|
691
925
|
};
|
|
692
926
|
}
|
package/docs/RELEASING.md
CHANGED
|
@@ -125,3 +125,29 @@ Official npm references (check them when changing authentication):
|
|
|
125
125
|
[trusted publishers](https://docs.npmjs.com/trusted-publishers/),
|
|
126
126
|
[provenance](https://docs.npmjs.com/generating-provenance-statements/),
|
|
127
127
|
[granular tokens](https://docs.npmjs.com/creating-and-viewing-access-tokens/).
|
|
128
|
+
|
|
129
|
+
## First publish of a new package name
|
|
130
|
+
|
|
131
|
+
Established procedure, after a morning spent rediscovering it:
|
|
132
|
+
|
|
133
|
+
1. **`npm trust` works before the package exists.** `npm trust github <pkg> --repo <owner>/<repo> --file publish.yml --allow-publish`
|
|
134
|
+
returns **201** even for a name that has never been published, as long as you are authenticated. Do this first; it is
|
|
135
|
+
what lets every later `v*` tag publish itself through OIDC with provenance, no tokens.
|
|
136
|
+
2. **Workflows that set `NODE_AUTH_TOKEN` cannot use trusted publishing.** An empty secret sends an
|
|
137
|
+
unauthenticated request and npm answers `404` on the `PUT`, which reads like a missing package. This workflow
|
|
138
|
+
therefore sets no token.
|
|
139
|
+
3. **The first publish has to happen once from a machine** — the registry has no artifact yet:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
cd <the package checkout>
|
|
143
|
+
npm login --registry https://registry.npmjs.org # prints a browser URL; no OTP code needed
|
|
144
|
+
NPM_CONFIG_USERCONFIG=<config> npm publish # browser URL again, then "+ <pkg>@<version>"
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
A `~/.npmrc` that maps the scope to another registry (e.g. `@scope:registry=https://npm.pkg.github.com/`) makes a
|
|
148
|
+
plain `npm publish` target that registry instead and fail with `403`. Point the scope at npmjs in the config used
|
|
149
|
+
for the publish.
|
|
150
|
+
4. **Verify from the registry, not from the local output:** `curl -s https://registry.npmjs.org/<scope>%2f<pkg>`
|
|
151
|
+
must list the version. Until it does, nothing is published.
|
|
152
|
+
5. After that, releases are tags: `git tag -a vX.Y.Z && git push origin vX.Y.Z`, and the publish workflow does the
|
|
153
|
+
rest. Do **not** push a tag for a version you already published by hand — npm rejects the duplicate.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jfrader/storybook-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A documentation front for any design system that has Storybook: navigation, live examples and API tables are generated from the story files themselves.",
|
|
6
6
|
"exports": {
|
|
@@ -27,12 +27,6 @@
|
|
|
27
27
|
"react-dom": "^19.3.0",
|
|
28
28
|
"vite": "^8.3.4"
|
|
29
29
|
},
|
|
30
|
-
"packageManager": "pnpm@10.12.1",
|
|
31
|
-
"pnpm": {
|
|
32
|
-
"onlyBuiltDependencies": [
|
|
33
|
-
"esbuild"
|
|
34
|
-
]
|
|
35
|
-
},
|
|
36
30
|
"dependencies": {
|
|
37
31
|
"marked": "^18.1.1",
|
|
38
32
|
"react-docgen-typescript": "^2.4.0",
|
|
@@ -60,15 +54,6 @@
|
|
|
60
54
|
"props",
|
|
61
55
|
"docs"
|
|
62
56
|
],
|
|
63
|
-
"scripts": {
|
|
64
|
-
"test": "node --test test/*.test.ts",
|
|
65
|
-
"typecheck": "tsc --noEmit",
|
|
66
|
-
"build": "pnpm build:package && pnpm --dir examples/jfrader-ui build",
|
|
67
|
-
"build:package": "node scripts/build.mjs",
|
|
68
|
-
"smoke:package": "node scripts/smoke-package.mjs",
|
|
69
|
-
"check:frames": "node scripts/check-example-frames.mjs",
|
|
70
|
-
"prepack": "pnpm build:package"
|
|
71
|
-
},
|
|
72
57
|
"engines": {
|
|
73
58
|
"node": ">=22.12.0"
|
|
74
59
|
},
|
|
@@ -81,5 +66,13 @@
|
|
|
81
66
|
],
|
|
82
67
|
"sideEffects": [
|
|
83
68
|
"**/*.css"
|
|
84
|
-
]
|
|
85
|
-
|
|
69
|
+
],
|
|
70
|
+
"scripts": {
|
|
71
|
+
"test": "node --test test/*.test.ts",
|
|
72
|
+
"typecheck": "tsc --noEmit",
|
|
73
|
+
"build": "pnpm build:package && pnpm --dir examples/jfrader-ui build",
|
|
74
|
+
"build:package": "node scripts/build.mjs",
|
|
75
|
+
"smoke:package": "node scripts/smoke-package.mjs",
|
|
76
|
+
"check:frames": "node scripts/check-example-frames.mjs"
|
|
77
|
+
}
|
|
78
|
+
}
|