@jfrader/storybook-docs 0.5.0 → 0.7.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.
@@ -0,0 +1,3 @@
1
+ import ts from 'typescript';
2
+ export declare function snippetProgram(file: ts.SourceFile): ts.Program;
3
+ export declare function unresolvedSnippetBindings(source: string, localTags?: Set<string>): string[];
package/dist/index.css CHANGED
@@ -106,16 +106,20 @@
106
106
  .toc {
107
107
  display: none;
108
108
  }
109
+ .toc-track {
110
+ display: none;
111
+ }
109
112
  @media (min-width: 1180px) {
110
113
  .app.has-toc {
111
114
  grid-template-columns: 276px minmax(0, 1fr) 224px;
112
115
  }
113
- .toc {
116
+ .toc-track {
114
117
  display: block;
115
118
  grid-column: 3;
116
119
  grid-row: 2;
117
- align-self: start;
118
- transform: translateY(var(--sd-rail-follow, 0px));
120
+ }
121
+ .toc {
122
+ display: block;
119
123
  padding: 34px 18px 40px 6px;
120
124
  font-size: 12.5px;
121
125
  }
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ 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 useEffect8, useRef as useRef5, useState as useState9 } from "react";
8
+ import { Children, useEffect as useEffect8, useRef as useRef5, useState as useState9 } from "react";
9
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
 
@@ -263,6 +263,11 @@ import { useCallback as useCallback2, useEffect as useEffect4, useId, useRef as
263
263
  import { Component as Component2, createElement as createElement2, useEffect as useEffect2, useRef, useState as useState2 } from "react";
264
264
 
265
265
  // src/sections.ts
266
+ function frameSource(meta, story) {
267
+ const unchanged = Object.entries(story.args ?? {}).every(([key, value]) => Object.hasOwn(meta.args ?? {}, key) && Object.is(value, meta.args?.[key]));
268
+ return story.parameters?.docs?.source?.code ?? (unchanged ? meta.parameters?.docs?.source?.code : void 0);
269
+ }
270
+ var STORY_EXCERPT = "// Excerpt from the story";
266
271
  function mergeAnnotations(...values) {
267
272
  const merged = {};
268
273
  const isRecord = (value) => value !== null && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype;
@@ -375,13 +380,20 @@ function usageSnippet(componentName, args) {
375
380
  return `${opening}>${children}</${componentName}>`;
376
381
  }
377
382
  function buildUsageSnippet({ componentName, from, args, source, renderSource, jsxName, component }) {
378
- if (!source?.trim() && renderSource?.trim()) return renderSource;
379
383
  const rootName = componentName.split(".")[0];
380
384
  const identity = component;
381
385
  const tag = jsxName ?? (typeof identity?.displayName === "string" && identity.displayName.startsWith(`${rootName}.`) ? identity.displayName : componentName);
382
386
  const compound = component != null && Object.entries(component).some(([name, part]) => /^[A-Z]/.test(name) && (typeof part === "function" || part !== null && typeof part === "object" && "$$typeof" in part));
383
- const example = source?.trim() || (compound && !tag.includes(".") ? null : usageSnippet(tag, args));
387
+ const generated = () => compound && !tag.includes(".") ? null : usageSnippet(tag, args);
388
+ const extracted = renderSource?.trim();
389
+ const excerpt = extracted?.startsWith(STORY_EXCERPT);
390
+ const example = source?.trim() || (excerpt ? generated() || extracted : extracted || generated());
384
391
  if (!example) return null;
392
+ if (!source?.trim() && example === extracted) {
393
+ const rootTag = example.replace(/^\s*\/\/[^\n]*(?:\n|$)/gm, "").trim().match(/^<([A-Za-z_$][\w$]*(?:\.[\w$]+)*)[\s/>]/)?.[1]?.split(".")[0];
394
+ if (excerpt || rootTag !== rootName || !from.trim()) return example;
395
+ }
396
+ if (!from.trim()) return example;
385
397
  const imports = [...example.matchAll(/import\s*\{([^}]+)\}\s*from\s*(['"])(.*?)\2/g)];
386
398
  const imported = imports.some((match) => match[3] === from && match[1].split(",").some((name) => name.trim() === rootName));
387
399
  return imported ? example : `import { ${rootName} } from '${from}';
@@ -511,6 +523,77 @@ function LazySection({ children, placeholder = "\u2026" }) {
511
523
 
512
524
  // src/toc.tsx
513
525
  import { useCallback, useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
526
+
527
+ // src/rail.ts
528
+ var RAIL_MEDIA = "(min-width: 1180px)";
529
+ var RAIL_GAP = 20;
530
+ function followRail(rail, header) {
531
+ const track = rail.parentElement;
532
+ if (!track) return () => {
533
+ };
534
+ const media = window.matchMedia(RAIL_MEDIA);
535
+ let lastY = window.scrollY;
536
+ let direction = "down";
537
+ let spacer = 0;
538
+ const geometry = () => {
539
+ const top = header.offsetHeight + RAIL_GAP;
540
+ const viewportBottom = window.innerHeight - RAIL_GAP;
541
+ const height = rail.offsetHeight;
542
+ return {
543
+ top,
544
+ viewportBottom,
545
+ height,
546
+ fits: top + height <= viewportBottom,
547
+ trackTop: track.getBoundingClientRect().top + window.scrollY,
548
+ maxOffset: Math.max(0, track.clientHeight - height)
549
+ };
550
+ };
551
+ const clear = () => {
552
+ rail.removeAttribute("style");
553
+ track.removeAttribute("style");
554
+ };
555
+ const arm = (g = geometry()) => {
556
+ if (!media.matches) {
557
+ clear();
558
+ return;
559
+ }
560
+ spacer = g.fits ? 0 : Math.min(spacer, g.maxOffset);
561
+ track.style.setProperty("--sd-rail-offset", `${spacer}px`);
562
+ rail.style.position = "sticky";
563
+ rail.style.top = g.fits ? `${g.top}px` : direction === "down" ? `${g.viewportBottom - g.height}px` : "auto";
564
+ rail.style.bottom = g.fits || direction === "down" ? "auto" : `${window.innerHeight - g.top - g.height}px`;
565
+ };
566
+ const onScroll = () => {
567
+ const y = window.scrollY;
568
+ const next = y > lastY ? "down" : y < lastY ? "up" : direction;
569
+ if (next !== direction && media.matches) {
570
+ const g = geometry();
571
+ spacer = direction === "down" ? Math.min(Math.max(spacer, lastY + g.viewportBottom - g.height - g.trackTop), g.maxOffset) : Math.max(Math.min(spacer, lastY + g.top - g.trackTop), 0);
572
+ direction = next;
573
+ arm(g);
574
+ }
575
+ direction = next;
576
+ lastY = y;
577
+ };
578
+ const relayout = () => arm();
579
+ const observer = new ResizeObserver(relayout);
580
+ observer.observe(rail);
581
+ observer.observe(track);
582
+ observer.observe(header);
583
+ window.addEventListener("scroll", onScroll, { passive: true });
584
+ window.addEventListener("resize", relayout);
585
+ media.addEventListener("change", relayout);
586
+ arm();
587
+ return () => {
588
+ observer.disconnect();
589
+ window.removeEventListener("scroll", onScroll);
590
+ window.removeEventListener("resize", relayout);
591
+ media.removeEventListener("change", relayout);
592
+ clear();
593
+ };
594
+ }
595
+
596
+ // src/toc.tsx
514
597
  import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
515
598
  var STORY_PARAM = "story";
516
599
  var SCROLL_OFFSET_PX = 76;
@@ -535,34 +618,9 @@ var slugify = (text) => text.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCa
535
618
  function useRailFollow(ref, ready = true) {
536
619
  useEffect3(() => {
537
620
  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
- };
621
+ const header = element?.closest(".app")?.querySelector(".top");
622
+ if (!element || !header) return;
623
+ return followRail(element, header);
566
624
  }, [ready, ref]);
567
625
  }
568
626
  function scanPage(root, pageKey) {
@@ -706,7 +764,7 @@ function PageRail({ pageKey }) {
706
764
  const active = useActiveIds(scan);
707
765
  const go = useCallback((element) => scrollToElement(element), []);
708
766
  if (!scan) return null;
709
- return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
767
+ return /* @__PURE__ */ jsx5(Fragment, { children: scan.sections.length > 0 ? /* @__PURE__ */ jsx5("div", { className: "rail-track toc-track", children: /* @__PURE__ */ jsxs3("aside", { className: "toc", "aria-label": "On this page", ref: railRef, children: [
710
768
  /* @__PURE__ */ jsx5("div", { className: "toc-title", children: "On this page" }),
711
769
  /* @__PURE__ */ jsx5("ul", { children: scan.sections.map((section) => /* @__PURE__ */ jsxs3("li", { children: [
712
770
  /* @__PURE__ */ jsx5(
@@ -731,7 +789,7 @@ function PageRail({ pageKey }) {
731
789
  }
732
790
  ) }, story.id)) }) : null
733
791
  ] }, section.id)) })
734
- ] }) : null });
792
+ ] }) }) : null });
735
793
  }
736
794
 
737
795
  // src/component-demo.tsx
@@ -992,7 +1050,7 @@ function ControlledStoryFrame({ module, story, componentName, componentDefaults,
992
1050
  const storyLayout = requestedLayout === "centered" || requestedLayout === "fullscreen" ? requestedLayout : "padded";
993
1051
  const docsOverride = definition.parameters?.docs;
994
1052
  const docsMeta = meta.parameters?.docs;
995
- const authoredCode = docsOverride?.source?.code ?? docsMeta?.source?.code;
1053
+ const authoredCode = frameSource(meta, definition);
996
1054
  const authoredStory = docsOverride?.description?.story ?? docsMeta?.description?.story;
997
1055
  const storyName = definition.name ?? story;
998
1056
  const skipped = ["loaders", "beforeEach"].filter((key) => meta[key] || definition[key]);
@@ -1251,7 +1309,7 @@ function ComponentPage({ entry }) {
1251
1309
  " props"
1252
1310
  ] })
1253
1311
  ] }),
1254
- /* @__PURE__ */ jsx8("p", { className: "lede", children: description ?? `${entry.name} \u2014 every story in ${entry.storybookTitle} is rendered below, in sections.` }),
1312
+ description ? /* @__PURE__ */ jsx8("p", { className: "lede", children: description }) : null,
1255
1313
  error ? /* @__PURE__ */ jsxs5("div", { className: "story-failed", children: [
1256
1314
  "Could not load the story module: ",
1257
1315
  error
@@ -1276,9 +1334,7 @@ function ComponentPage({ entry }) {
1276
1334
  ] }, section.title))
1277
1335
  ] }) : null,
1278
1336
  /* @__PURE__ */ jsx8("h2", { children: "Props" }),
1279
- /* @__PURE__ */ jsx8(PropsTable, { components: props, argTypes, defaultArgs }),
1280
- /* @__PURE__ */ jsx8("h2", { children: "Notes" }),
1281
- /* @__PURE__ */ jsx8("p", { children: "Everything above is generated from this component\u2019s story module and source files. Editorial judgement \u2014 when to reach for it, when not to \u2014 is written next to the code and rendered as guidance above." })
1337
+ /* @__PURE__ */ jsx8(PropsTable, { components: props, argTypes, defaultArgs })
1282
1338
  ] });
1283
1339
  }
1284
1340
 
@@ -1801,6 +1857,23 @@ function SearchBox() {
1801
1857
  import { releases as releases2 } from "virtual:storybook-docs/releases";
1802
1858
  import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
1803
1859
  var routeHref = (path) => `#${hrefFor(path)}`;
1860
+ var NAV_GROUP_WINDOW = 12;
1861
+ function NavGroup({ id, title, active, activeIndex = -1, children }) {
1862
+ const [open, setOpen] = useState9(false);
1863
+ const [showAll, setShowAll] = useState9(false);
1864
+ const expanded = active || open;
1865
+ const links = Children.toArray(children);
1866
+ const capped = active && links.length > NAV_GROUP_WINDOW;
1867
+ const start2 = Math.min(Math.max(activeIndex - NAV_GROUP_WINDOW / 2, 0), Math.max(0, links.length - NAV_GROUP_WINDOW));
1868
+ const visibleLinks = capped && !showAll ? links.slice(start2, start2 + NAV_GROUP_WINDOW) : links;
1869
+ return /* @__PURE__ */ jsxs13("div", { role: "group", "aria-labelledby": id, children: [
1870
+ /* @__PURE__ */ jsx16("button", { type: "button", className: "grp", id, "aria-expanded": expanded, "aria-controls": `${id}-links`, disabled: active, onClick: () => setOpen(!open), children: title }),
1871
+ /* @__PURE__ */ jsxs13("div", { id: `${id}-links`, hidden: !expanded, children: [
1872
+ /* @__PURE__ */ jsx16("div", { id: `${id}-window`, children: visibleLinks }),
1873
+ capped ? /* @__PURE__ */ jsx16("button", { type: "button", className: "nav-more", "aria-expanded": showAll, "aria-controls": `${id}-window`, onClick: () => setShowAll(!showAll), children: showAll ? "Show less" : `Show all ${links.length}` }) : null
1874
+ ] })
1875
+ ] });
1876
+ }
1804
1877
  function parseRoute(hash) {
1805
1878
  const path = hashPath(hash).replace(new RegExp(`^#?\\/?${STORYBOOK_DOCS_BASE}`), "").replace(/^\/+/, "");
1806
1879
  if (path.startsWith("components/")) {
@@ -1909,7 +1982,7 @@ function DocsApp() {
1909
1982
  /* @__PURE__ */ jsx16("span", { "aria-hidden": "true", children: "\u2197" })
1910
1983
  ] }, link.href))
1911
1984
  ] }),
1912
- /* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
1985
+ /* @__PURE__ */ jsx16("div", { className: "rail-track nav-track", children: /* @__PURE__ */ jsxs13("nav", { id: "docs-nav", className: navOpen ? "nav open" : "nav", "aria-label": "Documentation", ref: navRef, children: [
1913
1986
  /* @__PURE__ */ jsx16("a", { href: routeHref("/"), className: route.kind === "overview" ? "on" : void 0, "aria-current": route.kind === "overview" ? "page" : void 0, children: "Overview" }),
1914
1987
  releases2.changelog ? /* @__PURE__ */ jsxs13(
1915
1988
  "a",
@@ -1926,22 +1999,20 @@ function DocsApp() {
1926
1999
  ]
1927
2000
  }
1928
2001
  ) : null,
1929
- foundations.length > 0 ? /* @__PURE__ */ jsxs13("div", { role: "group", "aria-labelledby": "nav-grp-foundations", children: [
1930
- /* @__PURE__ */ jsx16("div", { className: "grp", id: "nav-grp-foundations", children: "Foundations" }),
1931
- foundations.map((item) => /* @__PURE__ */ jsx16(
1932
- "a",
1933
- {
1934
- href: item.href,
1935
- className: route.kind === item.kind ? "on" : void 0,
1936
- "aria-current": route.kind === item.kind ? "page" : void 0,
1937
- children: item.label
1938
- },
1939
- item.href
1940
- ))
1941
- ] }) : null,
1942
- groups2.map((group, index) => /* @__PURE__ */ jsxs13("div", { role: "group", "aria-labelledby": `nav-grp-${index}`, children: [
1943
- /* @__PURE__ */ jsx16("div", { className: "grp", id: `nav-grp-${index}`, children: group }),
1944
- catalog3.filter((entry) => entry.group === group).map((entry) => /* @__PURE__ */ jsx16(
2002
+ foundations.length > 0 ? /* @__PURE__ */ jsx16(NavGroup, { id: "nav-grp-foundations", title: "Foundations", active: foundations.some((item) => route.kind === item.kind), children: foundations.map((item) => /* @__PURE__ */ jsx16(
2003
+ "a",
2004
+ {
2005
+ href: item.href,
2006
+ className: route.kind === item.kind ? "on" : void 0,
2007
+ "aria-current": route.kind === item.kind ? "page" : void 0,
2008
+ children: item.label
2009
+ },
2010
+ item.href
2011
+ )) }, `foundations-${activeSlug ?? route.kind}`) : null,
2012
+ groups2.map((group, index) => {
2013
+ const entries = catalog3.filter((entry) => entry.group === group);
2014
+ const activeIndex = entries.findIndex((entry) => entry.slug === activeSlug);
2015
+ return /* @__PURE__ */ jsx16(NavGroup, { id: `nav-grp-${index}`, title: group, active: activeIndex !== -1, activeIndex, children: entries.map((entry) => /* @__PURE__ */ jsx16(
1945
2016
  "a",
1946
2017
  {
1947
2018
  href: routeHref(`/components/${entry.slug}`),
@@ -1952,9 +2023,9 @@ function DocsApp() {
1952
2023
  children: entry.name
1953
2024
  },
1954
2025
  entry.slug
1955
- ))
1956
- ] }, group))
1957
- ] }),
2026
+ )) }, `${group}-${activeSlug ?? route.kind}`);
2027
+ })
2028
+ ] }) }),
1958
2029
  /* @__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, {}) }),
1959
2030
  route.kind !== "overview" ? /* @__PURE__ */ jsx16(PageRail, { pageKey: activeSlug ?? route.kind }) : null
1960
2031
  ] });
package/dist/rail.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function followRail(rail: HTMLElement, header: HTMLElement): () => void;
@@ -13,6 +13,18 @@ export type DocsParameters = {
13
13
  language?: string;
14
14
  };
15
15
  };
16
+ export declare function frameSource(meta: {
17
+ args?: Record<string, unknown>;
18
+ parameters?: {
19
+ docs?: DocsParameters;
20
+ };
21
+ }, story: {
22
+ args?: Record<string, unknown>;
23
+ parameters?: {
24
+ docs?: DocsParameters;
25
+ };
26
+ }): string | undefined;
27
+ export declare const STORY_EXCERPT = "// Excerpt from the story";
16
28
  export declare function mergeAnnotations(...values: Array<Record<string, unknown> | undefined>): Record<string, unknown>;
17
29
  export interface StorySection {
18
30
  title: string;
package/dist/styles.css CHANGED
@@ -186,23 +186,44 @@ body {
186
186
  outline-offset: 2px;
187
187
  }
188
188
 
189
+ .rail-track {
190
+ overflow-anchor: none;
191
+ min-width: 0;
192
+ }
193
+ @media (min-width: 1180px) {
194
+ .rail-track::before {
195
+ content: '';
196
+ display: block;
197
+ height: var(--sd-rail-offset, 0px);
198
+ }
199
+ }
189
200
  .nav {
190
201
  border-right: 0;
191
202
  background: var(--sd-surface);
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));
203
+ padding: 14px 12px 12px;
196
204
  }
197
- .nav a {
198
- display: block;
199
- padding: 6px 10px;
205
+ .nav a,
206
+ .nav-more {
207
+ display: flex;
208
+ align-items: center;
209
+ box-sizing: border-box;
210
+ min-height: 28px;
211
+ padding: 4px 10px;
200
212
  border-radius: 8px;
201
213
  color: var(--sd-text-muted);
202
214
  font-size: 13.5px;
203
215
  text-decoration: none;
204
216
  }
205
- .nav a:hover {
217
+ .nav-more {
218
+ width: 100%;
219
+ background: none;
220
+ border: 0;
221
+ font-family: inherit;
222
+ cursor: pointer;
223
+ text-align: left;
224
+ }
225
+ .nav a:hover,
226
+ .nav-more:hover {
206
227
  background: var(--sd-surface-hover);
207
228
  color: var(--sd-text);
208
229
  }
@@ -212,13 +233,26 @@ body {
212
233
  font-weight: 600;
213
234
  }
214
235
  .grp {
215
- margin: 16px 0 4px;
236
+ margin: 3px 0 2px;
216
237
  padding: 0 10px;
217
238
  font-size: 10.5px;
218
239
  letter-spacing: 0.09em;
219
240
  text-transform: uppercase;
220
241
  color: var(--sd-text-muted);
221
242
  font-weight: 700;
243
+ display: block;
244
+ width: 100%;
245
+ text-align: left;
246
+ font-family: inherit;
247
+ background: none;
248
+ border: 0;
249
+ cursor: pointer;
250
+ min-height: 28px;
251
+ }
252
+ .grp:focus-visible,
253
+ .nav-more:focus-visible {
254
+ outline: 2px solid var(--sd-focus-ring);
255
+ outline-offset: 2px;
222
256
  }
223
257
 
224
258
  .main {
@@ -618,6 +652,9 @@ code.type {
618
652
  .nav-toggle {
619
653
  display: inline-block;
620
654
  }
655
+ .nav-track {
656
+ display: contents;
657
+ }
621
658
  .search {
622
659
  order: 10;
623
660
  flex: 1 1 100%;
package/dist/toc.d.ts CHANGED
@@ -6,12 +6,6 @@ export declare function hashPath(hash: string): string;
6
6
  /** The `?story=` deep-link target of a hash, if any. */
7
7
  export declare function storyFromHash(hash: string): string | null;
8
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
9
  export declare function useRailFollow(ref: RefObject<HTMLElement | null>, ready?: boolean): void;
16
10
  /** The "On this page" rail and the `?story=` deep-link landing. */
17
11
  export declare function PageRail({ pageKey }: {
@@ -609,58 +609,110 @@ export const cleanup = () => {};
609
609
  `;
610
610
 
611
611
  // src/build/story-sources.ts
612
+ import ts3 from "typescript";
613
+
614
+ // src/sections.ts
615
+ var STORY_EXCERPT = "// Excerpt from the story";
616
+
617
+ // src/build/snippet-bindings.ts
612
618
  import ts2 from "typescript";
619
+ var options = { target: ts2.ScriptTarget.Latest, jsx: ts2.JsxEmit.Preserve, noLib: true };
620
+ var UNRESOLVED_NAME_DIAGNOSTICS = /* @__PURE__ */ new Set([2304, 2552, 2580, 2581, 2582, 2591, 2592, 18004]);
621
+ function snippetProgram(file) {
622
+ const host = ts2.createCompilerHost(options);
623
+ host.getSourceFile = (name) => name === file.fileName ? file : void 0;
624
+ return ts2.createProgram([file.fileName], options, host);
625
+ }
626
+ var globals;
627
+ function globalNames() {
628
+ if (!globals) {
629
+ const host = ts2.createCompilerHost({ target: ts2.ScriptTarget.Latest });
630
+ const lib = host.getDefaultLibFileName({ target: ts2.ScriptTarget.Latest });
631
+ const libraries = ts2.createProgram([lib], { target: ts2.ScriptTarget.Latest });
632
+ const file = libraries.getSourceFile(lib);
633
+ globals = new Set(libraries.getTypeChecker().getSymbolsInScope(file, ts2.SymbolFlags.Value).map((symbol) => symbol.name));
634
+ }
635
+ return globals;
636
+ }
637
+ function unresolvedSnippetBindings(source, localTags = /* @__PURE__ */ new Set()) {
638
+ const file = ts2.createSourceFile("snippet.tsx", `declare const args: any;
639
+ ${source}`, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
640
+ const program = snippetProgram(file);
641
+ const expression = file.statements.find(ts2.isExpressionStatement)?.expression;
642
+ const rootTag = expression && (ts2.isJsxElement(expression) ? expression.openingElement.tagName : ts2.isJsxSelfClosingElement(expression) ? expression.tagName : void 0);
643
+ const publicRoot = rootTag?.getText(file).split(".")[0];
644
+ const identifiers = /* @__PURE__ */ new Map();
645
+ const visit = (node) => {
646
+ if (ts2.isIdentifier(node)) identifiers.set(node.getStart(file), node);
647
+ ts2.forEachChild(node, visit);
648
+ };
649
+ visit(file);
650
+ const unresolved = /* @__PURE__ */ new Set();
651
+ for (const diagnostic of program.getSemanticDiagnostics(file)) {
652
+ if (!UNRESOLVED_NAME_DIAGNOSTICS.has(diagnostic.code) || diagnostic.start === void 0) continue;
653
+ const node = identifiers.get(diagnostic.start);
654
+ if (!node) continue;
655
+ let tag = node;
656
+ while (ts2.isPropertyAccessExpression(tag.parent) && tag.parent.expression === tag) tag = tag.parent;
657
+ const jsxTag = (ts2.isJsxOpeningElement(tag.parent) || ts2.isJsxClosingElement(tag.parent) || ts2.isJsxSelfClosingElement(tag.parent)) && tag.parent.tagName === tag;
658
+ if (!(jsxTag && node.text === publicRoot && !localTags.has(node.text)) && !globalNames().has(node.text)) unresolved.add(node.text);
659
+ }
660
+ return [...unresolved].sort();
661
+ }
662
+
663
+ // src/build/story-sources.ts
613
664
  function unwrap(node) {
614
- while (ts2.isParenthesizedExpression(node) || ts2.isAsExpression(node) || ts2.isSatisfiesExpression(node) || ts2.isTypeAssertionExpression(node)) node = node.expression;
665
+ while (ts3.isParenthesizedExpression(node) || ts3.isAsExpression(node) || ts3.isSatisfiesExpression(node) || ts3.isTypeAssertionExpression(node)) node = node.expression;
615
666
  return node;
616
667
  }
617
668
  var MAX_HELPER_DEPTH = 3;
618
669
  function extractStorySourceDetails(source) {
619
- const file = ts2.createSourceFile("stories.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
670
+ const file = ts3.createSourceFile("stories.tsx", source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
671
+ const checker = snippetProgram(file).getTypeChecker();
620
672
  const declarations = /* @__PURE__ */ new Map();
621
673
  const exports = /* @__PURE__ */ new Map();
622
674
  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)) {
675
+ const exported = ts3.canHaveModifiers(statement) && ts3.getModifiers(statement)?.some((modifier) => modifier.kind === ts3.SyntaxKind.ExportKeyword);
676
+ if (ts3.isVariableStatement(statement)) {
625
677
  for (const declaration of statement.declarationList.declarations) {
626
- if (!ts2.isIdentifier(declaration.name) || !declaration.initializer) continue;
678
+ if (!ts3.isIdentifier(declaration.name) || !declaration.initializer) continue;
627
679
  declarations.set(declaration.name.text, declaration.initializer);
628
680
  if (exported) exports.set(declaration.name.text, declaration.name.text);
629
681
  }
630
- } else if (ts2.isFunctionDeclaration(statement) && statement.name) {
682
+ } else if (ts3.isFunctionDeclaration(statement) && statement.name) {
631
683
  declarations.set(statement.name.text, statement);
632
684
  if (exported) exports.set(statement.name.text, statement.name.text);
633
- } else if (ts2.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts2.isNamedExports(statement.exportClause)) {
685
+ } else if (ts3.isExportDeclaration(statement) && !statement.moduleSpecifier && statement.exportClause && ts3.isNamedExports(statement.exportClause)) {
634
686
  for (const element of statement.exportClause.elements) {
635
687
  exports.set(element.name.text, (element.propertyName ?? element.name).text);
636
688
  }
637
689
  }
638
690
  }
639
691
  const renderFor = (node, seen = /* @__PURE__ */ new Set()) => {
640
- if (ts2.isFunctionDeclaration(node)) return node;
692
+ if (ts3.isFunctionDeclaration(node)) return node;
641
693
  node = unwrap(node);
642
- if (ts2.isIdentifier(node)) {
694
+ if (ts3.isIdentifier(node)) {
643
695
  if (seen.has(node.text)) return;
644
696
  seen.add(node.text);
645
697
  const declaration = declarations.get(node.text);
646
698
  return declaration ? renderFor(declaration, seen) : void 0;
647
699
  }
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;
700
+ if (ts3.isPropertyAccessExpression(node) && node.name.text === "render" || ts3.isElementAccessExpression(node) && ts3.isStringLiteral(node.argumentExpression) && node.argumentExpression.text === "render") return renderFor(node.expression, seen);
701
+ if (ts3.isArrowFunction(node) || ts3.isFunctionExpression(node)) return node;
702
+ if (!ts3.isObjectLiteralExpression(node)) return;
651
703
  for (const property of [...node.properties].reverse()) {
652
- if (ts2.isSpreadAssignment(property)) {
704
+ if (ts3.isSpreadAssignment(property)) {
653
705
  const render = renderFor(property.expression, new Set(seen));
654
706
  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;
707
+ } else if (property.name && (ts3.isIdentifier(property.name) || ts3.isStringLiteral(property.name)) && property.name.text === "render") {
708
+ if (ts3.isMethodDeclaration(property)) return property;
709
+ if (ts3.isPropertyAssignment(property)) return renderFor(property.initializer, seen) ?? null;
710
+ if (ts3.isShorthandPropertyAssignment(property)) return renderFor(property.name, seen) ?? null;
659
711
  return null;
660
712
  }
661
713
  }
662
714
  };
663
- const hasJsx = (node) => ts2.isJsxElement(node) || ts2.isJsxSelfClosingElement(node) || ts2.isJsxFragment(node) || (ts2.forEachChild(node, hasJsx) ?? false);
715
+ const hasJsx = (node) => ts3.isJsxElement(node) || ts3.isJsxSelfClosingElement(node) || ts3.isJsxFragment(node) || (ts3.forEachChild(node, hasJsx) ?? false);
664
716
  const returnsCache = /* @__PURE__ */ new Map();
665
717
  const returnCounts = /* @__PURE__ */ new Map();
666
718
  const returnedJsx = (render) => {
@@ -669,17 +721,17 @@ function extractStorySourceDetails(source) {
669
721
  const expressions = [];
670
722
  if (!render.body) return expressions;
671
723
  let returnCount = 0;
672
- if (ts2.isBlock(render.body)) {
724
+ if (ts3.isBlock(render.body)) {
673
725
  const visit = (node) => {
674
- if (ts2.isFunctionLike(node)) return;
675
- if (ts2.isReturnStatement(node)) {
726
+ if (ts3.isFunctionLike(node)) return;
727
+ if (ts3.isReturnStatement(node)) {
676
728
  returnCount += 1;
677
729
  if (node.expression && hasJsx(node.expression)) expressions.push(unwrap(node.expression));
678
730
  return;
679
731
  }
680
- ts2.forEachChild(node, visit);
732
+ ts3.forEachChild(node, visit);
681
733
  };
682
- ts2.forEachChild(render.body, visit);
734
+ ts3.forEachChild(render.body, visit);
683
735
  } else {
684
736
  returnCount = 1;
685
737
  if (hasJsx(render.body)) expressions.push(unwrap(render.body));
@@ -693,16 +745,16 @@ function extractStorySourceDetails(source) {
693
745
  seen.add(name);
694
746
  const declaration = declarations.get(name);
695
747
  if (!declaration) return;
696
- if (ts2.isFunctionDeclaration(declaration)) return declaration;
748
+ if (ts3.isFunctionDeclaration(declaration)) return declaration;
697
749
  const expression = unwrap(declaration);
698
- if (ts2.isIdentifier(expression)) return helperFor(expression.text, seen);
699
- if (ts2.isArrowFunction(expression) || ts2.isFunctionExpression(expression)) return expression;
750
+ if (ts3.isIdentifier(expression)) return helperFor(expression.text, seen);
751
+ if (ts3.isArrowFunction(expression) || ts3.isFunctionExpression(expression)) return expression;
700
752
  };
701
- const bindingContains = (binding, name) => ts2.isIdentifier(binding) ? binding.text === name : binding.elements.some((element) => ts2.isBindingElement(element) && bindingContains(element.name, name));
753
+ const bindingContains = (binding, name) => ts3.isIdentifier(binding) ? binding.text === name : binding.elements.some((element) => ts3.isBindingElement(element) && bindingContains(element.name, name));
702
754
  const isShadowed = (expression, name) => {
703
755
  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;
756
+ if (ts3.isFunctionLike(node) && node.parameters.some((parameter) => bindingContains(parameter.name, name))) return true;
757
+ if (ts3.isBlock(node) && node.statements.some((statement) => ts3.isVariableStatement(statement) && statement.declarationList.declarations.some((declaration) => bindingContains(declaration.name, name)) || ts3.isFunctionDeclaration(statement) && statement.name?.text === name)) return true;
706
758
  }
707
759
  return false;
708
760
  };
@@ -712,24 +764,24 @@ function extractStorySourceDetails(source) {
712
764
  if (cached !== void 0) return cached;
713
765
  const bindings = /* @__PURE__ */ new Map();
714
766
  for (const [name, declaration] of declarations) {
715
- bindings.set(name, ts2.isFunctionDeclaration(declaration) ? void 0 : declaration);
767
+ bindings.set(name, ts3.isFunctionDeclaration(declaration) ? void 0 : declaration);
716
768
  }
717
769
  const bind = (scope2, name, initializer) => {
718
- if (ts2.isIdentifier(name)) scope2.set(name.text, initializer);
770
+ if (ts3.isIdentifier(name)) scope2.set(name.text, initializer);
719
771
  else for (const element of name.elements) {
720
- if (ts2.isBindingElement(element)) bind(scope2, element.name, element.initializer);
772
+ if (ts3.isBindingElement(element)) bind(scope2, element.name, element.initializer);
721
773
  }
722
774
  };
723
775
  const addScope = (node, parent) => {
724
776
  const scope2 = new Map(parent);
725
- if (ts2.isFunctionLike(node)) {
777
+ if (ts3.isFunctionLike(node)) {
726
778
  for (const parameter of node.parameters) bind(scope2, parameter.name, parameter.initializer);
727
779
  }
728
- if (ts2.isBlock(node)) {
780
+ if (ts3.isBlock(node)) {
729
781
  for (const statement of node.statements) {
730
- if (ts2.isVariableStatement(statement)) {
782
+ if (ts3.isVariableStatement(statement)) {
731
783
  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);
784
+ } else if (ts3.isFunctionDeclaration(statement) && statement.name) scope2.set(statement.name.text, void 0);
733
785
  }
734
786
  }
735
787
  return scope2;
@@ -740,55 +792,84 @@ function extractStorySourceDetails(source) {
740
792
  for (const node of ancestors.reverse()) scope = addScope(node, scope);
741
793
  const knownCollection = (expression, bindings2, seen = /* @__PURE__ */ new Set()) => {
742
794
  expression = unwrap(expression);
743
- if (ts2.isArrayLiteralExpression(expression)) {
744
- return expression.elements.every((element) => !ts2.isSpreadElement(element) || knownCollection(element.expression, bindings2, new Set(seen)));
795
+ if (ts3.isArrayLiteralExpression(expression)) {
796
+ return expression.elements.every((element) => !ts3.isSpreadElement(element) || knownCollection(element.expression, bindings2, new Set(seen)));
745
797
  }
746
- if (ts2.isIdentifier(expression)) {
798
+ if (ts3.isIdentifier(expression)) {
747
799
  if (seen.has(expression.text)) return false;
748
800
  seen.add(expression.text);
749
801
  const initializer = bindings2.get(expression.text);
750
802
  return Boolean(initializer && knownCollection(initializer, bindings2, seen));
751
803
  }
752
- if (ts2.isCallExpression(expression) && ts2.isPropertyAccessExpression(expression.expression) && ["slice", "filter", "toSorted", "toReversed"].includes(expression.expression.name.text)) {
804
+ if (ts3.isCallExpression(expression) && ts3.isPropertyAccessExpression(expression.expression) && ["slice", "filter", "toSorted", "toReversed"].includes(expression.expression.name.text)) {
753
805
  return knownCollection(expression.expression.expression, bindings2, seen);
754
806
  }
755
807
  return false;
756
808
  };
757
809
  const visit = (node, bindings2) => {
758
- if (ts2.isFunctionLike(node) || ts2.isBlock(node)) bindings2 = addScope(node, bindings2);
759
- if (ts2.isCallExpression(node)) {
810
+ if (ts3.isFunctionLike(node) || ts3.isBlock(node)) bindings2 = addScope(node, bindings2);
811
+ if (ts3.isCallExpression(node)) {
760
812
  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";
813
+ const map = ts3.isPropertyAccessExpression(target) && target.name.text === "map" || ts3.isElementAccessExpression(target) && ts3.isStringLiteral(target.argumentExpression) && target.argumentExpression.text === "map";
762
814
  if (map && !knownCollection(target.expression, bindings2)) return true;
763
815
  }
764
- return ts2.forEachChild(node, (child) => visit(child, bindings2)) ?? false;
816
+ return ts3.forEachChild(node, (child) => visit(child, bindings2)) ?? false;
765
817
  };
766
818
  const unknown = visit(jsx, scope);
767
819
  mapCache.set(jsx, unknown);
768
820
  return unknown;
769
821
  };
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;
822
+ const textWithBindings = (expression, bindings) => {
823
+ const edits = [];
824
+ const visit = (node) => {
825
+ if (ts3.isIdentifier(node)) {
826
+ const symbol = checker.getSymbolAtLocation(node);
827
+ const text2 = symbol && bindings.get(symbol);
828
+ const parent = node.parent;
829
+ const tag = (ts3.isJsxOpeningElement(parent) || ts3.isJsxClosingElement(parent) || ts3.isJsxSelfClosingElement(parent)) && parent.tagName === node;
830
+ if (text2 && !tag) edits.push({
831
+ start: node.getStart(file) - expression.getStart(file),
832
+ end: node.end - expression.getStart(file),
833
+ text: ts3.isShorthandPropertyAssignment(parent) ? `${node.text}: ${text2}` : text2
834
+ });
835
+ }
836
+ ts3.forEachChild(node, visit);
837
+ };
838
+ visit(expression);
839
+ let text = expression.getText(file);
840
+ for (const edit of edits.reverse()) text = text.slice(0, edit.start) + edit.text + text.slice(edit.end);
841
+ return text;
842
+ };
843
+ const inlineRoot = (expression, detail, trail = [], bindings = /* @__PURE__ */ new Map()) => {
844
+ const tag = ts3.isJsxElement(expression) ? expression.openingElement.tagName : ts3.isJsxSelfClosingElement(expression) ? expression.tagName : void 0;
845
+ const name = tag && ts3.isIdentifier(tag) && /^[A-Z]/.test(tag.text) ? tag.text : void 0;
773
846
  const helper = name && helperFor(name);
774
- if (!name || !declarations.has(name)) return expression.getText(file);
847
+ if (!name || !declarations.has(name)) return textWithBindings(expression, bindings);
775
848
  if (!helper) {
776
849
  const reason2 = "local definition is not a direct function";
777
850
  detail.remaining.push({ name, reason: reason2 });
778
851
  return `// ${name} not expanded: ${reason2}.
779
- ${expression.getText(file)}`;
852
+ ${textWithBindings(expression, bindings)}`;
780
853
  }
781
854
  const returned = returnedJsx(helper);
782
855
  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;
856
+ 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 || !(ts3.isJsxElement(jsx) || ts3.isJsxSelfClosingElement(jsx) || ts3.isJsxFragment(jsx)) ? "no single literal JSX return" : hasUnknownMap(helper, jsx) ? "maps over a collection without a local array definition" : void 0;
784
857
  if (reason) {
785
858
  detail.remaining.push({ name, reason });
786
859
  return `// ${name} not expanded: ${reason}.
787
- ${expression.getText(file)}`;
860
+ ${textWithBindings(expression, bindings)}`;
788
861
  }
789
862
  detail.inlined.push(name);
863
+ const attributes = ts3.isJsxElement(expression) ? expression.openingElement.attributes : ts3.isJsxSelfClosingElement(expression) ? expression.attributes : void 0;
864
+ const parameter = helper.parameters[0]?.name;
865
+ const rootAttributes = ts3.isJsxElement(jsx) ? jsx.openingElement.attributes : ts3.isJsxSelfClosingElement(jsx) ? jsx.attributes : void 0;
866
+ const spread = attributes?.properties.length === 1 ? attributes.properties[0] : void 0;
867
+ if (parameter && ts3.isIdentifier(parameter) && spread && ts3.isJsxSpreadAttribute(spread) && textWithBindings(spread.expression, bindings) === "args" && rootAttributes?.properties.some((prop) => ts3.isJsxSpreadAttribute(prop) && ts3.isIdentifier(prop.expression) && prop.expression.text === parameter.text)) {
868
+ const symbol = checker.getSymbolAtLocation(parameter);
869
+ if (symbol) bindings = new Map(bindings).set(symbol, "args");
870
+ }
790
871
  return `// JSX from ${name}, as authored.
791
- ${inlineRoot(jsx, detail, [...trail, name])}`;
872
+ ${inlineRoot(jsx, detail, [...trail, name], bindings)}`;
792
873
  };
793
874
  const sources = {};
794
875
  const helpers = {};
@@ -799,8 +880,16 @@ ${inlineRoot(jsx, detail, [...trail, name])}`;
799
880
  const expressions = returnedJsx(render);
800
881
  if (expressions.length) {
801
882
  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.
883
+ const bindings = /* @__PURE__ */ new Map();
884
+ const parameter = render.parameters[0]?.name;
885
+ if (parameter && ts3.isIdentifier(parameter)) {
886
+ const symbol = checker.getSymbolAtLocation(parameter);
887
+ if (symbol) bindings.set(symbol, "args");
888
+ }
889
+ const jsx = expressions.map((expression) => inlineRoot(expression, detail, [], bindings)).join("\n\n");
890
+ const unresolved = unresolvedSnippetBindings(jsx, new Set(declarations.keys()));
891
+ const incomplete = detail.remaining.length || unresolved.length || expressions.length > 1;
892
+ sources[name] = incomplete ? `${STORY_EXCERPT}${unresolved.length ? `; unresolved bindings: ${unresolved.join(", ")}` : "; multiple return branches"}.
804
893
  ${jsx}` : jsx;
805
894
  if (detail.inlined.length || detail.remaining.length) helpers[name] = detail;
806
895
  }
@@ -825,9 +914,9 @@ function storySourcesCode(sources) {
825
914
  }
826
915
 
827
916
  // src/vite-plugin.ts
828
- function storybookDocs(options) {
829
- const { config } = options;
830
- let projectRoot = options.root ?? process.cwd();
917
+ function storybookDocs(options2) {
918
+ const { config } = options2;
919
+ let projectRoot = options2.root ?? process.cwd();
831
920
  let catalog = buildCatalog(scanStories(config.stories, projectRoot), config.groups);
832
921
  catalog.files.clear();
833
922
  const engineStyles = path7.join(import.meta.dirname, "styles.css");
@@ -874,7 +963,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
874
963
  },
875
964
  configResolved(resolved) {
876
965
  propsCollector.setBuildMode(resolved.command === "build");
877
- projectRoot = options.root ?? resolved.root;
966
+ projectRoot = options2.root ?? resolved.root;
878
967
  catalog = buildCatalog(scanStories(config.stories, projectRoot), config.groups);
879
968
  guidanceFiles = indexGuidance(config.guidance, projectRoot);
880
969
  this.info?.(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfrader/storybook-docs",
3
- "version": "0.5.0",
3
+ "version": "0.7.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": {