@baybreezy/docd 0.0.1

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.
Files changed (268) hide show
  1. package/app/app.config.ts +239 -0
  2. package/app/app.vue +19 -0
  3. package/app/assets/css/tailwind.css +335 -0
  4. package/app/assets/css/theme.css +1171 -0
  5. package/app/components/OgImage/Docs.takumi.vue +69 -0
  6. package/app/components/OgImage/Landing.takumi.vue +64 -0
  7. package/app/components/OgImage/Site.takumi.vue +32 -0
  8. package/app/components/Ui/Avatar/Avatar.vue +75 -0
  9. package/app/components/Ui/Avatar/Badge.vue +34 -0
  10. package/app/components/Ui/Avatar/Fallback.vue +30 -0
  11. package/app/components/Ui/Avatar/Group.vue +29 -0
  12. package/app/components/Ui/Avatar/GroupCount.vue +29 -0
  13. package/app/components/Ui/Avatar/Image.vue +29 -0
  14. package/app/components/Ui/Badge.vue +86 -0
  15. package/app/components/Ui/BadgeGroup.vue +314 -0
  16. package/app/components/Ui/BgPattern/Grid.vue +137 -0
  17. package/app/components/Ui/BorderBeam.vue +97 -0
  18. package/app/components/Ui/Breadcrumbs.vue +103 -0
  19. package/app/components/Ui/Button.vue +169 -0
  20. package/app/components/Ui/ButtonGroup/ButtonGroup.vue +54 -0
  21. package/app/components/Ui/ButtonGroup/Separator.vue +38 -0
  22. package/app/components/Ui/ButtonGroup/Text.vue +33 -0
  23. package/app/components/Ui/Card/Action.vue +31 -0
  24. package/app/components/Ui/Card/Card.vue +58 -0
  25. package/app/components/Ui/Card/Content.vue +34 -0
  26. package/app/components/Ui/Card/Description.vue +36 -0
  27. package/app/components/Ui/Card/Footer.vue +31 -0
  28. package/app/components/Ui/Card/Header.vue +31 -0
  29. package/app/components/Ui/Card/Title.vue +37 -0
  30. package/app/components/Ui/Collapsible/Collapsible.vue +16 -0
  31. package/app/components/Ui/Collapsible/Content.vue +27 -0
  32. package/app/components/Ui/Collapsible/Trigger.vue +13 -0
  33. package/app/components/Ui/Command/Command.vue +115 -0
  34. package/app/components/Ui/Command/Dialog.vue +65 -0
  35. package/app/components/Ui/Command/Empty.vue +32 -0
  36. package/app/components/Ui/Command/Footer.vue +40 -0
  37. package/app/components/Ui/Command/Group.vue +58 -0
  38. package/app/components/Ui/Command/Input.vue +57 -0
  39. package/app/components/Ui/Command/Item.vue +102 -0
  40. package/app/components/Ui/Command/List.vue +33 -0
  41. package/app/components/Ui/Command/Separator.vue +30 -0
  42. package/app/components/Ui/Command/Shortcut.vue +28 -0
  43. package/app/components/Ui/Container.vue +46 -0
  44. package/app/components/Ui/ContextMenu/Arrow.vue +43 -0
  45. package/app/components/Ui/ContextMenu/CheckboxItem.vue +42 -0
  46. package/app/components/Ui/ContextMenu/Content.vue +53 -0
  47. package/app/components/Ui/ContextMenu/ContextMenu.vue +14 -0
  48. package/app/components/Ui/ContextMenu/Group.vue +12 -0
  49. package/app/components/Ui/ContextMenu/Item.vue +65 -0
  50. package/app/components/Ui/ContextMenu/ItemIndicator.vue +30 -0
  51. package/app/components/Ui/ContextMenu/Label.vue +35 -0
  52. package/app/components/Ui/ContextMenu/Portal.vue +12 -0
  53. package/app/components/Ui/ContextMenu/RadioGroup.vue +16 -0
  54. package/app/components/Ui/ContextMenu/RadioItem.vue +40 -0
  55. package/app/components/Ui/ContextMenu/Separator.vue +25 -0
  56. package/app/components/Ui/ContextMenu/Shortcut.vue +30 -0
  57. package/app/components/Ui/ContextMenu/Sub.vue +15 -0
  58. package/app/components/Ui/ContextMenu/SubContent.vue +55 -0
  59. package/app/components/Ui/ContextMenu/SubTrigger.vue +42 -0
  60. package/app/components/Ui/ContextMenu/Trigger.vue +12 -0
  61. package/app/components/Ui/Dialog/Close.vue +12 -0
  62. package/app/components/Ui/Dialog/Content.vue +79 -0
  63. package/app/components/Ui/Dialog/Description.vue +29 -0
  64. package/app/components/Ui/Dialog/Dialog.vue +14 -0
  65. package/app/components/Ui/Dialog/Footer.vue +32 -0
  66. package/app/components/Ui/Dialog/Header.vue +32 -0
  67. package/app/components/Ui/Dialog/Overlay.vue +25 -0
  68. package/app/components/Ui/Dialog/Portal.vue +12 -0
  69. package/app/components/Ui/Dialog/Title.vue +29 -0
  70. package/app/components/Ui/Dialog/Trigger.vue +12 -0
  71. package/app/components/Ui/Divider.vue +102 -0
  72. package/app/components/Ui/Drawer/Close.vue +12 -0
  73. package/app/components/Ui/Drawer/Content.vue +67 -0
  74. package/app/components/Ui/Drawer/Description.vue +33 -0
  75. package/app/components/Ui/Drawer/Drawer.vue +17 -0
  76. package/app/components/Ui/Drawer/Footer.vue +35 -0
  77. package/app/components/Ui/Drawer/Header.vue +34 -0
  78. package/app/components/Ui/Drawer/Overlay.vue +20 -0
  79. package/app/components/Ui/Drawer/Portal.vue +12 -0
  80. package/app/components/Ui/Drawer/Title.vue +35 -0
  81. package/app/components/Ui/Drawer/Trigger.vue +12 -0
  82. package/app/components/Ui/DropdownMenu/Arrow.vue +43 -0
  83. package/app/components/Ui/DropdownMenu/CheckboxItem.vue +41 -0
  84. package/app/components/Ui/DropdownMenu/Content.vue +56 -0
  85. package/app/components/Ui/DropdownMenu/DropdownMenu.vue +15 -0
  86. package/app/components/Ui/DropdownMenu/Group.vue +12 -0
  87. package/app/components/Ui/DropdownMenu/Item.vue +64 -0
  88. package/app/components/Ui/DropdownMenu/ItemIndicator.vue +32 -0
  89. package/app/components/Ui/DropdownMenu/Label.vue +34 -0
  90. package/app/components/Ui/DropdownMenu/Portal.vue +12 -0
  91. package/app/components/Ui/DropdownMenu/RadioGroup.vue +16 -0
  92. package/app/components/Ui/DropdownMenu/RadioItem.vue +40 -0
  93. package/app/components/Ui/DropdownMenu/Separator.vue +25 -0
  94. package/app/components/Ui/DropdownMenu/Shortcut.vue +44 -0
  95. package/app/components/Ui/DropdownMenu/Sub.vue +14 -0
  96. package/app/components/Ui/DropdownMenu/SubContent.vue +56 -0
  97. package/app/components/Ui/DropdownMenu/SubTrigger.vue +47 -0
  98. package/app/components/Ui/DropdownMenu/Trigger.vue +12 -0
  99. package/app/components/Ui/FancyIcon.vue +243 -0
  100. package/app/components/Ui/GradientDivider.vue +26 -0
  101. package/app/components/Ui/IframeLazy.vue +97 -0
  102. package/app/components/Ui/Input.vue +97 -0
  103. package/app/components/Ui/InputGroup/Addon.vue +66 -0
  104. package/app/components/Ui/InputGroup/Button.vue +50 -0
  105. package/app/components/Ui/InputGroup/Input.vue +25 -0
  106. package/app/components/Ui/InputGroup/InputGroup.vue +44 -0
  107. package/app/components/Ui/InputGroup/Text.vue +33 -0
  108. package/app/components/Ui/InputGroup/Textarea.vue +25 -0
  109. package/app/components/Ui/Kbd/Group.vue +32 -0
  110. package/app/components/Ui/Kbd/Kbd.vue +62 -0
  111. package/app/components/Ui/Label.vue +40 -0
  112. package/app/components/Ui/Popover/Anchor.vue +13 -0
  113. package/app/components/Ui/Popover/Arrow.vue +42 -0
  114. package/app/components/Ui/Popover/Close.vue +13 -0
  115. package/app/components/Ui/Popover/Content.vue +55 -0
  116. package/app/components/Ui/Popover/Popover.vue +15 -0
  117. package/app/components/Ui/Popover/Portal.vue +13 -0
  118. package/app/components/Ui/Popover/Trigger.vue +12 -0
  119. package/app/components/Ui/Popover/X.vue +38 -0
  120. package/app/components/Ui/ScrollArea/Corner.vue +12 -0
  121. package/app/components/Ui/ScrollArea/ScrollArea.vue +38 -0
  122. package/app/components/Ui/ScrollArea/Scrollbar.vue +39 -0
  123. package/app/components/Ui/ScrollArea/Thumb.vue +39 -0
  124. package/app/components/Ui/ScrollArea/Viewport.vue +27 -0
  125. package/app/components/Ui/Scrollspy.vue +61 -0
  126. package/app/components/Ui/Separator.vue +40 -0
  127. package/app/components/Ui/Skeleton.vue +39 -0
  128. package/app/components/Ui/Sonner.vue +25 -0
  129. package/app/components/Ui/Tabs/Content.vue +27 -0
  130. package/app/components/Ui/Tabs/Indicator.vue +37 -0
  131. package/app/components/Ui/Tabs/List.vue +37 -0
  132. package/app/components/Ui/Tabs/Tabs.vue +25 -0
  133. package/app/components/Ui/Tabs/Trigger.vue +41 -0
  134. package/app/components/Ui/Textarea.vue +81 -0
  135. package/app/components/Ui/Tooltip/Arrow.vue +30 -0
  136. package/app/components/Ui/Tooltip/Content.vue +50 -0
  137. package/app/components/Ui/Tooltip/Portal.vue +12 -0
  138. package/app/components/Ui/Tooltip/Provider.vue +14 -0
  139. package/app/components/Ui/Tooltip/Tooltip.vue +25 -0
  140. package/app/components/Ui/Tooltip/Trigger.vue +12 -0
  141. package/app/components/Ui/Tree/Item.vue +15 -0
  142. package/app/components/Ui/Tree/Tree.vue +15 -0
  143. package/app/components/Ui/Tree/Virtualizer.vue +13 -0
  144. package/app/components/component-api/ComponentApiSection.vue +135 -0
  145. package/app/components/content/docs/Callout/DocsCallout.vue +99 -0
  146. package/app/components/content/prose/ProseA.global.vue +31 -0
  147. package/app/components/content/prose/ProseBlockquote.global.vue +28 -0
  148. package/app/components/content/prose/ProseCallout.global.vue +306 -0
  149. package/app/components/content/prose/ProseCard.global.vue +100 -0
  150. package/app/components/content/prose/ProseCodeCollapse.global.vue +125 -0
  151. package/app/components/content/prose/ProseCodeCopy.global.vue +67 -0
  152. package/app/components/content/prose/ProseCodeGroup.global.vue +104 -0
  153. package/app/components/content/prose/ProseCodeTree.global.vue +277 -0
  154. package/app/components/content/prose/ProseCollapsible.global.vue +64 -0
  155. package/app/components/content/prose/ProseColorModeImage.global.vue +58 -0
  156. package/app/components/content/prose/ProseComponentApi.global.vue +104 -0
  157. package/app/components/content/prose/ProseComponentEvents.global.vue +27 -0
  158. package/app/components/content/prose/ProseComponentExposed.global.vue +27 -0
  159. package/app/components/content/prose/ProseComponentProps.global.vue +27 -0
  160. package/app/components/content/prose/ProseComponentSlots.global.vue +27 -0
  161. package/app/components/content/prose/ProseEm.global.vue +5 -0
  162. package/app/components/content/prose/ProseField.global.vue +118 -0
  163. package/app/components/content/prose/ProseFieldGroup.global.vue +62 -0
  164. package/app/components/content/prose/ProseH1.global.vue +58 -0
  165. package/app/components/content/prose/ProseH2.global.vue +58 -0
  166. package/app/components/content/prose/ProseH3.global.vue +58 -0
  167. package/app/components/content/prose/ProseH4.global.vue +58 -0
  168. package/app/components/content/prose/ProseH5.global.vue +47 -0
  169. package/app/components/content/prose/ProseH6.global.vue +47 -0
  170. package/app/components/content/prose/ProseHr.global.vue +33 -0
  171. package/app/components/content/prose/ProseIconList.global.vue +113 -0
  172. package/app/components/content/prose/ProseImg.global.vue +119 -0
  173. package/app/components/content/prose/ProseLead.global.vue +28 -0
  174. package/app/components/content/prose/ProseLi.global.vue +25 -0
  175. package/app/components/content/prose/ProseMermaid.global.vue +127 -0
  176. package/app/components/content/prose/ProseOl.global.vue +25 -0
  177. package/app/components/content/prose/ProseP.global.vue +25 -0
  178. package/app/components/content/prose/ProsePmInstall.global.vue +60 -0
  179. package/app/components/content/prose/ProsePmRun.global.vue +51 -0
  180. package/app/components/content/prose/ProsePmX.global.vue +56 -0
  181. package/app/components/content/prose/ProsePre.global.vue +181 -0
  182. package/app/components/content/prose/ProseShowCase.global.vue +56 -0
  183. package/app/components/content/prose/ProseSmartIcon.global.vue +54 -0
  184. package/app/components/content/prose/ProseStep.global.vue +22 -0
  185. package/app/components/content/prose/ProseSteps.global.vue +22 -0
  186. package/app/components/content/prose/ProseStrong.global.vue +34 -0
  187. package/app/components/content/prose/ProseTable.global.vue +34 -0
  188. package/app/components/content/prose/ProseTabs.global.vue +104 -0
  189. package/app/components/content/prose/ProseTabsInner.global.vue +274 -0
  190. package/app/components/content/prose/ProseTbody.global.vue +5 -0
  191. package/app/components/content/prose/ProseTd.global.vue +25 -0
  192. package/app/components/content/prose/ProseTh.global.vue +25 -0
  193. package/app/components/content/prose/ProseThead.global.vue +9 -0
  194. package/app/components/content/prose/ProseTr.global.vue +35 -0
  195. package/app/components/content/prose/ProseUl.global.vue +25 -0
  196. package/app/components/docs/DocsExtraStuff.vue +39 -0
  197. package/app/components/docs/DocsGithubLink.vue +11 -0
  198. package/app/components/docs/DocsHeader.vue +3 -0
  199. package/app/components/docs/DocsLogo.vue +74 -0
  200. package/app/components/docs/DocsMobileBar.vue +26 -0
  201. package/app/components/docs/DocsMobileNav.vue +49 -0
  202. package/app/components/docs/DocsNav.vue +161 -0
  203. package/app/components/docs/DocsPageTransition.vue +32 -0
  204. package/app/components/docs/DocsSearch.vue +220 -0
  205. package/app/components/docs/DocsSearchButton.vue +20 -0
  206. package/app/components/docs/DocsThemeCustomizer.vue +229 -0
  207. package/app/components/docs/DocsThemeToggler.vue +98 -0
  208. package/app/components/docs/DocsTocRail.vue +162 -0
  209. package/app/components/docs/DocsToclink.vue +52 -0
  210. package/app/components/docs/DocsZigZagRail.vue +70 -0
  211. package/app/components/landing/Hero.vue +222 -0
  212. package/app/components/page/PageFooter.vue +49 -0
  213. package/app/components/page/PageHeader.vue +68 -0
  214. package/app/components/page/PageHeaderLinks.vue +138 -0
  215. package/app/components/page/PageMobileToc.vue +162 -0
  216. package/app/components/page/PageToc.vue +33 -0
  217. package/app/composables/useDefaultSlotItems.ts +90 -0
  218. package/app/composables/useDocPage.ts +66 -0
  219. package/app/composables/useDocSearch.ts +23 -0
  220. package/app/composables/useDocd.ts +107 -0
  221. package/app/composables/useLogoAssets.ts +319 -0
  222. package/app/composables/usePm.ts +73 -0
  223. package/app/composables/useProseComponentMeta.ts +30 -0
  224. package/app/composables/useScrollspy.ts +327 -0
  225. package/app/composables/useSeo.ts +240 -0
  226. package/app/composables/useUIConfig.ts +36 -0
  227. package/app/layouts/default.vue +5 -0
  228. package/app/layouts/docs.vue +64 -0
  229. package/app/middleware/colorMode.global.ts +8 -0
  230. package/app/middleware/doc-page-layout.ts +11 -0
  231. package/app/middleware/landing-page-layout.ts +11 -0
  232. package/app/pages/[...slug].vue +63 -0
  233. package/app/plugins/mermaid.client.ts +35 -0
  234. package/app/plugins/navigation-direction.client.ts +15 -0
  235. package/app/plugins/theme-restore.client.ts +7 -0
  236. package/app/templates/landing.vue +40 -0
  237. package/app/transitions/presets.ts +71 -0
  238. package/app/types/prose-component-meta.d.ts +4 -0
  239. package/app/utils/og.ts +26 -0
  240. package/app/utils/prerender.ts +9 -0
  241. package/app/utils/translucent.ts +56 -0
  242. package/app/utils/tw-helper.ts +4 -0
  243. package/content.config.ts +81 -0
  244. package/index.d.ts +25 -0
  245. package/modules/config.ts +89 -0
  246. package/modules/custom-icons.ts +17 -0
  247. package/modules/markdown-rewrite.ts +96 -0
  248. package/modules/prose-component-meta.ts +150 -0
  249. package/modules/routing.ts +22 -0
  250. package/modules/skills/index.ts +167 -0
  251. package/modules/skills/runtime/server/routes/skills-files.ts +49 -0
  252. package/modules/skills/runtime/server/routes/skills-index.ts +8 -0
  253. package/nuxt.config.ts +96 -0
  254. package/nuxt.schema.ts +288 -0
  255. package/package.json +95 -0
  256. package/server/mcp/tools/get-page.ts +66 -0
  257. package/server/mcp/tools/list-pages.ts +61 -0
  258. package/server/routes/sitemap.xml.ts +73 -0
  259. package/server/utils/content.ts +3 -0
  260. package/tailwind.config.cjs +55 -0
  261. package/utils/component-api-content.ts +467 -0
  262. package/utils/component-api-discovery.ts +165 -0
  263. package/utils/component-api-schema.ts +28 -0
  264. package/utils/component-api.ts +243 -0
  265. package/utils/generate-prose-component-meta.ts +303 -0
  266. package/utils/git.ts +123 -0
  267. package/utils/meta.ts +40 -0
  268. package/utils/pages.ts +21 -0
@@ -0,0 +1,467 @@
1
+ import { existsSync } from "node:fs";
2
+ import { basename, extname } from "node:path";
3
+
4
+ import { parseMarkdown } from "@nuxtjs/mdc/runtime";
5
+
6
+ import {
7
+ BUILT_IN_PROSE_COMPONENT_API_REGISTRY,
8
+ DEFAULT_COMPONENT_API_LAYOUT,
9
+ DEFAULT_COMPONENT_API_SECTIONS,
10
+ componentLabelFromPath,
11
+ componentNameFromPath,
12
+ inferredBuiltInProseComponentPathFromSlug,
13
+ normalizeComponentApiConfig,
14
+ normalizeComponentApiLayout,
15
+ normalizeComponentApiSections,
16
+ type BuiltInComponentApiRegistryItem,
17
+ type NormalizedComponentApiItem,
18
+ } from "./component-api";
19
+ import { resolveComponentApiSourcePath } from "./component-api-discovery";
20
+
21
+ type TransformerBodyNode = {
22
+ type?: string;
23
+ tag?: string;
24
+ children?: TransformerBodyNode[];
25
+ };
26
+
27
+ type TransformerTocLink = {
28
+ id?: string;
29
+ depth?: number;
30
+ text?: string;
31
+ children?: TransformerTocLink[];
32
+ };
33
+
34
+ type MinimarkNode = string | number | boolean | null | MinimarkElement;
35
+ type MinimarkElement = [string, Record<string, unknown>?, ...MinimarkNode[]];
36
+
37
+ export type ComponentApiTransformerContent = {
38
+ id?: string;
39
+ body?: {
40
+ type?: string;
41
+ children?: TransformerBodyNode[];
42
+ value?: MinimarkNode[];
43
+ toc?: {
44
+ title?: string;
45
+ searchDepth?: number;
46
+ depth?: number;
47
+ links?: TransformerTocLink[];
48
+ };
49
+ [key: string]: unknown;
50
+ };
51
+ componentApi?: unknown;
52
+ [key: string]: unknown;
53
+ };
54
+
55
+ export interface ComponentApiTransformerContext {
56
+ rootDir: string;
57
+ layerRoot: string;
58
+ warn: (message: string) => void;
59
+ }
60
+
61
+ function escapeMdcProp(value: string) {
62
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
63
+ }
64
+
65
+ function fileId(file: ComponentApiTransformerContent) {
66
+ return typeof file.id === "string" ? file.id : "";
67
+ }
68
+
69
+ function hasExistingComponentApiNode(nodes: TransformerBodyNode[] = []): boolean {
70
+ return nodes.some((node) => {
71
+ if (node.tag === "prose-component-api") {
72
+ return true;
73
+ }
74
+
75
+ return Array.isArray(node.children) && hasExistingComponentApiNode(node.children);
76
+ });
77
+ }
78
+
79
+ function isMinimarkElement(node: MinimarkNode): node is MinimarkElement {
80
+ return Array.isArray(node) && typeof node[0] === "string";
81
+ }
82
+
83
+ function hasExistingComponentApiMinimarkNode(nodes: MinimarkNode[] = []): boolean {
84
+ return nodes.some((node) => {
85
+ if (!isMinimarkElement(node)) {
86
+ return false;
87
+ }
88
+
89
+ if (node[0] === "prose-component-api") {
90
+ return true;
91
+ }
92
+
93
+ return hasExistingComponentApiMinimarkNode(node.slice(2) as MinimarkNode[]);
94
+ });
95
+ }
96
+
97
+ export function withAutoTitles(items: NormalizedComponentApiItem[]) {
98
+ const needsAutoTitles = items.length > 1;
99
+
100
+ return items.map((item) => ({
101
+ ...item,
102
+ title: item.title ?? (needsAutoTitles ? componentNameFromPath(item.path) : undefined),
103
+ }));
104
+ }
105
+
106
+ function registryItemsToNormalizedItems(
107
+ items: readonly BuiltInComponentApiRegistryItem[],
108
+ defaults: Pick<NormalizedComponentApiItem, "layout" | "sections">
109
+ ) {
110
+ return items.map(
111
+ (item) =>
112
+ ({
113
+ path: item.path,
114
+ title: item.title,
115
+ layout: normalizeComponentApiLayout(item.layout, defaults.layout),
116
+ sections: normalizeComponentApiSections(item.sections, defaults.sections),
117
+ }) satisfies NormalizedComponentApiItem
118
+ );
119
+ }
120
+
121
+ function inferredItem(
122
+ slug: string,
123
+ defaults: Pick<NormalizedComponentApiItem, "layout" | "sections">,
124
+ context: ComponentApiTransformerContext
125
+ ) {
126
+ const inferredPath = inferredBuiltInProseComponentPathFromSlug(slug);
127
+ const resolvedPath = resolveComponentApiSourcePath({
128
+ rootDir: context.rootDir,
129
+ layerRoot: context.layerRoot,
130
+ path: inferredPath,
131
+ });
132
+
133
+ if (!existsSync(resolvedPath.absolutePath)) {
134
+ return [];
135
+ }
136
+
137
+ return [
138
+ {
139
+ path: inferredPath,
140
+ layout: defaults.layout,
141
+ sections: [...defaults.sections],
142
+ } satisfies NormalizedComponentApiItem,
143
+ ];
144
+ }
145
+
146
+ function defaultItemSettings(config: ReturnType<typeof normalizeComponentApiConfig>) {
147
+ if (config && config !== false) {
148
+ return {
149
+ heading: config.heading,
150
+ layout: config.layout,
151
+ sections: config.sections,
152
+ };
153
+ }
154
+
155
+ return {
156
+ heading: undefined,
157
+ layout: DEFAULT_COMPONENT_API_LAYOUT,
158
+ sections: [...DEFAULT_COMPONENT_API_SECTIONS],
159
+ };
160
+ }
161
+
162
+ function buildComponentApiBlockSource(item: NormalizedComponentApiItem, title?: string) {
163
+ const props = [`path="${escapeMdcProp(item.path)}"`];
164
+
165
+ if (item.layout !== DEFAULT_COMPONENT_API_LAYOUT) {
166
+ props.push(`layout="${item.layout}"`);
167
+ }
168
+
169
+ if (title) {
170
+ props.push(`title="${escapeMdcProp(title)}"`);
171
+ }
172
+
173
+ if (item.sections.length) {
174
+ props.push(`sections="${escapeMdcProp(item.sections.join(","))}"`);
175
+ }
176
+
177
+ return `:prose-component-api{${props.join(" ")}}`;
178
+ }
179
+
180
+ function buildComponentApiBlockMinimarkNode(
181
+ item: NormalizedComponentApiItem,
182
+ title?: string
183
+ ): MinimarkElement {
184
+ const props: Record<string, string> = {
185
+ path: item.path,
186
+ sections: item.sections.join(","),
187
+ };
188
+
189
+ if (item.layout !== DEFAULT_COMPONENT_API_LAYOUT) {
190
+ props.layout = item.layout;
191
+ }
192
+
193
+ if (title) {
194
+ props.title = title;
195
+ }
196
+
197
+ return ["prose-component-api", props];
198
+ }
199
+
200
+ function buildComponentApiBlocksSource(
201
+ items: NormalizedComponentApiItem[],
202
+ heading: string | false | undefined
203
+ ) {
204
+ const lines: string[] = [];
205
+ const hasGroupHeading = heading !== false;
206
+ const groupHeading =
207
+ heading ||
208
+ (items.length === 1 ? `${componentLabelFromPath(items[0].path)} API` : "Component API");
209
+ const needsItemHeadings = items.length > 1;
210
+
211
+ if (hasGroupHeading) {
212
+ lines.push(`## ${groupHeading}`, "");
213
+ }
214
+
215
+ for (const item of items) {
216
+ if (needsItemHeadings) {
217
+ lines.push(
218
+ `${hasGroupHeading ? "###" : "##"} ${item.title || componentNameFromPath(item.path)}`,
219
+ ""
220
+ );
221
+ }
222
+
223
+ lines.push(buildComponentApiBlockSource(item), "");
224
+ }
225
+
226
+ return lines.join("\n").trim();
227
+ }
228
+
229
+ function slugifyHeading(text: string) {
230
+ return text
231
+ .normalize("NFKD")
232
+ .replace(/[\u0300-\u036f]/g, "")
233
+ .toLowerCase()
234
+ .trim()
235
+ .replace(/[^a-z0-9\s-]/g, "")
236
+ .replace(/\s+/g, "-")
237
+ .replace(/-+/g, "-");
238
+ }
239
+
240
+ function uniqueHeadingId(text: string, counts: Map<string, number>) {
241
+ const base = slugifyHeading(text) || "section";
242
+ const current = counts.get(base) || 0;
243
+ counts.set(base, current + 1);
244
+ return current ? `${base}-${current}` : base;
245
+ }
246
+
247
+ function buildComponentApiMinimarkAppend(
248
+ items: NormalizedComponentApiItem[],
249
+ heading: string | false | undefined
250
+ ) {
251
+ const value: MinimarkNode[] = [];
252
+ const links: TransformerTocLink[] = [];
253
+ const hasGroupHeading = heading !== false;
254
+ const groupHeading =
255
+ heading ||
256
+ (items.length === 1 ? `${componentLabelFromPath(items[0].path)} API` : "Component API");
257
+ const needsItemHeadings = items.length > 1;
258
+ const idCounts = new Map<string, number>();
259
+ let parentLink: TransformerTocLink | undefined;
260
+
261
+ if (hasGroupHeading) {
262
+ const groupId = uniqueHeadingId(groupHeading, idCounts);
263
+ parentLink = {
264
+ id: groupId,
265
+ depth: 2,
266
+ text: groupHeading,
267
+ children: [],
268
+ };
269
+ links.push(parentLink);
270
+ value.push(["h2", { id: groupId }, groupHeading]);
271
+ }
272
+
273
+ for (const item of items) {
274
+ if (needsItemHeadings) {
275
+ const itemHeading = item.title || componentNameFromPath(item.path);
276
+ const itemDepth = hasGroupHeading ? 3 : 2;
277
+ const itemTag = hasGroupHeading ? "h3" : "h2";
278
+ const itemId = uniqueHeadingId(itemHeading, idCounts);
279
+ const itemLink: TransformerTocLink = {
280
+ id: itemId,
281
+ depth: itemDepth,
282
+ text: itemHeading,
283
+ };
284
+
285
+ if (parentLink) {
286
+ parentLink.children = [...(parentLink.children || []), itemLink];
287
+ } else {
288
+ links.push(itemLink);
289
+ }
290
+
291
+ value.push([itemTag, { id: itemId }, itemHeading]);
292
+ }
293
+
294
+ value.push(buildComponentApiBlockMinimarkNode(item));
295
+ }
296
+
297
+ return { links, value };
298
+ }
299
+
300
+ function appendTocLinks(
301
+ body: ComponentApiTransformerContent["body"],
302
+ links: TransformerTocLink[] | undefined
303
+ ) {
304
+ if (!body || !Array.isArray(links) || !links.length) {
305
+ return body;
306
+ }
307
+
308
+ return {
309
+ ...body,
310
+ toc: {
311
+ ...(body.toc || {}),
312
+ links: [...(body.toc?.links || []), ...links],
313
+ },
314
+ };
315
+ }
316
+
317
+ export function isBuiltInProseDocFile(file: ComponentApiTransformerContent) {
318
+ return /^docs\/4\.prose\/.+\.md$/i.test(fileId(file));
319
+ }
320
+
321
+ export function builtInProseDocSlug(file: ComponentApiTransformerContent) {
322
+ return basename(fileId(file), extname(fileId(file)));
323
+ }
324
+
325
+ export function projectComponentApiConfig(file: ComponentApiTransformerContent) {
326
+ return normalizeComponentApiConfig(file.componentApi);
327
+ }
328
+
329
+ export function builtInComponentApiItems(
330
+ file: ComponentApiTransformerContent,
331
+ context: ComponentApiTransformerContext
332
+ ) {
333
+ const config = normalizeComponentApiConfig(file.componentApi);
334
+
335
+ if (config === false) {
336
+ return {
337
+ heading: undefined,
338
+ items: [] as NormalizedComponentApiItem[],
339
+ explicit: false,
340
+ };
341
+ }
342
+
343
+ if (config?.hasExplicitComponents) {
344
+ return {
345
+ heading: config.heading,
346
+ items: withAutoTitles(config.components),
347
+ explicit: true,
348
+ };
349
+ }
350
+
351
+ const slug = builtInProseDocSlug(file);
352
+ const defaults = defaultItemSettings(config);
353
+ const registryEntry = BUILT_IN_PROSE_COMPONENT_API_REGISTRY[slug];
354
+
355
+ if (registryEntry === false) {
356
+ return {
357
+ heading: defaults.heading,
358
+ items: [] as NormalizedComponentApiItem[],
359
+ explicit: false,
360
+ };
361
+ }
362
+
363
+ if (Array.isArray(registryEntry)) {
364
+ return {
365
+ heading: defaults.heading,
366
+ items: withAutoTitles(registryItemsToNormalizedItems(registryEntry, defaults)),
367
+ explicit: false,
368
+ };
369
+ }
370
+
371
+ return {
372
+ heading: defaults.heading,
373
+ items: inferredItem(slug, defaults, context),
374
+ explicit: false,
375
+ };
376
+ }
377
+
378
+ export function warnForMissingComponentPaths(
379
+ file: ComponentApiTransformerContent,
380
+ items: NormalizedComponentApiItem[],
381
+ context: ComponentApiTransformerContext
382
+ ) {
383
+ for (const item of items) {
384
+ const resolvedPath = resolveComponentApiSourcePath({
385
+ rootDir: context.rootDir,
386
+ layerRoot: context.layerRoot,
387
+ path: item.path,
388
+ });
389
+
390
+ if (!existsSync(resolvedPath.absolutePath)) {
391
+ context.warn(
392
+ `No component metadata was found for "${item.path}" while transforming ${fileId(file)}.`
393
+ );
394
+ }
395
+ }
396
+ }
397
+
398
+ export async function appendComponentApiBlocks(
399
+ file: ComponentApiTransformerContent,
400
+ items: NormalizedComponentApiItem[],
401
+ heading?: string | false
402
+ ) {
403
+ if (!file.body || !items.length) {
404
+ return file;
405
+ }
406
+
407
+ const config = normalizeComponentApiConfig(file.componentApi);
408
+ const resolvedHeading =
409
+ heading === false
410
+ ? false
411
+ : (heading ?? (config && config !== false ? config.heading : undefined));
412
+ const source = buildComponentApiBlocksSource(items, resolvedHeading);
413
+ const parsed = await parseMarkdown(source);
414
+ const parsedTocLinks = parsed.body.toc?.links as TransformerTocLink[] | undefined;
415
+
416
+ if (file.body.type === "minimark" && Array.isArray(file.body.value)) {
417
+ if (hasExistingComponentApiMinimarkNode(file.body.value)) {
418
+ return file;
419
+ }
420
+
421
+ const { links, value: parsedValue } = buildComponentApiMinimarkAppend(items, resolvedHeading);
422
+ const value = [...file.body.value];
423
+
424
+ for (const node of parsedValue) {
425
+ value.push(node);
426
+ }
427
+
428
+ return {
429
+ ...file,
430
+ body: appendTocLinks(
431
+ {
432
+ ...file.body,
433
+ value,
434
+ },
435
+ links
436
+ ),
437
+ };
438
+ }
439
+
440
+ if (!Array.isArray(file.body.children)) {
441
+ return file;
442
+ }
443
+
444
+ if (hasExistingComponentApiNode(file.body.children)) {
445
+ return file;
446
+ }
447
+
448
+ const children = [...file.body.children];
449
+ const parsedChildren = Array.isArray(parsed.body.children)
450
+ ? (parsed.body.children as TransformerBodyNode[])
451
+ : [];
452
+
453
+ for (const node of parsedChildren) {
454
+ children.push(node);
455
+ }
456
+
457
+ return {
458
+ ...file,
459
+ body: appendTocLinks(
460
+ {
461
+ ...file.body,
462
+ children,
463
+ },
464
+ parsedTocLinks
465
+ ),
466
+ };
467
+ }
@@ -0,0 +1,165 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { readdir } from "node:fs/promises";
3
+ import { relative, resolve, join } from "node:path";
4
+
5
+ import { parse as parseYaml } from "yaml";
6
+
7
+ import {
8
+ isBuiltInProseComponentPath,
9
+ normalizeComponentApiConfig,
10
+ normalizeComponentApiPath,
11
+ normalizeComponentMetaPath,
12
+ } from "./component-api";
13
+
14
+ export interface ResolvedComponentApiSourcePath {
15
+ kind: "builtin" | "project";
16
+ manifestPath: string;
17
+ absolutePath: string;
18
+ }
19
+
20
+ export interface DiscoveredProjectComponentPath {
21
+ manifestPath: string;
22
+ absolutePath: string;
23
+ }
24
+
25
+ export interface MissingProjectComponentPath {
26
+ contentFile: string;
27
+ componentPath: string;
28
+ }
29
+
30
+ export interface ProjectComponentApiDiscoveryResult {
31
+ contentFiles: string[];
32
+ components: DiscoveredProjectComponentPath[];
33
+ missingComponents: MissingProjectComponentPath[];
34
+ }
35
+
36
+ async function listMarkdownFiles(dir: string): Promise<string[]> {
37
+ const files: string[] = [];
38
+ const entries = await readdir(dir, { withFileTypes: true });
39
+
40
+ for (const entry of entries) {
41
+ const fullPath = join(dir, entry.name);
42
+
43
+ if (entry.isDirectory()) {
44
+ files.push(...(await listMarkdownFiles(fullPath)));
45
+ continue;
46
+ }
47
+
48
+ if (entry.isFile() && fullPath.endsWith(".md")) {
49
+ files.push(fullPath);
50
+ }
51
+ }
52
+
53
+ return files.sort((a, b) => a.localeCompare(b));
54
+ }
55
+
56
+ function extractFrontmatter(source: string) {
57
+ const normalizedSource = source.replace(/\r\n/g, "\n");
58
+
59
+ if (!normalizedSource.startsWith("---\n")) {
60
+ return null;
61
+ }
62
+
63
+ const endIndex = normalizedSource.indexOf("\n---\n", 4);
64
+ if (endIndex === -1) {
65
+ return null;
66
+ }
67
+
68
+ return normalizedSource.slice(4, endIndex);
69
+ }
70
+
71
+ function readFrontmatter(path: string) {
72
+ try {
73
+ const contents = readFileSync(path, "utf8");
74
+ const frontmatter = extractFrontmatter(contents);
75
+
76
+ return frontmatter ? (parseYaml(frontmatter) as Record<string, unknown>) : null;
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+
82
+ export function resolveComponentApiSourcePath(options: {
83
+ rootDir: string;
84
+ layerRoot: string;
85
+ path: string;
86
+ }): ResolvedComponentApiSourcePath {
87
+ const normalizedMetaPath = normalizeComponentMetaPath(options.path);
88
+
89
+ if (isBuiltInProseComponentPath(normalizedMetaPath)) {
90
+ return {
91
+ kind: "builtin",
92
+ manifestPath: normalizedMetaPath,
93
+ absolutePath: resolve(options.layerRoot, normalizedMetaPath),
94
+ };
95
+ }
96
+
97
+ const manifestPath = normalizeComponentApiPath(options.path);
98
+
99
+ return {
100
+ kind: "project",
101
+ manifestPath,
102
+ absolutePath: resolve(options.rootDir, manifestPath),
103
+ };
104
+ }
105
+
106
+ export async function discoverProjectComponentApiPaths(options: {
107
+ rootDir: string;
108
+ layerRoot: string;
109
+ contentDir?: string;
110
+ }): Promise<ProjectComponentApiDiscoveryResult> {
111
+ const contentDir = options.contentDir || resolve(options.rootDir, "content");
112
+
113
+ if (!existsSync(contentDir)) {
114
+ return {
115
+ contentFiles: [],
116
+ components: [],
117
+ missingComponents: [],
118
+ };
119
+ }
120
+
121
+ const markdownFiles = await listMarkdownFiles(contentDir);
122
+ const componentMap = new Map<string, DiscoveredProjectComponentPath>();
123
+ const missingComponentMap = new Map<string, MissingProjectComponentPath>();
124
+
125
+ for (const file of markdownFiles) {
126
+ const frontmatter = readFrontmatter(file);
127
+ const componentApi = normalizeComponentApiConfig(frontmatter?.componentApi);
128
+
129
+ if (!componentApi || componentApi === false || !componentApi.hasExplicitComponents) {
130
+ continue;
131
+ }
132
+
133
+ for (const component of componentApi.components) {
134
+ const resolvedPath = resolveComponentApiSourcePath({
135
+ rootDir: options.rootDir,
136
+ layerRoot: options.layerRoot,
137
+ path: component.path,
138
+ });
139
+
140
+ if (resolvedPath.kind === "builtin") {
141
+ continue;
142
+ }
143
+
144
+ if (!existsSync(resolvedPath.absolutePath)) {
145
+ const key = `${relative(options.rootDir, file)}:${resolvedPath.manifestPath}`;
146
+ missingComponentMap.set(key, {
147
+ contentFile: relative(options.rootDir, file),
148
+ componentPath: resolvedPath.manifestPath,
149
+ });
150
+ continue;
151
+ }
152
+
153
+ componentMap.set(resolvedPath.manifestPath, {
154
+ manifestPath: resolvedPath.manifestPath,
155
+ absolutePath: resolvedPath.absolutePath,
156
+ });
157
+ }
158
+ }
159
+
160
+ return {
161
+ contentFiles: markdownFiles,
162
+ components: Array.from(componentMap.values()),
163
+ missingComponents: Array.from(missingComponentMap.values()),
164
+ };
165
+ }
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+
3
+ import { DEFAULT_COMPONENT_API_LAYOUT, DEFAULT_COMPONENT_API_SECTIONS } from "./component-api";
4
+
5
+ const componentApiLayoutSchema = z.enum(["field", "table"]);
6
+ const componentApiSectionsSchema = z
7
+ .array(z.enum(["props", "slots", "events", "exposed"]))
8
+ .default([...DEFAULT_COMPONENT_API_SECTIONS]);
9
+
10
+ const componentApiComponentSchema = z.object({
11
+ path: z.string(),
12
+ title: z.string().optional(),
13
+ layout: componentApiLayoutSchema.default(DEFAULT_COMPONENT_API_LAYOUT).optional(),
14
+ sections: componentApiSectionsSchema.optional(),
15
+ });
16
+
17
+ export const componentApiSchema = z
18
+ .union([
19
+ z.literal(false),
20
+ z.object({
21
+ heading: z.union([z.string(), z.literal(false)]).optional(),
22
+ path: z.string().optional(),
23
+ layout: componentApiLayoutSchema.default(DEFAULT_COMPONENT_API_LAYOUT).optional(),
24
+ sections: componentApiSectionsSchema.optional(),
25
+ components: z.array(componentApiComponentSchema).optional(),
26
+ }),
27
+ ])
28
+ .optional();