@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,243 @@
1
+ export const DEFAULT_COMPONENT_API_LAYOUT = "table" as const;
2
+ export const DEFAULT_COMPONENT_API_SECTIONS = ["props", "slots", "events", "exposed"] as const;
3
+ export const BUILT_IN_PROSE_COMPONENT_ROOT = "app/components/content/prose/";
4
+
5
+ export type ProseComponentApiLayout = "field" | "table";
6
+ export type ComponentApiSectionKind = (typeof DEFAULT_COMPONENT_API_SECTIONS)[number];
7
+
8
+ export interface ComponentApiComponentInput {
9
+ path?: string;
10
+ title?: string;
11
+ layout?: ProseComponentApiLayout;
12
+ sections?: ComponentApiSectionKind[] | string;
13
+ }
14
+
15
+ export interface NormalizedComponentApiItem {
16
+ path: string;
17
+ title?: string;
18
+ layout: ProseComponentApiLayout;
19
+ sections: ComponentApiSectionKind[];
20
+ }
21
+
22
+ export interface NormalizedComponentApiConfig {
23
+ heading?: string | false;
24
+ layout: ProseComponentApiLayout;
25
+ sections: ComponentApiSectionKind[];
26
+ components: NormalizedComponentApiItem[];
27
+ hasExplicitComponents: boolean;
28
+ }
29
+
30
+ export interface BuiltInComponentApiRegistryItem extends ComponentApiComponentInput {
31
+ path: string;
32
+ }
33
+
34
+ export const BUILT_IN_PROSE_COMPONENT_API_REGISTRY: Record<
35
+ string,
36
+ false | readonly BuiltInComponentApiRegistryItem[]
37
+ > = {
38
+ field: [
39
+ { path: "app/components/content/prose/ProseField.global.vue" },
40
+ { path: "app/components/content/prose/ProseFieldGroup.global.vue" },
41
+ ],
42
+ images: [
43
+ { path: "app/components/content/prose/ProseImg.global.vue" },
44
+ { path: "app/components/content/prose/ProseColorModeImage.global.vue" },
45
+ ],
46
+ "icon-list": [
47
+ { path: "app/components/content/prose/ProseIconList.global.vue" },
48
+ { path: "app/components/content/prose/ProseLi.global.vue" },
49
+ ],
50
+ "package-manager": [
51
+ { path: "app/components/content/prose/ProsePmInstall.global.vue" },
52
+ { path: "app/components/content/prose/ProsePmRun.global.vue" },
53
+ { path: "app/components/content/prose/ProsePmX.global.vue" },
54
+ ],
55
+ steps: [
56
+ { path: "app/components/content/prose/ProseSteps.global.vue" },
57
+ { path: "app/components/content/prose/ProseStep.global.vue" },
58
+ ],
59
+ "code-snippet": false,
60
+ typography: false,
61
+ };
62
+
63
+ function toPosixPath(value: string) {
64
+ return value.replace(/\\/g, "/");
65
+ }
66
+
67
+ export function normalizeComponentApiPath(path: string) {
68
+ return toPosixPath(path)
69
+ .trim()
70
+ .replace(/^([@~])\//, "")
71
+ .replace(/^\.\//, "")
72
+ .replace(/^\/(?!\/)/, "");
73
+ }
74
+
75
+ export function normalizeComponentMetaPath(path: string) {
76
+ const normalized = normalizeComponentApiPath(path);
77
+ const proseIndex = normalized.lastIndexOf(BUILT_IN_PROSE_COMPONENT_ROOT);
78
+
79
+ if (proseIndex !== -1) {
80
+ return normalized.slice(proseIndex);
81
+ }
82
+
83
+ return normalized;
84
+ }
85
+
86
+ export const normalizeProseComponentPath = normalizeComponentMetaPath;
87
+
88
+ export function isBuiltInProseComponentPath(path: string) {
89
+ return normalizeComponentMetaPath(path).startsWith(BUILT_IN_PROSE_COMPONENT_ROOT);
90
+ }
91
+
92
+ export function componentNameFromPath(path: string) {
93
+ const normalized = normalizeComponentMetaPath(path);
94
+ const fileName = normalized.split("/").pop() || normalized;
95
+
96
+ return fileName.replace(/\.vue$/, "").replace(/\.global$/, "");
97
+ }
98
+
99
+ export function componentLabelFromName(name: string) {
100
+ return name
101
+ .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
102
+ .replace(/_/g, " ")
103
+ .trim();
104
+ }
105
+
106
+ export function componentLabelFromPath(path: string) {
107
+ return componentLabelFromName(componentNameFromPath(path));
108
+ }
109
+
110
+ export function componentTagFromName(name: string) {
111
+ return name
112
+ .replace(/([a-z0-9])([A-Z])/g, "$1-$2")
113
+ .replace(/_/g, "-")
114
+ .toLowerCase();
115
+ }
116
+
117
+ export function componentNameFromSlug(slug: string) {
118
+ const pascalCase = slug
119
+ .split(/[-_\s]+/g)
120
+ .filter(Boolean)
121
+ .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1))
122
+ .join("");
123
+
124
+ return `Prose${pascalCase}`;
125
+ }
126
+
127
+ export function inferredBuiltInProseComponentPathFromSlug(slug: string) {
128
+ return `${BUILT_IN_PROSE_COMPONENT_ROOT}${componentNameFromSlug(slug)}.global.vue`;
129
+ }
130
+
131
+ export function normalizeComponentApiLayout(
132
+ value: unknown,
133
+ fallback: ProseComponentApiLayout = DEFAULT_COMPONENT_API_LAYOUT
134
+ ): ProseComponentApiLayout {
135
+ return value === "table" || value === "field" ? value : fallback;
136
+ }
137
+
138
+ export function normalizeComponentApiSections(
139
+ value: unknown,
140
+ fallback: readonly ComponentApiSectionKind[] = DEFAULT_COMPONENT_API_SECTIONS
141
+ ) {
142
+ const values = Array.isArray(value)
143
+ ? value
144
+ : typeof value === "string"
145
+ ? value
146
+ .split(",")
147
+ .map((section) => section.trim())
148
+ .filter(Boolean)
149
+ : [];
150
+
151
+ const sections = values.filter((section): section is ComponentApiSectionKind =>
152
+ (DEFAULT_COMPONENT_API_SECTIONS as readonly string[]).includes(String(section))
153
+ );
154
+
155
+ return (
156
+ sections.length ? Array.from(new Set(sections)) : [...fallback]
157
+ ) as ComponentApiSectionKind[];
158
+ }
159
+
160
+ export function normalizeComponentApiHeading(value: unknown) {
161
+ if (value === false) {
162
+ return false;
163
+ }
164
+
165
+ if (typeof value !== "string") {
166
+ return undefined;
167
+ }
168
+
169
+ const heading = value.trim();
170
+ return heading || undefined;
171
+ }
172
+
173
+ function normalizeComponentApiItem(
174
+ input: unknown,
175
+ defaults: Pick<NormalizedComponentApiConfig, "layout" | "sections">
176
+ ) {
177
+ if (typeof input === "string") {
178
+ return {
179
+ path: normalizeComponentMetaPath(input),
180
+ layout: defaults.layout,
181
+ sections: [...defaults.sections],
182
+ } satisfies NormalizedComponentApiItem;
183
+ }
184
+
185
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
186
+ return null;
187
+ }
188
+
189
+ const item = input as Record<string, unknown>;
190
+
191
+ if (typeof item.path !== "string" || !item.path.trim()) {
192
+ return null;
193
+ }
194
+
195
+ return {
196
+ path: normalizeComponentMetaPath(item.path),
197
+ title: typeof item.title === "string" ? item.title : undefined,
198
+ layout: normalizeComponentApiLayout(item.layout, defaults.layout),
199
+ sections: normalizeComponentApiSections(item.sections, defaults.sections),
200
+ } satisfies NormalizedComponentApiItem;
201
+ }
202
+
203
+ export function normalizeComponentApiConfig(
204
+ input: unknown
205
+ ): false | NormalizedComponentApiConfig | null {
206
+ if (input === false) {
207
+ return false;
208
+ }
209
+
210
+ if (!input || typeof input !== "object" || Array.isArray(input)) {
211
+ return null;
212
+ }
213
+
214
+ const config = input as Record<string, unknown>;
215
+ const heading = normalizeComponentApiHeading(config.heading);
216
+ const layout = normalizeComponentApiLayout(config.layout);
217
+ const sections = normalizeComponentApiSections(config.sections);
218
+ const defaults = { layout, sections };
219
+ const components: NormalizedComponentApiItem[] = [];
220
+
221
+ const pathItem = normalizeComponentApiItem(config.path, defaults);
222
+ if (pathItem) {
223
+ components.push(pathItem);
224
+ }
225
+
226
+ if (Array.isArray(config.components)) {
227
+ for (const component of config.components) {
228
+ const normalizedComponent = normalizeComponentApiItem(component, defaults);
229
+
230
+ if (normalizedComponent) {
231
+ components.push(normalizedComponent);
232
+ }
233
+ }
234
+ }
235
+
236
+ return {
237
+ heading,
238
+ layout,
239
+ sections,
240
+ components,
241
+ hasExplicitComponents: components.length > 0,
242
+ };
243
+ }
@@ -0,0 +1,303 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { mkdir, readdir, stat, writeFile } from "node:fs/promises";
3
+ import { dirname, join, relative, resolve } from "node:path";
4
+
5
+ import { getComponentMeta } from "nuxt-component-meta/parser";
6
+
7
+ import {
8
+ componentNameFromPath,
9
+ componentTagFromName,
10
+ normalizeComponentMetaPath,
11
+ } from "./component-api";
12
+
13
+ export type { ProseComponentApiLayout } from "./component-api";
14
+
15
+ export interface ProseComponentMetaTag {
16
+ name: string;
17
+ text?: string;
18
+ }
19
+
20
+ interface BaseProseComponentMetaItem {
21
+ name: string;
22
+ description: string;
23
+ type: string;
24
+ tags: ProseComponentMetaTag[];
25
+ }
26
+
27
+ type RawProseComponentSchema = unknown;
28
+
29
+ interface RawMetaTag {
30
+ name: string;
31
+ text?: string;
32
+ }
33
+
34
+ interface RawPropertyMeta {
35
+ name: string;
36
+ description: string;
37
+ type: string;
38
+ default?: string;
39
+ required: boolean;
40
+ tags: RawMetaTag[];
41
+ schema?: RawProseComponentSchema;
42
+ }
43
+
44
+ interface RawSlotMeta {
45
+ name: string;
46
+ description: string;
47
+ type: string;
48
+ tags: RawMetaTag[];
49
+ schema?: RawProseComponentSchema;
50
+ }
51
+
52
+ interface RawEventMeta {
53
+ name: string;
54
+ description: string;
55
+ type: string;
56
+ signature: string;
57
+ tags: RawMetaTag[];
58
+ schema?: RawProseComponentSchema[];
59
+ }
60
+
61
+ interface RawExposedMeta {
62
+ name: string;
63
+ description: string;
64
+ type: string;
65
+ tags: RawMetaTag[];
66
+ schema?: RawProseComponentSchema;
67
+ }
68
+
69
+ export interface ProseComponentMetaProp extends BaseProseComponentMetaItem {
70
+ default?: string;
71
+ required: boolean;
72
+ schema?: RawProseComponentSchema;
73
+ }
74
+
75
+ export interface ProseComponentMetaSlot extends BaseProseComponentMetaItem {
76
+ schema?: RawProseComponentSchema;
77
+ }
78
+
79
+ export interface ProseComponentMetaEvent extends BaseProseComponentMetaItem {
80
+ signature: string;
81
+ schema?: RawProseComponentSchema[];
82
+ }
83
+
84
+ export interface ProseComponentMetaExposed extends BaseProseComponentMetaItem {
85
+ schema?: RawProseComponentSchema;
86
+ }
87
+
88
+ export interface ProseComponentMetaEntry {
89
+ name: string;
90
+ tag: string;
91
+ path: string;
92
+ props: ProseComponentMetaProp[];
93
+ slots: ProseComponentMetaSlot[];
94
+ events: ProseComponentMetaEvent[];
95
+ exposed: ProseComponentMetaExposed[];
96
+ }
97
+
98
+ export interface ProseComponentMetaManifest {
99
+ generatedAt: string;
100
+ components: Record<string, ProseComponentMetaEntry>;
101
+ sourceHashes?: Record<string, string>;
102
+ }
103
+
104
+ export interface ProseComponentMetaSource {
105
+ absolutePath: string;
106
+ manifestPath: string;
107
+ }
108
+
109
+ export interface GenerateProseComponentMetaOptions {
110
+ rootDir: string;
111
+ layerRoot: string;
112
+ componentsDir: string;
113
+ outputFile: string;
114
+ cache?: boolean;
115
+ cacheDir?: string;
116
+ additionalComponents?: ProseComponentMetaSource[];
117
+ }
118
+
119
+ export const EMPTY_PROSE_COMPONENT_META_MANIFEST: ProseComponentMetaManifest = {
120
+ generatedAt: "",
121
+ components: {},
122
+ };
123
+
124
+ export const normalizeProseComponentPath = normalizeComponentMetaPath;
125
+
126
+ function normalizeTags(tags: RawMetaTag[] = []): ProseComponentMetaTag[] {
127
+ return tags.map((tag) => ({ name: tag.name, text: tag.text }));
128
+ }
129
+
130
+ function normalizeProp(prop: RawPropertyMeta): ProseComponentMetaProp {
131
+ return {
132
+ name: prop.name,
133
+ description: prop.description || "",
134
+ type: prop.type || "unknown",
135
+ default: prop.default,
136
+ required: prop.required,
137
+ tags: normalizeTags(prop.tags),
138
+ schema: prop.schema,
139
+ };
140
+ }
141
+
142
+ function normalizeSlot(slot: RawSlotMeta): ProseComponentMetaSlot {
143
+ return {
144
+ name: slot.name,
145
+ description: slot.description || "",
146
+ type: slot.type || "unknown",
147
+ tags: normalizeTags(slot.tags),
148
+ schema: slot.schema,
149
+ };
150
+ }
151
+
152
+ function normalizeEvent(event: RawEventMeta): ProseComponentMetaEvent {
153
+ return {
154
+ name: event.name,
155
+ description: event.description || "",
156
+ type: event.type || "unknown",
157
+ signature: event.signature || event.type || "unknown",
158
+ tags: normalizeTags(event.tags),
159
+ schema: event.schema,
160
+ };
161
+ }
162
+
163
+ function normalizeExposed(exposed: RawExposedMeta): ProseComponentMetaExposed {
164
+ return {
165
+ name: exposed.name,
166
+ description: exposed.description || "",
167
+ type: exposed.type || "unknown",
168
+ tags: normalizeTags(exposed.tags),
169
+ schema: exposed.schema,
170
+ };
171
+ }
172
+
173
+ async function listVueFiles(dir: string): Promise<string[]> {
174
+ const files: string[] = [];
175
+ const entries = await readdir(dir, { withFileTypes: true });
176
+
177
+ for (const entry of entries) {
178
+ const fullPath = join(dir, entry.name);
179
+
180
+ if (entry.isDirectory()) {
181
+ files.push(...(await listVueFiles(fullPath)));
182
+ continue;
183
+ }
184
+
185
+ if (entry.isFile() && fullPath.endsWith(".vue")) {
186
+ files.push(fullPath);
187
+ }
188
+ }
189
+
190
+ return files.sort((a, b) => a.localeCompare(b));
191
+ }
192
+
193
+ function readExistingManifest(outputFile: string): ProseComponentMetaManifest {
194
+ if (!existsSync(outputFile)) {
195
+ return EMPTY_PROSE_COMPONENT_META_MANIFEST;
196
+ }
197
+
198
+ try {
199
+ const contents = readFileSync(outputFile, "utf8");
200
+ return JSON.parse(contents) as ProseComponentMetaManifest;
201
+ } catch {
202
+ return EMPTY_PROSE_COMPONENT_META_MANIFEST;
203
+ }
204
+ }
205
+
206
+ export function readGeneratedProseComponentMeta(outputFile: string): ProseComponentMetaManifest {
207
+ return readExistingManifest(outputFile);
208
+ }
209
+
210
+ export async function generateProseComponentMeta(
211
+ options: GenerateProseComponentMetaOptions
212
+ ): Promise<ProseComponentMetaManifest> {
213
+ const previousManifest = readExistingManifest(options.outputFile);
214
+ const builtInComponentFiles = await listVueFiles(options.componentsDir);
215
+ const componentSources = new Map<string, ProseComponentMetaSource>();
216
+ const components: Record<string, ProseComponentMetaEntry> = {};
217
+ const sourceHashes: Record<string, string> = {};
218
+
219
+ for (const componentPath of builtInComponentFiles) {
220
+ const manifestPath = normalizeComponentMetaPath(relative(options.layerRoot, componentPath));
221
+ componentSources.set(manifestPath, {
222
+ absolutePath: componentPath,
223
+ manifestPath,
224
+ });
225
+ }
226
+
227
+ for (const component of options.additionalComponents || []) {
228
+ componentSources.set(component.manifestPath, component);
229
+ }
230
+
231
+ const sources = Array.from(componentSources.values()).sort((a, b) =>
232
+ a.manifestPath.localeCompare(b.manifestPath)
233
+ );
234
+ let hasChanges = sources.length !== Object.keys(previousManifest.components || {}).length;
235
+
236
+ for (const component of sources) {
237
+ const stats = await stat(component.absolutePath);
238
+ const sourceHash = `${stats.mtimeMs}:${stats.size}`;
239
+
240
+ sourceHashes[component.manifestPath] = sourceHash;
241
+
242
+ const cachedEntry = previousManifest.components?.[component.manifestPath];
243
+ const cachedHash = previousManifest.sourceHashes?.[component.manifestPath];
244
+
245
+ if (cachedEntry && cachedHash === sourceHash) {
246
+ components[component.manifestPath] = cachedEntry;
247
+ continue;
248
+ }
249
+
250
+ hasChanges = true;
251
+
252
+ const meta = getComponentMeta(component.absolutePath, {
253
+ rootDir: options.rootDir,
254
+ cache: options.cache ?? true,
255
+ cacheDir: options.cacheDir,
256
+ });
257
+
258
+ const name = componentNameFromPath(component.manifestPath);
259
+ components[component.manifestPath] = {
260
+ name,
261
+ tag: componentTagFromName(name),
262
+ path: component.manifestPath,
263
+ props: meta.props.map(normalizeProp),
264
+ slots: meta.slots.map(normalizeSlot),
265
+ events: meta.events.map(normalizeEvent),
266
+ exposed: meta.exposed.map(normalizeExposed),
267
+ };
268
+ }
269
+
270
+ if (!hasChanges) {
271
+ return previousManifest;
272
+ }
273
+
274
+ const nextManifest: ProseComponentMetaManifest = {
275
+ generatedAt: new Date().toISOString(),
276
+ components,
277
+ sourceHashes,
278
+ };
279
+
280
+ const nextContents = `${JSON.stringify(nextManifest, null, 2)}\n`;
281
+ const previousContents = existsSync(options.outputFile)
282
+ ? readFileSync(options.outputFile, "utf8")
283
+ : "";
284
+
285
+ if (previousContents !== nextContents) {
286
+ await mkdir(dirname(options.outputFile), { recursive: true });
287
+ await writeFile(options.outputFile, nextContents, "utf8");
288
+ }
289
+
290
+ return nextManifest;
291
+ }
292
+
293
+ export function resolveProseComponentPaths(options: { rootDir: string; layerRoot: string }) {
294
+ const componentsDir = resolve(options.layerRoot, "app/components/content/prose");
295
+ const outputFile = resolve(options.rootDir, ".data/docd/prose-component-meta.json");
296
+ const cacheDir = resolve(options.rootDir, ".data/docd/prose-component-meta");
297
+
298
+ return {
299
+ componentsDir,
300
+ outputFile,
301
+ cacheDir,
302
+ };
303
+ }
package/utils/git.ts ADDED
@@ -0,0 +1,123 @@
1
+ import { execSync } from "node:child_process";
2
+
3
+ import gitUrlParse from "git-url-parse";
4
+ import { readGitConfig } from "pkg-types";
5
+
6
+ export interface GitInfo {
7
+ // Repository name
8
+ name: string;
9
+ // Repository owner/organization
10
+ owner: string;
11
+ // Repository URL
12
+ url: string;
13
+ }
14
+
15
+ export function getGitBranch() {
16
+ const envName =
17
+ process.env.CF_PAGES_BRANCH ||
18
+ process.env.CI_COMMIT_BRANCH ||
19
+ process.env.VERCEL_GIT_COMMIT_REF ||
20
+ process.env.BRANCH ||
21
+ process.env.GITHUB_REF_NAME;
22
+
23
+ if (envName && envName !== "HEAD") {
24
+ return envName;
25
+ }
26
+ try {
27
+ const branch = execSync("git rev-parse --abbrev-ref HEAD", {
28
+ stdio: ["ignore", "pipe", "ignore"],
29
+ })
30
+ .toString()
31
+ .trim();
32
+ if (branch && branch !== "HEAD") {
33
+ return branch;
34
+ }
35
+ } catch {
36
+ // Ignore error
37
+ }
38
+
39
+ return "main";
40
+ }
41
+
42
+ /**
43
+ * Get Git repository information from the local .git config or environment variables.
44
+ */
45
+ export async function getLocalGitInfo(rootDir: string): Promise<GitInfo | undefined> {
46
+ const remote = await getLocalGitRemote(rootDir);
47
+ if (!remote) {
48
+ return;
49
+ }
50
+
51
+ // https://www.npmjs.com/package/git-url-parse#clipboard-example
52
+ const { name, owner, source } = gitUrlParse(remote);
53
+ const url = `https://${source}/${owner}/${name}`;
54
+
55
+ return {
56
+ name,
57
+ owner,
58
+ url,
59
+ };
60
+ }
61
+
62
+ /**
63
+ * Read the local .git/config file to get the remote URL of the repository.
64
+ */
65
+ async function getLocalGitRemote(dir: string): Promise<string | undefined> {
66
+ try {
67
+ const parsed = await readGitConfig(dir);
68
+ if (!parsed) {
69
+ return;
70
+ }
71
+ return parsed.remote?.["origin"]?.url;
72
+ } catch {
73
+ // Ignore error
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Get Git repository information from environment variables set by hosting providers (e.g., Vercel, Netlify, GitHub Actions).
79
+ */
80
+ export function getGitEnv(): GitInfo {
81
+ const envInfo = {
82
+ // Provider
83
+ provider:
84
+ process.env.VERCEL_GIT_PROVIDER || // vercel
85
+ (process.env.GITHUB_SERVER_URL ? "github" : undefined) || // github
86
+ "",
87
+ // Owner
88
+ owner:
89
+ process.env.VERCEL_GIT_REPO_OWNER || // vercel
90
+ process.env.GITHUB_REPOSITORY_OWNER || // github
91
+ process.env.CI_PROJECT_PATH?.split("/").shift() || // gitlab
92
+ "",
93
+ // Name
94
+ name:
95
+ process.env.VERCEL_GIT_REPO_SLUG ||
96
+ process.env.GITHUB_REPOSITORY?.split("/").pop() || // github
97
+ process.env.CI_PROJECT_PATH?.split("/").splice(1).join("/") || // gitlab
98
+ "",
99
+ // Url
100
+ url: process.env.REPOSITORY_URL || "", // netlify
101
+ };
102
+
103
+ if (!envInfo.url && envInfo.provider && envInfo.owner && envInfo.name) {
104
+ envInfo.url = `https://${envInfo.provider}.com/${envInfo.owner}/${envInfo.name}`;
105
+ }
106
+
107
+ // If only url available (ex: Netlify)
108
+ if (!envInfo.name && !envInfo.owner && envInfo.url) {
109
+ try {
110
+ const { name, owner } = gitUrlParse(envInfo.url);
111
+ envInfo.name = name;
112
+ envInfo.owner = owner;
113
+ } catch {
114
+ // Ignore error
115
+ }
116
+ }
117
+
118
+ return {
119
+ name: envInfo.name,
120
+ owner: envInfo.owner,
121
+ url: envInfo.url,
122
+ };
123
+ }
package/utils/meta.ts ADDED
@@ -0,0 +1,40 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+
4
+ import { withHttps } from "ufo";
5
+
6
+ /**
7
+ * Infers the site URL from various environment variables commonly used in hosting platforms.
8
+ */
9
+ export function inferSiteURL() {
10
+ // https://github.com/unjs/std-env/issues/59
11
+ const url =
12
+ process.env.NUXT_PUBLIC_SITE_URL || // Nuxt public runtime config
13
+ process.env.NUXT_SITE_URL || // Nuxt site config
14
+ process.env.VERCEL_PROJECT_PRODUCTION_URL || // Vercel production URL
15
+ process.env.VERCEL_BRANCH_URL || // Vercel branch URL
16
+ process.env.VERCEL_URL || // Vercel deployment URL
17
+ process.env.URL || // Netlify
18
+ process.env.CI_PAGES_URL || // Gitlab Pages
19
+ process.env.CF_PAGES_URL; // Cloudflare Pages
20
+
21
+ return url ? withHttps(url) : undefined;
22
+ }
23
+
24
+ /**
25
+ * Reads the package.json file from the specified directory and extracts metadata such as `name` and `description`.
26
+ */
27
+ export async function getPackageJsonMetadata(dir: string) {
28
+ try {
29
+ const packageJson = await readFile(resolve(dir, "package.json"), "utf-8");
30
+ const parsed = JSON.parse(packageJson);
31
+ return {
32
+ name: parsed.name,
33
+ description: parsed.description,
34
+ };
35
+ } catch {
36
+ return {
37
+ name: "Docd",
38
+ };
39
+ }
40
+ }