@jfrader/storybook-docs 0.9.2 → 0.11.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.
@@ -1,7 +1,11 @@
1
1
  import type { ComponentPropsDoc, PropDoc } from '../prop-docs.js';
2
2
  export declare function toPropDoc(prop: unknown): PropDoc;
3
3
  export declare function createPropsCollector(): {
4
- collect: (storyFile: string | undefined, entryName: string, slug: string) => ComponentPropsDoc[];
4
+ collect: (storyFile: string | undefined, entryName: string, slug: string, bases?: {
5
+ name: string;
6
+ slug: string;
7
+ storyFile: string;
8
+ }[]) => ComponentPropsDoc[];
5
9
  setBuildMode(value: boolean): void;
6
10
  clearPrograms(): void;
7
11
  };
@@ -7,10 +7,9 @@ export interface ComponentDemoProps {
7
7
  story: string;
8
8
  storyName: string;
9
9
  authoredStory?: string;
10
- skipped: string[];
10
+ notice?: string | null;
11
11
  controls: StoryControl[];
12
12
  args: Record<string, unknown>;
13
- unavailable: string | null;
14
13
  changed: boolean;
15
14
  onControlChange: (name: string, value: unknown) => void;
16
15
  onReset: () => void;
@@ -20,4 +19,4 @@ export interface ComponentDemoProps {
20
19
  children: ReactNode;
21
20
  }
22
21
  export declare function hasHorizontalOverflow({ scrollWidth, clientWidth }: Pick<HTMLElement, 'scrollWidth' | 'clientWidth'>): boolean;
23
- export declare function ComponentDemo({ story, storyName, authoredStory, skipped, controls, args, unavailable, changed, onControlChange, onReset, storyLayout, snippet, codeLanguage, children, }: ComponentDemoProps): import("react").JSX.Element;
22
+ export declare function ComponentDemo({ story, storyName, authoredStory, notice, controls, args, changed, onControlChange, onReset, storyLayout, snippet, codeLanguage, children, }: ComponentDemoProps): import("react").JSX.Element;
@@ -38,8 +38,8 @@ export declare function getStoryControls(meta: StoryShape, story: StoryShape, co
38
38
  [x: string]: unknown;
39
39
  };
40
40
  controls: StoryControl[];
41
- unavailable: string | null;
42
41
  };
42
+ export declare function skippedNotice(meta: StoryShape, definition: StoryShape): string | null;
43
43
  export declare function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }: {
44
44
  module: Record<string, unknown>;
45
45
  story: string;
package/dist/index.js CHANGED
@@ -13,6 +13,15 @@ import { pages, site as site2, theme } from "virtual:storybook-docs/site";
13
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
+ // src/base.ts
17
+ var STORYBOOK_DOCS_BASE = (() => {
18
+ const globalBase = globalThis.__STORYBOOK_DOCS_BASE__;
19
+ if (globalBase) return globalBase.replace(/\/$/, "");
20
+ const { pathname } = window.location;
21
+ return pathname === "/" || pathname.endsWith(".html") ? "" : pathname.replace(/\/$/, "");
22
+ })();
23
+ var hrefFor = (path) => `${STORYBOOK_DOCS_BASE}${path}`;
24
+
16
25
  // src/guidance.tsx
17
26
  import { createElement, useEffect, useMemo as useMemo2, useState } from "react";
18
27
  import { loadGuidance } from "virtual:storybook-docs/guidance";
@@ -923,10 +932,9 @@ function ComponentDemo({
923
932
  story,
924
933
  storyName,
925
934
  authoredStory,
926
- skipped,
935
+ notice,
927
936
  controls,
928
937
  args,
929
- unavailable,
930
938
  changed,
931
939
  onControlChange,
932
940
  onReset,
@@ -977,12 +985,8 @@ function ComponentDemo({
977
985
  /* @__PURE__ */ jsx6("span", { "data-toc-slot": "", children: /* @__PURE__ */ jsx6(CopyLink, { name: storyName, story }) })
978
986
  ] }),
979
987
  authoredStory ? /* @__PURE__ */ jsx6("p", { className: "story-blurb", children: authoredStory }) : null,
980
- skipped.length ? /* @__PURE__ */ jsxs4("p", { className: "sd-controls-note", children: [
981
- "Not run here: ",
982
- skipped.join(", "),
983
- ". See it in Storybook."
984
- ] }) : null,
985
- unavailable ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: unavailable }) : /* @__PURE__ */ jsxs4("div", { className: "sd-controls", role: "group", "aria-labelledby": controlsLabelId, children: [
988
+ notice ? /* @__PURE__ */ jsx6("p", { className: "sd-controls-note", children: notice }) : null,
989
+ controls.length ? /* @__PURE__ */ jsxs4("div", { className: "sd-controls", role: "group", "aria-labelledby": controlsLabelId, children: [
986
990
  /* @__PURE__ */ jsxs4("p", { className: "sd-controls-label", id: controlsLabelId, children: [
987
991
  "Controls for ",
988
992
  storyName
@@ -999,7 +1003,7 @@ function ComponentDemo({
999
1003
  )),
1000
1004
  /* @__PURE__ */ jsx6("button", { className: "sd-controls-reset", type: "button", disabled: !changed, onClick: onReset, children: "Reset" })
1001
1005
  ] })
1002
- ] }),
1006
+ ] }) : null,
1003
1007
  /* @__PURE__ */ jsx6(
1004
1008
  "div",
1005
1009
  {
@@ -1068,14 +1072,27 @@ function getStoryControls(meta, story, componentDefaults = {}) {
1068
1072
  }
1069
1073
  return {
1070
1074
  args,
1071
- controls,
1072
- unavailable: controls.length === 0 ? "No controls." : null
1075
+ controls
1073
1076
  };
1074
1077
  }
1078
+ function skippedNotice(meta, definition) {
1079
+ const hasSetup = Boolean(meta.loaders || definition.loaders || meta.beforeEach || definition.beforeEach);
1080
+ const hasViewport = Boolean(definition.parameters?.viewport ?? meta.parameters?.viewport);
1081
+ if (hasSetup && hasViewport) {
1082
+ return "Setup not run here and shown at this page\u2019s width. Open it in Storybook.";
1083
+ }
1084
+ if (hasSetup) {
1085
+ return "Setup not run here. Open it in Storybook.";
1086
+ }
1087
+ if (hasViewport) {
1088
+ return "Shown at this page\u2019s width. Open it in Storybook.";
1089
+ }
1090
+ return null;
1091
+ }
1075
1092
  function ControlledStoryFrame({ module, story, componentName, componentDefaults, renderSource }) {
1076
1093
  const meta = module.default ?? {};
1077
1094
  const definition = module[story] ?? {};
1078
- const { args: defaults, controls, unavailable } = getStoryControls(meta, definition, componentDefaults);
1095
+ const { args: defaults, controls } = getStoryControls(meta, definition, componentDefaults);
1079
1096
  const [edits, setEdits] = useState5({});
1080
1097
  const args = { ...defaults, ...edits };
1081
1098
  const changed = Object.entries(edits).some(([name, value]) => !Object.is(value, defaults[name]));
@@ -1086,8 +1103,7 @@ function ControlledStoryFrame({ module, story, componentName, componentDefaults,
1086
1103
  const authoredCode = frameSource(meta, definition);
1087
1104
  const authoredStory = docsOverride?.description?.story ?? docsMeta?.description?.story;
1088
1105
  const storyName = definition.name ?? story;
1089
- const skipped = ["loaders", "beforeEach"].filter((key) => meta[key] || definition[key]);
1090
- if (definition.parameters?.viewport ?? meta.parameters?.viewport) skipped.push("viewport");
1106
+ const notice = skippedNotice(meta, definition);
1091
1107
  const codeLanguage = authoredCode?.trim() ? docsOverride?.source?.language ?? docsMeta?.source?.language ?? "tsx" : "tsx";
1092
1108
  const snippet = buildUsageSnippet({
1093
1109
  componentName,
@@ -1106,10 +1122,9 @@ function ControlledStoryFrame({ module, story, componentName, componentDefaults,
1106
1122
  story,
1107
1123
  storyName,
1108
1124
  authoredStory,
1109
- skipped,
1125
+ notice,
1110
1126
  controls,
1111
1127
  args,
1112
- unavailable,
1113
1128
  changed,
1114
1129
  storyLayout,
1115
1130
  snippet,
@@ -1156,7 +1171,7 @@ function formatValue(value) {
1156
1171
  return String(value);
1157
1172
  }
1158
1173
  function TableFrame({ label, children }) {
1159
- return /* @__PURE__ */ jsx8("div", { className: "table-scroll", role: "region", "aria-label": label, tabIndex: 0, children: /* @__PURE__ */ jsxs5("table", { children: [
1174
+ return /* @__PURE__ */ jsx8("div", { className: "table-scroll", role: "region", "aria-label": label, tabIndex: 0, children: /* @__PURE__ */ jsxs5("table", { className: "props-table", children: [
1160
1175
  /* @__PURE__ */ jsx8("caption", { className: "sr-only", children: label }),
1161
1176
  children
1162
1177
  ] }) });
@@ -1175,28 +1190,67 @@ function PropNotes({ prop }) {
1175
1190
  !prop.description && !prop.tags?.length && !prop.inheritedFrom ? "\u2014" : null
1176
1191
  ] });
1177
1192
  }
1193
+ var SHAPE_PREVIEW_FIELDS = 3;
1194
+ function PropType({ prop }) {
1195
+ const signature2 = prop.signatures?.[0];
1196
+ const fields = prop.members?.slice(0, SHAPE_PREVIEW_FIELDS).map((member) => `${member.name}${member.required ? "" : "?"}: \u2026`).join("; ");
1197
+ return /* @__PURE__ */ jsxs5(Fragment3, { children: [
1198
+ /* @__PURE__ */ jsx8("code", { className: signature2 ? "type signature" : "type", tabIndex: signature2 ? 0 : void 0, children: prop.typeUnavailable ? "Not resolved" : signature2?.type ?? prop.type }),
1199
+ signature2 ? /* @__PURE__ */ jsxs5("details", { children: [
1200
+ /* @__PURE__ */ jsx8("summary", { children: "Signature details" }),
1201
+ prop.type !== signature2.type ? /* @__PURE__ */ jsx8("code", { className: "type", children: prop.type }) : null,
1202
+ prop.signatures?.map((item, index) => /* @__PURE__ */ jsxs5("div", { children: [
1203
+ /* @__PURE__ */ jsx8("code", { className: "type", children: item.type }),
1204
+ /* @__PURE__ */ jsx8("dl", { children: item.parameters.filter((parameter) => parameter.description).map((parameter) => /* @__PURE__ */ jsxs5("div", { children: [
1205
+ /* @__PURE__ */ jsx8("dt", { children: /* @__PURE__ */ jsxs5("code", { children: [
1206
+ parameter.name,
1207
+ ": ",
1208
+ parameter.type
1209
+ ] }) }),
1210
+ /* @__PURE__ */ jsx8("dd", { children: /* @__PURE__ */ jsx8(MarkdownView, { markdown: parameter.description }) })
1211
+ ] }, parameter.name)) })
1212
+ ] }, index))
1213
+ ] }) : null,
1214
+ prop.members?.length ? /* @__PURE__ */ jsxs5("details", { children: [
1215
+ /* @__PURE__ */ jsxs5("summary", { children: [
1216
+ prop.name === "slotProps" ? "Slots" : "Fields",
1217
+ " ",
1218
+ /* @__PURE__ */ jsx8("code", { children: `{ ${fields}${prop.members.length > SHAPE_PREVIEW_FIELDS ? "; \u2026" : ""} }` })
1219
+ ] }),
1220
+ /* @__PURE__ */ jsx8("dl", { children: prop.members.map((member) => /* @__PURE__ */ jsxs5("div", { children: [
1221
+ /* @__PURE__ */ jsxs5("dt", { children: [
1222
+ /* @__PURE__ */ jsxs5("code", { children: [
1223
+ member.name,
1224
+ member.required ? "" : "?"
1225
+ ] }),
1226
+ member.required ? " (required)" : ""
1227
+ ] }),
1228
+ /* @__PURE__ */ jsxs5("dd", { children: [
1229
+ /* @__PURE__ */ jsx8(PropType, { prop: member }),
1230
+ /* @__PURE__ */ jsx8(PropNotes, { prop: member })
1231
+ ] })
1232
+ ] }, member.name)) })
1233
+ ] }) : null
1234
+ ] });
1235
+ }
1236
+ function InheritedLinks({ component }) {
1237
+ const bases = Array.from(new Map(component.props.flatMap((prop) => prop.inheritedComponent ? [[prop.inheritedComponent.slug, prop.inheritedComponent]] : [])).values());
1238
+ return bases.length ? /* @__PURE__ */ jsxs5("p", { children: [
1239
+ "Also available from ",
1240
+ bases.map((entry, index) => /* @__PURE__ */ jsxs5("span", { children: [
1241
+ index ? ", " : "",
1242
+ /* @__PURE__ */ jsx8("a", { href: `#${hrefFor(`/components/${entry.slug}`)}`, children: entry.name })
1243
+ ] }, entry.slug)),
1244
+ "."
1245
+ ] }) : null;
1246
+ }
1178
1247
  function PropRows({ props }) {
1179
1248
  return props.map((prop) => /* @__PURE__ */ jsxs5("tr", { children: [
1180
1249
  /* @__PURE__ */ jsxs5("th", { scope: "row", children: [
1181
1250
  /* @__PURE__ */ jsx8("code", { children: prop.name }),
1182
1251
  prop.required ? /* @__PURE__ */ jsx8("span", { className: "req", children: "required" }) : null
1183
1252
  ] }),
1184
- /* @__PURE__ */ jsxs5("td", { children: [
1185
- /* @__PURE__ */ jsx8("code", { className: "type", children: prop.typeUnavailable ? "Not resolved" : prop.type }),
1186
- prop.members?.length ? /* @__PURE__ */ jsxs5("details", { children: [
1187
- /* @__PURE__ */ jsx8("summary", { children: prop.name === "slotProps" ? "Slots" : "Fields" }),
1188
- /* @__PURE__ */ jsx8("dl", { children: prop.members.map((member) => /* @__PURE__ */ jsxs5("div", { children: [
1189
- /* @__PURE__ */ jsxs5("dt", { children: [
1190
- /* @__PURE__ */ jsx8("code", { children: member.name }),
1191
- member.required ? " (required)" : ""
1192
- ] }),
1193
- /* @__PURE__ */ jsxs5("dd", { children: [
1194
- /* @__PURE__ */ jsx8("code", { children: member.type }),
1195
- /* @__PURE__ */ jsx8(PropNotes, { prop: member })
1196
- ] })
1197
- ] }, member.name)) })
1198
- ] }) : null
1199
- ] }),
1253
+ /* @__PURE__ */ jsx8("td", { children: /* @__PURE__ */ jsx8(PropType, { prop }) }),
1200
1254
  /* @__PURE__ */ jsx8("td", { children: /* @__PURE__ */ jsx8("code", { children: prop.defaultValue === "undefined" ? "\u2014" : prop.defaultValue ?? "\u2014" }) }),
1201
1255
  /* @__PURE__ */ jsx8("td", { children: /* @__PURE__ */ jsx8(PropNotes, { prop }) })
1202
1256
  ] }, prop.name));
@@ -1231,6 +1285,7 @@ function PropsTable({
1231
1285
  ] }) }),
1232
1286
  /* @__PURE__ */ jsx8("tbody", { children: /* @__PURE__ */ jsx8(PropRows, { props: component.props.filter((prop) => !prop.inheritedFrom) }) })
1233
1287
  ] }) : /* @__PURE__ */ jsx8("p", { children: "No component-specific props." }),
1288
+ /* @__PURE__ */ jsx8(InheritedLinks, { component }),
1234
1289
  component.props.some((prop) => prop.inheritedFrom) ? /* @__PURE__ */ jsxs5("details", { children: [
1235
1290
  /* @__PURE__ */ jsxs5("summary", { children: [
1236
1291
  "Inherited props (",
@@ -1567,17 +1622,6 @@ function TypePage() {
1567
1622
  // src/pages/overview.tsx
1568
1623
  import { catalog, groups, stats } from "virtual:storybook-docs/catalog";
1569
1624
  import { site } from "virtual:storybook-docs/site";
1570
-
1571
- // src/base.ts
1572
- var STORYBOOK_DOCS_BASE = (() => {
1573
- const globalBase = globalThis.__STORYBOOK_DOCS_BASE__;
1574
- if (globalBase) return globalBase.replace(/\/$/, "");
1575
- const { pathname } = window.location;
1576
- return pathname === "/" || pathname.endsWith(".html") ? "" : pathname.replace(/\/$/, "");
1577
- })();
1578
- var hrefFor = (path) => `${STORYBOOK_DOCS_BASE}${path}`;
1579
-
1580
- // src/pages/overview.tsx
1581
1625
  import { Fragment as Fragment8, jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
1582
1626
  function OverviewPage() {
1583
1627
  const startHere = catalog.slice(0, 6);
@@ -6,11 +6,23 @@ export interface PropDoc {
6
6
  defaultValue: string | null;
7
7
  description: string;
8
8
  inheritedFrom?: string;
9
+ inheritedComponent?: {
10
+ name: string;
11
+ slug: string;
12
+ };
9
13
  tags?: {
10
14
  name: string;
11
15
  text: string;
12
16
  }[];
13
17
  members?: PropDoc[];
18
+ signatures?: {
19
+ type: string;
20
+ parameters: {
21
+ name: string;
22
+ type: string;
23
+ description: string;
24
+ }[];
25
+ }[];
14
26
  }
15
27
  export interface ComponentPropsDoc {
16
28
  name: string;
package/dist/styles.css CHANGED
@@ -426,8 +426,30 @@ pre.code {
426
426
  }
427
427
  code.type {
428
428
  white-space: pre-wrap;
429
+ overflow-wrap: anywhere;
430
+ display: inline-block;
431
+ max-inline-size: 100%;
432
+ }
433
+ code.signature {
434
+ display: block;
435
+ white-space: pre;
436
+ overflow-x: auto;
437
+ padding-bottom: 4px;
438
+ }
439
+ .table-scroll > .props-table {
440
+ table-layout: fixed;
441
+ min-inline-size: 640px;
442
+ }
443
+ .props-table thead th:nth-child(1) { width: 18%; }
444
+ .props-table thead th:nth-child(2) { width: 34%; }
445
+ .props-table thead th:nth-child(3) { width: 12%; }
446
+ .props-table thead th:nth-child(4) { width: 36%; }
447
+ .props-table :is(th, td) {
448
+ overflow-wrap: anywhere;
429
449
  }
430
450
  .req {
451
+ display: inline-block;
452
+ white-space: nowrap;
431
453
  margin-left: 6px;
432
454
  font-size: 10.5px;
433
455
  letter-spacing: 0.05em;
@@ -1,5 +1,5 @@
1
1
  // src/vite-plugin.ts
2
- import { readFileSync as readFileSync3, realpathSync } from "node:fs";
2
+ import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
3
3
  import path7 from "node:path";
4
4
 
5
5
  // src/build/catalog.ts
@@ -248,7 +248,7 @@ function globalStylesCode(engineStyles, css = []) {
248
248
  }
249
249
 
250
250
  // src/build/props.ts
251
- import { readdirSync as readdirSync2 } from "node:fs";
251
+ import { readdirSync as readdirSync2, realpathSync } from "node:fs";
252
252
  import path3 from "node:path";
253
253
  import { withCompilerOptions } from "react-docgen-typescript";
254
254
  import ts from "typescript";
@@ -319,7 +319,7 @@ function createPropsCollector() {
319
319
  cache.program = ts.createProgram(sources, compilerOptions, cache.host, cache.program);
320
320
  return cache.program;
321
321
  };
322
- const collect = (storyFile, entryName, slug) => {
322
+ const collect = (storyFile, entryName, slug, bases = []) => {
323
323
  if (storyFile) {
324
324
  const directory = path3.dirname(storyFile);
325
325
  const sources = readdirSync2(directory).filter((file) => /\.tsx?$/.test(file) && !file.endsWith(".stories.tsx")).map((file) => path3.join(directory, file));
@@ -331,7 +331,7 @@ function createPropsCollector() {
331
331
  })).filter((component) => component.props.length > 0);
332
332
  const matchesOwnName = (name) => entryName.length > 0 && name.toLowerCase().startsWith(entryName.toLowerCase());
333
333
  const relevant = parsed.some((component) => matchesOwnName(component.name)) ? parsed.filter((component) => matchesOwnName(component.name)) : parsed;
334
- const typed = collectTypeProps(sources, entryName);
334
+ const typed = collectTypeProps(sources, entryName, bases);
335
335
  return mergePropsTables(typed.length > 0 ? [...relevant.filter((table) => typed.some((item) => item.name === table.name)), ...typed] : relevant);
336
336
  } catch (cause) {
337
337
  console.warn(`storybook-docs: could not read props for "${slug}" (${String(cause)})`);
@@ -340,10 +340,45 @@ function createPropsCollector() {
340
340
  }
341
341
  return [];
342
342
  };
343
- const collectTypeProps = (sources, entryName) => {
343
+ const collectTypeProps = (sources, entryName, bases) => {
344
344
  const program = createPropsProgram(sources, TYPE_PROPS_COMPILER_OPTIONS);
345
345
  const checker = program.getTypeChecker();
346
346
  const tables = [];
347
+ const baseContracts = bases.filter((base) => base.name !== entryName).map((base) => ({
348
+ ...base,
349
+ directory: path3.dirname(realpathSync(base.storyFile)),
350
+ declarations: /* @__PURE__ */ new Set()
351
+ }));
352
+ const readContract = (type, location, declarations, depth = 0) => {
353
+ for (const signature of type.getCallSignatures()) {
354
+ const parameter = signature.getParameters()[0];
355
+ if (!parameter) continue;
356
+ const propsType = checker.getTypeOfSymbolAtLocation(parameter, signature.getDeclaration() ?? location);
357
+ for (const prop of checker.getPropertiesOfType(propsType)) {
358
+ prop.declarations?.forEach((declaration) => declarations.add(declaration));
359
+ }
360
+ }
361
+ if (depth >= 2) return;
362
+ for (const member of checker.getPropertiesOfType(type)) {
363
+ if (["bind", "call", "apply"].includes(member.getName())) continue;
364
+ const memberType = checker.getTypeOfSymbolAtLocation(member, member.valueDeclaration ?? location);
365
+ if (memberType.getCallSignatures().length) readContract(memberType, location, declarations, depth + 1);
366
+ }
367
+ };
368
+ for (const source of program.getSourceFiles()) {
369
+ if (isExternal(source.fileName)) continue;
370
+ const directory = path3.dirname(realpathSync(source.fileName));
371
+ const candidates = baseContracts.filter((base) => base.directory === directory);
372
+ if (!candidates.length) continue;
373
+ const module = checker.getSymbolAtLocation(source);
374
+ if (!module) continue;
375
+ for (const symbol of checker.getExportsOfModule(module)) {
376
+ for (const base of candidates.filter((candidate) => candidate.name === symbol.getName())) {
377
+ const target = symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
378
+ readContract(checker.getTypeOfSymbolAtLocation(target, target.valueDeclaration ?? source), source, base.declarations);
379
+ }
380
+ }
381
+ }
347
382
  const unresolved = /* @__PURE__ */ Symbol("unresolved");
348
383
  const literalValue = (node, seen = /* @__PURE__ */ new Set()) => {
349
384
  if (seen.has(node)) return unresolved;
@@ -391,9 +426,26 @@ function createPropsCollector() {
391
426
  const printedType = checker.typeToString(propType, void 0, typeFlags);
392
427
  const displayType = literalUnion && propType.isUnion() ? propType.types.filter((part) => !optional || !(part.flags & ts.TypeFlags.Undefined)).map((part) => checker.typeToString(part, void 0, typeFlags)).join(" | ").replace(/false \| true/g, "boolean") : optional ? printedType.replace(/ \| undefined$/, "") : printedType;
393
428
  const parent = declaration?.parent;
394
- const inheritedFrom = declaration && (isExternal(declaration.getSourceFile().fileName) || !sources.includes(declaration.getSourceFile().fileName)) ? parent && (ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)) ? parent.name.text : path3.basename(declaration.getSourceFile().fileName) : void 0;
429
+ const declarationName = declaration ? parent && (ts.isInterfaceDeclaration(parent) || ts.isTypeAliasDeclaration(parent)) ? parent.name.text : path3.basename(declaration.getSourceFile().fileName) : void 0;
430
+ const inheritedComponent = declaration ? baseContracts.find(
431
+ (base) => base.declarations.has(declaration) && base.directory === path3.dirname(realpathSync(declaration.getSourceFile().fileName))
432
+ ) : void 0;
433
+ const inheritedFrom = declaration && (inheritedComponent || isExternal(declaration.getSourceFile().fileName) || !sources.includes(declaration.getSourceFile().fileName)) ? declarationName : void 0;
395
434
  const memberType = checker.getNonNullableType(propType);
396
- const hasLocalMembers = !checker.isArrayType(memberType) && !checker.isTupleType(memberType) && !memberType.getCallSignatures().length && checker.getPropertiesOfType(memberType).some((member) => member.declarations?.some((node) => !isExternal(node.getSourceFile().fileName)));
435
+ const nonCallableAlternatives = propType.isUnion() ? propType.types.filter((part) => Boolean(part.flags & (ts.TypeFlags.Null | ts.TypeFlags.Undefined)) && (!optional || !(part.flags & ts.TypeFlags.Undefined))).map((part) => checker.typeToString(part, void 0, typeFlags)) : [];
436
+ const signatures = memberType.getCallSignatures().map((signature) => {
437
+ const signatureType = checker.signatureToString(signature, location, typeFlags | ts.TypeFormatFlags.WriteArrowStyleSignature);
438
+ return {
439
+ type: nonCallableAlternatives.length ? `(${signatureType}) | ${nonCallableAlternatives.join(" | ")}` : signatureType,
440
+ parameters: signature.getParameters().map((parameter) => ({
441
+ name: parameter.getName(),
442
+ type: checker.typeToString(checker.getTypeOfSymbolAtLocation(parameter, parameter.valueDeclaration ?? location), location, typeFlags),
443
+ description: ts.displayPartsToString(parameter.getDocumentationComment(checker)).trim()
444
+ }))
445
+ };
446
+ });
447
+ const objectTypes = memberType.isUnion() ? memberType.types : [memberType];
448
+ const hasLocalMembers = objectTypes.every((part) => Boolean(part.flags & (ts.TypeFlags.Object | ts.TypeFlags.Intersection))) && !checker.isArrayType(memberType) && !checker.isTupleType(memberType) && !signatures.length && checker.getPropertiesOfType(memberType).some((member) => member.declarations?.some((node) => !isExternal(node.getSourceFile().fileName)));
397
449
  return {
398
450
  name: symbol.getName(),
399
451
  type: displayType || "unknown",
@@ -402,6 +454,8 @@ function createPropsCollector() {
402
454
  defaultValue: null,
403
455
  description: ts.displayPartsToString(symbol.getDocumentationComment(checker)).trim(),
404
456
  inheritedFrom,
457
+ inheritedComponent: inheritedComponent ? { name: inheritedComponent.name, slug: inheritedComponent.slug } : void 0,
458
+ signatures: signatures.length ? signatures : void 0,
405
459
  tags: symbol.getJsDocTags(checker).filter((tag) => ["deprecated", "since", "example"].includes(tag.name)).map((tag) => ({ name: tag.name, text: ts.displayPartsToString(tag.text) })),
406
460
  members: depth === 0 && (symbol.getName() === "slotProps" || !inheritedFrom && hasLocalMembers) ? describeProps(memberType, location, depth + 1) : void 0
407
461
  };
@@ -928,7 +982,10 @@ function storybookDocs(options2) {
928
982
  let components = propsCache.get(slug);
929
983
  if (!components) {
930
984
  const name = catalog.entries.find((entry) => entry.slug === slug)?.name ?? "";
931
- components = propsCollector.collect(catalog.files.get(slug), name, slug);
985
+ components = propsCollector.collect(catalog.files.get(slug), name, slug, catalog.entries.flatMap((entry) => {
986
+ const storyFile = catalog.files.get(entry.slug);
987
+ return storyFile ? [{ name: entry.name, slug: entry.slug, storyFile }] : [];
988
+ }));
932
989
  propsCache.set(slug, components);
933
990
  }
934
991
  return propsCode(components);
@@ -980,7 +1037,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
980
1037
  },
981
1038
  resolveId(id) {
982
1039
  if (id.startsWith(VIRTUAL_PREFIX)) return `\0${id}`;
983
- if (id.startsWith(FILE_PREFIX)) return realpathSync(id.slice(FILE_PREFIX.length));
1040
+ if (id.startsWith(FILE_PREFIX)) return realpathSync2(id.slice(FILE_PREFIX.length));
984
1041
  if (id === "storybook/test") return `\0${VIRTUAL_PREFIX}stub-storybook-test`;
985
1042
  return null;
986
1043
  },
@@ -993,7 +1050,7 @@ export const storySource = ${JSON.stringify(config.stories.from)};`,
993
1050
  if (name.startsWith("story-sources/")) {
994
1051
  const storyFile = catalog.files.get(name.slice("story-sources/".length));
995
1052
  if (!storyFile) return storySourcesCode({});
996
- const file = realpathSync(storyFile);
1053
+ const file = realpathSync2(storyFile);
997
1054
  let sources = storySourcesCache.get(file);
998
1055
  if (!sources) {
999
1056
  sources = extractStorySources(readFileSync3(file, "utf8"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jfrader/storybook-docs",
3
- "version": "0.9.2",
3
+ "version": "0.11.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": {